Python設計模式——組合模式
阿新 • • 發佈:2019-02-07
組合模式(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 + " ")