『 读书笔记 』 设计模式总结之结构型模式
原文链接 https://litaotao.github.io/design-pattern-structural-pattern
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
1. 适配器模式
在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类别自己的接口包裹在一个已存在的类中。
1.1 Python源码示例
{% highlight python %}
!/usr/bin/env python
-- coding: utf-8 --
"""http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/"""
import os
class Dog(object): def init(self): self.name = "Dog" def bark(self): return "woof!"
class Cat(object): def init(self): self.name = "Cat" def meow(self): return "meow!"
class Human(object): def init(self): self.name = "Human" def speak(self): return "'hello'"
class Car(object): def init(self): self.name = "Car" def make_noise(self, octane_level): return "vroom{0}".format("!" * octane_level)
class Adapter(object):
"""
Adapts an object by replacing methods.
Usage:
dog = Dog
dog = Adapter(dog, dict(make_noise=dog.bark))
>>> objects = []
>>> dog = Dog()
>>> objects.append(Adapter(dog, make_noise=dog.bark))
>>> cat = Cat()
>>> objects.append(Adapter(cat, make_noise=cat.meow))
>>> human = Human()
>>> objects.append(Adapter(human, make_noise=human.speak))
>>> car = Car()
>>> car_noise = lambda: car.make_noise(3)
>>> objects.append(Adapter(car, make_noise=car_noise))
>>> for obj in objects:
... print('A {} goes {}'.format(obj.name, obj.make_noise()))
A Dog goes woof!
A Cat goes meow!
A Human goes 'hello'
A Car goes vroom!!!
"""
def __init__(self, obj, **adapted_methods):
"""We set the adapted methods in the object's dict"""
self.obj = obj
self.__dict__.update(adapted_methods)
def __getattr__(self, attr):
"""All non-adapted calls are passed to the object"""
return getattr(self.obj, attr)
def main(): objects = [] dog = Dog() objects.append(Adapter(dog, make_noise=dog.bark)) cat = Cat() objects.append(Adapter(cat, make_noise=cat.meow)) human = Human() objects.append(Adapter(human, make_noise=human.speak)) car = Car() objects.append(Adapter(car, make_noise=lambda: car.make_noise(3)))
for obj in objects:
print("A {0} goes {1}".format(obj.name, obj.make_noise()))
if name == "main": main()
OUTPUT
A Dog goes woof!
A Cat goes meow!
A Human goes 'hello'
A Car goes vroom!!!
{% endhighlight %}
2. 桥接模式
桥接模式是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。
2.1 Python源码示例
{% highlight python %}
!/usr/bin/env python
-- coding: utf-8 --
"""http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Bridge_Pattern#Python"""
ConcreteImplementor 1/2
class DrawingAPI1(object):
def draw_circle(self, x, y, radius):
print('API1.circle at {}:{} radius {}'.format(x, y, radius))
ConcreteImplementor 2/2
class DrawingAPI2(object):
def draw_circle(self, x, y, radius):
print('API2.circle at {}:{} radius {}'.format(x, y, radius))
Refined Abstraction
class CircleShape(object):
def __init__(self, x, y, radius, drawing_api):
self._x = x
self._y = y
self._radius = radius
self._drawing_api = drawing_api
# low-level i.e. Implementation specific
def draw(self):
self._drawing_api.draw_circle(self._x, self._y, self._radius)
# high-level i.e. Abstraction specific
def scale(self, pct):
self._radius *= pct
def main(): shapes = ( CircleShape(1, 2, 3, DrawingAPI1()), CircleShape(5, 7, 11, DrawingAPI2()) )
for shape in shapes:
shape.scale(2.5)
shape.draw()
if name == 'main': main()
OUTPUT
API1.circle at 1:2 radius 7.5
API2.circle at 5:7 radius 27.5
{% endhighlight %}
3. 组合模式
针对“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
3.1 Python源码示例
{% highlight python %}
!/usr/bin/env python
-- coding: utf-8 --
""" A class which defines a composite object which can store hieararchical dictionaries with names. This class is same as a hiearchical dictionary, but it provides methods to add/access/modify children by name, like a Composite. Created Anand B Pillai abpillai@gmail.com """ author = "Anand B Pillai" maintainer = "Anand B Pillai" version = "0.2"
def normalize(val): """ Normalize a string so that it can be used as an attribute to a Python object """
if val.find('-') != -1:
val = val.replace('-', '_')
return val
def denormalize(val): """ De-normalize a string """
if val.find('_') != -1:
val = val.replace('_', '-')
return val
class SpecialDict(dict):
""" A dictionary type which allows direct attribute
access to its keys """
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
elif name in self:
return self.get(name)
else:
# Check for denormalized name
name = denormalize(name)
if name in self:
return self.get(name)
else:
raise AttributeError('no attribute named %s' % name)
def __setattr__(self, name, value):
if name in self.__dict__:
self.__dict__[name] = value
elif name in self:
self[name] = value
else:
# Check for denormalized name
name2 = denormalize(name)
if name2 in self:
self[name2] = value
else:
# New attribute
self[name] = value
class CompositeDict(SpecialDict):
""" A class which works like a hierarchical dictionary.
This class is based on the Composite design-pattern """
ID = 0
def __init__(self, name=''):
if name:
self._name = name
else:
self._name = ''.join(('id#', str(self.__class__.ID)))
self.__class__.ID += 1
self._children = []
# Link back to father
self._father = None
self[self._name] = SpecialDict()
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
elif name in self:
return self.get(name)
else:
# Check for denormalized name
name = denormalize(name)
if name in self:
return self.get(name)
else:
# Look in children list
child = self.findChild(name)
if child:
return child
else:
attr = getattr(self[self._name], name)
if attr:
return attr
raise AttributeError('no attribute named %s' % name)
def isRoot(self):
""" Return whether I am a root component or not """
# If I don't have a parent, I am root
return not self._father
def isLeaf(self):
""" Return whether I am a leaf component or not """
# I am a leaf if I have no children
return not self._children
def getName(self):
""" Return the name of this ConfigInfo object """
return self._name
def getIndex(self, child):
""" Return the index of the child ConfigInfo object 'child' """
if child in self._children:
return self._children.index(child)
else:
return -1
def getDict(self):
""" Return the contained dictionary """
return self[self._name]
def getProperty(self, child, key):
""" Return the value for the property for child
'child' with key 'key' """
# First get the child's dictionary
childDict = self.getInfoDict(child)
if childDict:
return childDict.get(key, None)
def setProperty(self, child, key, value):
""" Set the value for the property 'key' for
the child 'child' to 'value' """
# First get the child's dictionary
childDict = self.getInfoDict(child)
if childDict:
childDict[key] = value
def getChildren(self):
""" Return the list of immediate children of this object """
return self._children
def getAllChildren(self):
""" Return the list of all children of this object """
l = []
for child in self._children:
l.append(child)
l.extend(child.getAllChildren())
return l
def getChild(self, name):
""" Return the immediate child object with the given name """
for child in self._children:
if child.getName() == name:
return child
def findChild(self, name):
""" Return the child with the given name from the tree """
# Note - this returns the first child of the given name
# any other children with similar names down the tree
# is not considered.
for child in self.getAllChildren():
if child.getName() == name:
return child
def findChildren(self, name):
""" Return a list of children with the given name from the tree """
# Note: this returns a list of all the children of a given
# name, irrespective of the depth of look-up.
children = []
for child in self.getAllChildren():
if child.getName() == name:
children.append(child)
return children
def getPropertyDict(self):
""" Return the property dictionary """
d = self.getChild('__properties')
if d:
return d.getDict()
else:
return {}
def getParent(self):
""" Return the person who created me """
return self._father
def __setChildDict(self, child):
""" Private method to set the dictionary of the child
object 'child' in the internal dictionary """
d = self[self._name]
d[child.getName()] = child.getDict()
def setParent(self, father):
""" Set the parent object of myself """
# This should be ideally called only once
# by the father when creating the child :-)
# though it is possible to change parenthood
# when a new child is adopted in the place
# of an existing one - in that case the existing
# child is orphaned - see addChild and addChild2
# methods !
self._father = father
def setName(self, name):
""" Set the name of this ConfigInfo object to 'name' """
self._name = name
def setDict(self, d):
""" Set the contained dictionary """
self[self._name] = d.copy()
def setAttribute(self, name, value):
""" Set a name value pair in the contained dictionary """
self[self._name][name] = value
def getAttribute(self, name):
""" Return value of an attribute from the contained dictionary """
return self[self._name][name]
def addChild(self, name, force=False):
""" Add a new child 'child' with the name 'name'.
If the optional flag 'force' is set to True, the
child object is overwritten if it is already there.
This function returns the child object, whether
new or existing """
if type(name) != str:
raise ValueError('Argument should be a string!')
child = self.getChild(name)
if child:
# print('Child %s present!' % name)
# Replace it if force==True
if force:
index = self.getIndex(child)
if index != -1:
child = self.__class__(name)
self._children[index] = child
child.setParent(self)
self.__setChildDict(child)
return child
else:
child = self.__class__(name)
child.setParent(self)
self._children.append(child)
self.__setChildDict(child)
return child
def addChild2(self, child):
""" Add the child object 'child'. If it is already present,
it is overwritten by default """
currChild = self.getChild(child.getName())
if currChild:
index = self.getIndex(currChild)
if index != -1:
self._children[index] = child
child.setParent(self)
# Unset the existing child's parent
currChild.setParent(None)
del currChild
self.__setChildDict(child)
else:
child.setParent(self)
self._children.append(child)
self.__setChildDict(child)
if name == "main": window = CompositeDict('Window') frame = window.addChild('Frame') tfield = frame.addChild('Text Field') tfield.setAttribute('size', '20')
btn = frame.addChild('Button1')
btn.setAttribute('label', 'Submit')
btn = frame.addChild('Button2')
btn.setAttribute('label', 'Browse')
# print(window)
# print(window.Frame)
# print(window.Frame.Button1)
# print(window.Frame.Button2)
print(window.Frame.Button1.label)
print(window.Frame.Button2.label)
OUTPUT
Submit
Browse
{% endhighlight %}
4. 修饰模式
修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
4.1 Python源码示例
{% highlight python %}
"""https://docs.python.org/2/library/functools.html#functools.wraps""" """https://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/739665#739665"""
from functools import wraps
def makebold(fn): @wraps(fn) def wrapped(): return "" + fn() + "" return wrapped
def makeitalic(fn): @wraps(fn) def wrapped(): return "" + fn() + "" return wrapped
@makebold @makeitalic def hello(): """a decorated hello world""" return "hello world"
if name == 'main': print('result:{} name:{} doc:{}'.format(hello(), hello.name, hello.doc))
OUTPUT
result:hello world name:hello doc:a decorated hello world
{% endhighlight %}
5. 外观模式
外观模式(Facade pattern),是软件工程中常用的一种软件设计模式,它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。
5.1 Python源码示例
{% highlight python %}
!/usr/bin/env python
-- coding: utf-8 --
import time
SLEEP = 0.5
Complex Parts
class TC1:
def run(self):
print("###### In Test 1 ######")
time.sleep(SLEEP)
print("Setting up")
time.sleep(SLEEP)
print("Running test")
time.sleep(SLEEP)
print("Tearing down")
time.sleep(SLEEP)
print("Test Finished\n")
class TC2:
def run(self):
print("###### In Test 2 ######")
time.sleep(SLEEP)
print("Setting up")
time.sleep(SLEEP)
print("Running test")
time.sleep(SLEEP)
print("Tearing down")
time.sleep(SLEEP)
print("Test Finished\n")
class TC3:
def run(self):
print("###### In Test 3 ######")
time.sleep(SLEEP)
print("Setting up")
time.sleep(SLEEP)
print("Running test")
time.sleep(SLEEP)
print("Tearing down")
time.sleep(SLEEP)
print("Test Finished\n")
Facade
class TestRunner:
def __init__(self):
self.tc1 = TC1()
self.tc2 = TC2()
self.tc3 = TC3()
self.tests = [i for i in (self.tc1, self.tc2, self.tc3)]
def runAll(self):
[i.run() for i in self.tests]
Client
if name == 'main': testrunner = TestRunner() testrunner.runAll()
OUTPUT
###### In Test 1
Setting up
Running test
Tearing down
Test Finished
###### In Test 2
Setting up
Running test
Tearing down
Test Finished
###### In Test 3
Setting up
Running test
Tearing down
Test Finished
{% endhighlight %}