User:Xvedejas/Design Patterns
XVedejas' Guide to Design Patterns
[edit]This is a guide to using
Composite
[edit]The composite pattern lets us treat an object and a group of these objects as the same thing. This is extremely handy when you want groupings of objects or even groupings of groupings. As an example, we will pretend we are writing a game where you can control army units. The most basic unit (the "component") is a single soldier. Yet what we want to do is to be able to treat a whole group of soldiers as if we were commanding just one. So we will create a soldier interface class called ISoldier that basically lists all the things a soldier (or group of soldiers) might need to do.
# We are going to simulate an interface, though python doesn't really have interfaces.
def abstract():
import inspect
caller = inspect.getouterframes(inspect.currentframe())[1][3]
raise NotImplementedError(caller + ' must be implemented in subclass')
# All the things that both soldiers and groups of soldiers must do
class ISoldier():
def Attack(self, enemy): abstract()
def Retreat(self): abstract()
def DoNothing(self): abstract()
Now let's create a soldier class with the appropriate methods.
# The "Component"
class Soldier(ISoldier):
# Here are some attributes about a soldier
Name = ''
Position = (0,0)
# Here are some things a soldier does.
def Attack(self, enemy):
... code ...
def Retreat(self):
... code ...
def DoNothing(self):
... code ...
Simple enough, right? So if we wanted to control this soldier, we could tell it to attack, retreat, or do nothing. But an army has many, many soldiers and we might want to issue all of them a command at once, as a group. You could write a 'for' loop each and every time you wanted to call multiple soldiers, but that doesn't retain information about the group. It is a much wiser decision to abstract this by creating a new class.
# The "Composite"
class SoldierGroup(ISoldier):
NumberOfSoldiers = 0
Soldiers = set()
def AddSoldier(self, soldier):
Soldiers.add(soldier)
NumberOfSoldiers += 1
def Attack(self, enemy):
for soldier in Soldiers:
soldier.Attack(enemy)
# etc.
Since we want to treat the group as if it were a single soldier, we will also inherit from ISoldier. This will require us to independently redefine all the functions that ISoldier gives us.