组合模式(Composite Pattern)可用来统合类体系中的这两种对象,一种对象能够包含体系中的其他对象,另一种为基本的单元对象,并不能包含其他对象。
(Python中其实很少使用到组合模式,因为采用dict
就可以实现相同的功能)
常规方法,我们先可以用两种基类来分别表示这两种类型的组件。
先让我们看看运行的结果:
$0.40 Pencil
$1.60 Ruler
$0.20 Eraser
$2.20 Pencil Set
$0.40 Pencil
$1.60 Ruler
$0.20 Eraser
$3.60 Boxed Pencil Set
$1.00 Box
$2.20 Pencil Set
$0.40 Pencil
$1.60 Ruler
$0.20 Eraser
$0.40 Pencil
其中main()函数中:
def main():
pencil = SimpleItem("Pencil", 0.40)
ruler = SimpleItem("Ruler", 1.60)
eraser = SimpleItem("Eraser", 0.20)
pencilSet = CompositeItem("Pencil Set", pencil, ruler, eraser)
box = SimpleItem("Box", 1.00)
boxedPencilSet = CompositeItem("Boxed Pencil Set", box, pencilSet)
boxedPencilSet.add(pencil)
for item in (pencil, ruler, eraser, pencilSet, boxedPencilSet):
item.print()
层次结构如图所示
其UML:
AbstractItem要求所有子类的对象明确是不是组合体,同时还要求子类对象必须可迭代。
class AbstractItem(object):
__metaclass__=abc.ABCMeta
@abc.abstractproperty
def composite(self):
pass
def __iter__(self):
return iter([])
SimpleItem类用来表示组合单元,也就是非组合体
class SimpleItem(AbstractItem):
def __init__(self, name, price=0.00):
self.name = name
self.price = price
@property
def composite(self):
"""
表明是非组合体
"""
return False
def print(self, indent="", file=sys.stdout):
print("{}${:.2f} {}".format(indent, self.price, self.name),
file=file)
AbstractCompositeItem作为CompositeItem的基类,它实现了组合体所需的添加,移除,迭代等功能
class AbstractCompositeItem(AbstractItem):
def __init__(self, *items):
self.children = []
if items:
self.add(*items)
def add(self, first, *items):
self.children.append(first)
if items:
self.children.extend(items)
def remove(self, item):
self.children.remove(item)
def __iter__(self):
return iter(self.children)
接下来就是组合体CompositeItem的具体实现:
class CompositeItem(AbstractCompositeItem):
def __init__(self, name, *items):
super().__init__(*items)
self.name = name
@property
def composite(self):
"""
确认是组合体
"""
return True
@property
def price(self):
return sum(item.price for item in self)
def print(self, indent="", file=sys.stdout):
print("{}${:.2f} {}".format(indent, self.price, self.name),
file=file)
for child in self:
child.print(indent + " ")
这样就实现分别用两个类来表示组合体与非组合体了。但是这种方法导致我们创建了两个抽象类,两个具体类,接口也没有完全统一,如果我们再能忍受少许的额外开销也许将会更简单。
接下来我们使用一个类来表示组合体与非组合体,这二者接口完全一直,只是非组合体并不能使用某些接口
它的main函数
def main():
pencil = Item.create("Pencil", 0.40)
ruler = Item.create("Ruler", 1.60)
eraser = make_item("Eraser", 0.20)
pencilSet = Item.compose("Pencil Set", pencil, ruler, eraser)
box = Item.create("Box", 1.00)
boxedPencilSet = make_composite("Boxed Pencil Set", box, pencilSet)
boxedPencilSet.add(pencil)
for item in (pencil, ruler, eraser, pencilSet, boxedPencilSet):
item.print()
assert not pencil.composite
pencil.add(eraser, box)
assert pencil.composite
pencil.print()
pencil.remove(eraser)
assert pencil.composite
pencil.remove(box)
assert not pencil.composite
pencil.print()
类的代码
class Item:
def __init__(self, name, *items, price=0.00):
"""
将items加入children
可以依据children判断是否是组合体
"""
self.name = name
self.price = price
self.children = []
if items:
self.add(*items)
这里定义了两个工厂方法(还记得之前的工厂模式吗?),其中create
创建非组合体,compose
创建组合体
@classmethod
def create(Class, name, price):
return Class(name, price=price)
@classmethod
def compose(Class, name, *items):
return Class(name, *items)
我们还可以在类外面创建这两个工厂方法来创建非组合体和组合体
def make_item(name, price):
return Item(name, price=price)
def make_composite(name, *items):
return Item(name, *items)
composite
判断是否是组合体
@property
def composite(self):
return bool(self.children)
定义组合体方法
def add(self, first, *items):
self.children.extend(itertools.chain((first,), items))
def remove(self, item):
self.children.remove(item)
def __iter__(self):
return iter(self.children)
打印
@property
def price(self):
return (sum(item.price for item in self) if self.children else
self.__price)
@price.setter
def price(self, price):
self.__price = price
def print(self, indent="", file=sys.stdout):
print("{}${:.2f} {}".format(indent, self.price, self.name),
file=file)
for child in self:
child.print(indent + " ")