您的位置:首页 > 编程语言 > Python开发

Python设计模式——组合模式

2015-03-11 09:28 405 查看
组合模式(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 + "      ")
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: