『 读书笔记 』 设计模式总结之结构型模式

2015-03-13 litaotao 更多博文 » 博客 » GitHub »

原文链接 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 %}

扫一扫

2015-03-13-design-pattern-structural-pattern.md