1. 程式人生 > >Python設計模式——組合模式

Python設計模式——組合模式

組合模式(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:
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 + "      ")