Table of Contents

A pattern as an especially clever and insightful way of solving a particular class. of problems.

1 Classifying Patterns

1.1 Creational

  • Singleton
  • Factory method
  • Prototype

1.2 Structural

objects connect with other objects, when changing in the system doesn't require changes to those connection.

1.3 Behavioral

  • Observer
  • Visitor

2 Singleton

单例模式是日常应用中最广泛的模式了,其目的就是令到单个进程中只存在一个类的实例,从而可以实现数据的共享,节省系统开销,防止io阻塞等等。A way to provide one and only one object of a particular type. 但是在多进程的应用中,单例模式就实现不了了,例如一些web应用,django,这些,因为会启动多条进程来监听http请求,这样的会通过单例模式是实现不了数据共享的,也就是实现不了单例模式的目的了,这时需要用进程间通信方法来实现数据共享,当然也可以尝试使用redis这些nosql数据库实现数据共享,因为它们的读取数据较快。

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_the_instance'):
            cls._the_instance=object.__new__(cls,*args, **kwargs)
        return cls._the_instance

class A(Singleton):
    print 'init before'
    def __init__(self):
        print 'i am __init__'
    def f(self):
        print 'i am f'

a=A()
b=A()
a.f()
print 'done'

3 State Machine

While State has a way to allow the client programmer to change the implementation, StateMachine imposes a structure to automatically change the implementation from one object to the next. The current implementation represents the state that a system is in, and the system behaves differently from one state to the next (because it uses State). Basically, this is a “state machine” using objects. Each State object decides what other states it can move to, based on the “input”.

# StateMachine/State.py
# A State has an operation, and can be moved
# into the next State given an Input:
class State:
    def run(self):
        assert 0, "run not implemented"
    def next(self, input):
        assert 0, "next not implemented"

The StateMachine keeps track of the current state, which is initialized by the constructor. The runAll( ) method takes a list of Input objects. This method not only moves to the next state, but it also calls run( ) for each state object

# StateMachine/StateMachine.py
# Takes a list of Inputs to move from State to
# State using a template method.
class StateMachine:
    def __init__(self, initialState):
        self.currentState = initialState
        self.currentState.run()
    # Template method:
    def runAll(self, inputs):
        for i in inputs:
            print(i)
            self.currentState = self.currentState.next(i)
            self.currentState.run()
# StateMachine/mouse/MouseAction.py
class MouseAction:
    def __init__(self, action):
        self.action = action
    def __str__(self): return self.action
    def __cmp__(self, other):
        return cmp(self.action, other.action)
# Necessary when __cmp__ or __eq__ is defined
# in order to make this class usable as a
# dictionary key:
    def __hash__(self):
        return hash(self.action)
# Static fields; an enumeration of instances:
MouseAction.appears = MouseAction("mouse appears")
MouseAction.runsAway = MouseAction("mouse runs away")
MouseAction.enters = MouseAction("mouse enters trap")
MouseAction.escapes = MouseAction("mouse escapes")
MouseAction.trapped = MouseAction("mouse trapped")
MouseAction.removed = MouseAction("mouse removed")
# StateMachine/mouse/MouseMoves.txt
mouse appears
mouse runs away
mouse appears
mouse enters trap
mouse escapes
mouse appears
mouse enters trap
mouse trapped
mouse removed
mouse appears
mouse runs away
mouse appears
mouse enters trap
mouse trapped
mouse removed
# StateMachine/mousetrap1/MouseTrapTest.py
# State Machine pattern using 'if' statements
# to determine the next state.
import string, sys
sys.path += ['../stateMachine', '../mouse']
from State import State
from StateMachine import StateMachine
from MouseAction import MouseAction
# A different subclass for each state:
class Waiting(State):
  def run(self):
    print("Waiting: Broadcasting cheese smell")
  def next(self, input):
    if input == MouseAction.appears:
      return MouseTrap.luring
    return MouseTrap.waiting
class Luring(State):
  def run(self):
    print("Luring: Presenting Cheese, door open")
  def next(self, input):
    if input == MouseAction.runsAway:
      return MouseTrap.waiting
    if input == MouseAction.enters:
      return MouseTrap.trapping
    return MouseTrap.luring
class Trapping(State):
  def run(self):
    print("Trapping: Closing door")
  def next(self, input):
    if input == MouseAction.escapes:
      return MouseTrap.waiting
    if input == MouseAction.trapped:
      return MouseTrap.holding
    return MouseTrap.trapping
class Holding(State):
  def run(self):
    print("Holding: Mouse caught")
  def next(self, input):
    if input == MouseAction.removed:
      return MouseTrap.waiting
    return MouseTrap.holding
class MouseTrap(StateMachine):
  def __init__(self):
    # Initial state
    StateMachine.__init__(self, MouseTrap.waiting)
# Static variable initialization:
MouseTrap.waiting = Waiting()
MouseTrap.luring = Luring()
MouseTrap.trapping = Trapping()
MouseTrap.holding = Holding()
moves = map(string.strip,
  open("../mouse/MouseMoves.txt").readlines())
MouseTrap().runAll(map(MouseAction, moves))

The StateT class is an implementation of State (so that the same StateMachine class can be used from the previous example) that adds a Map and a method to initialize the map from a two-dimensional array. The next( ) method has a base-class implementation which must be called from the overridden derived class next( ) methods after they test for a null Map (and initialize it if it’s null):

# StateMachine/mousetrap2/MouseTrap2Test.py
# A better mousetrap using tables
import string, sys
sys.path += ['../stateMachine', '../mouse']
from State import State
from StateMachine import StateMachine
from MouseAction import MouseAction
class StateT(State):
  def __init__(self):
    self.transitions = None
  def next(self, input):
    if self.transitions.has_key(input):
      return self.transitions[input]
    else:
      raise "Input not supported for current state"
class Waiting(StateT):
  def run(self):
    print("Waiting: Broadcasting cheese smell")
  def next(self, input):
    # Lazy initialization:
    if not self.transitions:
      self.transitions = {
        MouseAction.appears : MouseTrap.luring
      }
    return StateT.next(self, input)
class Luring(StateT):
  def run(self):
    print("Luring: Presenting Cheese, door open")
  def next(self, input):
    # Lazy initialization:
    if not self.transitions:
      self.transitions = {
        MouseAction.enters : MouseTrap.trapping,
        MouseAction.runsAway : MouseTrap.waiting
      }
    return StateT.next(self, input)
class Trapping(StateT):
  def run(self):
    print("Trapping: Closing door")
  def next(self, input):
    # Lazy initialization:
    if not self.transitions:
      self.transitions = {
        MouseAction.escapes : MouseTrap.waiting,
        MouseAction.trapped : MouseTrap.holding
      }
    return StateT.next(self, input)
class Holding(StateT):
  def run(self):
    print("Holding: Mouse caught")
  def next(self, input):
  # Lazy initialization:
    if not self.transitions:
      self.transitions = {
      MouseAction.removed : MouseTrap.waiting
    }
    return StateT.next(self, input)
class MouseTrap(StateMachine):
  def __init__(self):
    # Initial state
    StateMachine.__init__(self, MouseTrap.waiting)
# Static variable initialization:
MouseTrap.waiting = Waiting()
MouseTrap.luring = Luring()
MouseTrap.trapping = Trapping()
MouseTrap.holding = Holding()
moves = map(string.strip,
open("../mouse/MouseMoves.txt").readlines())
mouseMoves = map(MouseAction, moves)
MouseTrap().runAll(mouseMoves)

4 Decorator

假如我们需要开发一个程序来展示一个人穿衣服的过程。

class Person():
    def __init__(self,name):
        print '%s开始穿衣'
    def wear_tshirt(self):
        print '穿TShirst'
    def wear_trouser(self):
        print '穿裤子'
    def wear_shoe(self):
        print '穿T鞋子'
    def wear_tie(self):
        print '穿领带'

if __name__=='__main__':
    person=Person('kevin')
    person.wear_shoe()
    person.wear_tie()
    person.wear_trouser()

这样写无疑是最快的,代码最简洁的,但是扩展性比较差,例如客户要求我们增加一个穿袜子的动作,我们就需要修改Person类,但是根据封闭-开发原则中的封闭原则,一个类写完之后是尽量不要修改它的,所以我们就需要另外一种实现方式

from abc import ABCMeta, abstractmethod
class Person():
    def __init__(self, name):
        print '%s开始穿衣' % name

class Finery():
    __metaclass__ = ABCMeta
    @abstractmethod
    def show(self):
        pass
class TShirt(Finery):
    def show(self):
        print '穿TShirst'

class Trouser(Finery):
    def show(self):
        print '穿裤子'

class Shoe(Finery):
    def show(self):
        print '穿鞋子'

class Tie(Finery):
    def show(self):
        print '穿领带'

if __name__ == '__main__':
    person = Person('kevin')
    finerys=[]
    finerys.append(TShirt())
    finerys.append(Trouser())
    finerys.append(Shoe())
    finerys.append(Tie())
    map(lambda x:x.show(),finerys)

首先定义一个积累Finery,定义一个抽象方法show,然后每一个穿衣动作都写一个类,重写show方法。 如果客户修改需求,我们就新增加一个类就可以了。 装饰模式的做法:

from abc import ABCMeta, abstractmethod

class Person():
    def __init__(self, name):
        self.name = name

    def decorator(self, component):
        self.component = component

    def show(self):
        print '%s开始穿衣' % self.name
        self.component.show()

class Finery():
    def __init__(self):
        self.component = None

    def decorator(self, component):
        self.component = component

    __metaclass__ = ABCMeta

    @abstractmethod
    def show(self):
        if self.component:
            self.component.show()

class TShirt(Finery):
    def show(self):
        Finery.show(self)
        print '穿TShirst'

class Trouser(Finery):
    def show(self):
        Finery.show(self)
        print '穿裤子'

class Shoe(Finery):
    def show(self):
        Finery.show(self)
        print '穿鞋子'

class Tie(Finery):
    def show(self):
        Finery.show(self)
        print '穿领带'

if __name__ == '__main__':
    person = Person('kevin')
    tshirt = TShirt()
    trouser = Trouser()
    shoe = Shoe()
    tie = Tie()

    trouser.decorator(tshirt)
    shoe.decorator(trouser)
    tie.decorator(shoe)
    person.decorator(tie)
    person.show()

每个类都有show方法,衣服类都有decorator方法,利用这个方法,动态地把不同衣服的show方法装饰到person这个类上,这样做一方面可以令person类更为精简,因为在实际应用中Person类可能会有很多方法,而穿衣服这个需求只是其中一个,另一方面是,增加Person类的可扩展性,例如如果Person类已经写好了,现在新的需求需要在某一次调用Person类的show方法的时候增加穿衣服的功能,这种模式就能很好地实现了。

5 Iterator

6 Factory

需求:有一个学雷锋活动,有买米和扫地两个内容,参与的人有大学生和社区志愿者,他们各自的方法不一样。 如果用简单工厂模式实现:

class LeiFeng():
    def buy_rice(self):
        pass

    def sweep(self):
        pass

class Student(LeiFeng):
    def buy_rice(self):
        print '大学生帮你买米'

    def sweep(self):
        print '大学生帮你扫地'

class Volunteer(LeiFeng):
    def buy_rice(self):
        print '社区志愿者帮你买米'

    def sweep(self):
        print '社区志愿者帮你扫地'

class LeiFengFactory():
    def create_lei_feng(self, type):
        map_ = {
            '大学生': Student(),
            '社区志愿者': Volunteer()
        }
        return map_[type]

if __name__ == '__main__':
    leifeng1 = LeiFengFactory().create_lei_feng('大学生')
    leifeng2 = LeiFengFactory().create_lei_feng('大学生')
    leifeng3 = LeiFengFactory().create_lei_feng('大学生')
    leifeng1.buy_rice()
    leifeng1.sweep()

写一个雷锋类,定义买米和扫地两个方法,写一个学生类和社区志愿者类,继承雷锋类,写一个工厂类,根据输入的类型返回学生类或志愿者类。 用工厂方法模式实现:

class LeiFeng():
    def buy_rice(self):
        pass

    def sweep(self):
        pass

class Student(LeiFeng):
    def buy_rice(self):
        print '大学生帮你买米'

    def sweep(self):
        print '大学生帮你扫地'

class Volunteer(LeiFeng):
    def buy_rice(self):
        print '社区志愿者帮你买米'

    def sweep(self):
        print '社区志愿者帮你扫地'

class LeiFengFactory():
    def create_lei_feng(self):
        pass

class StudentFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Student()

class VolunteerFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Volunteer()

if __name__ == '__main__':
    myFactory = StudentFactory()

    leifeng1 = myFactory.create_lei_feng()
    leifeng2 = myFactory.create_lei_feng()
    leifeng3 = myFactory.create_lei_feng()

    leifeng1.buy_rice()
    leifeng1.sweep()

雷锋类,大学生类,志愿者类和简单工厂一样,新写一个工厂方法基类,定义一个工厂方法接口(工厂方法模式的工厂方法应该就是指这个方法),然后写一个学生工厂类,志愿者工厂类,重新工厂方法,返回各自的类。 工厂方法相对于简单工厂的优点:

  • 在简单工厂中,如果需要新增类,例如加一个中学生类(MiddleStudent),就需要新写一个类,同时要修改工厂类的map_,加入'中 学生':MiddleStudent()。这样就违背了封闭开放原则中的一个类写好后,尽量不要修改里面的内容,这个原则。而在工厂方法中,需要增加一个 中学生类和一个中学生工厂类(MiddleStudentFactory),虽然比较繁琐,但是符合封闭开放原则。在工厂方法中,将判断输入的类型,返回 相应的类这个过程从工厂类中移到了客户端中实现,所以当需要新增类是,也是要修改代码的,不过是改客户端的代码而不是工厂类的代码。
  • 对代码的修改会更加方便。例如在客户端中,需要将Student的实现改为Volunteer,如果在简单工厂中,就需要把leifeng1 = LeiFengFactory().create_lei_feng('大学生')中的大学生改成社区志愿者,这里就需要改三处地方,但是在工厂方法中,只需要myFactory = StudentFactory()改成myFactory = VolunteerFactory()就可以了。

7 Observer

class Receptionist():
    def __init__(self):
        self.observes=[]
        self.status=''
    def attach(self,observe):
        self.observes.append(observe)
    def notify(self):
        for observe in self.observes:
            observe.update()

class StockObserve():
    def __init__(self,name,receptionist):
        self.name=name
        self.receptionist=receptionist
    def update(self):
        print '%s,%s停止看股票'%(self.receptionist.status,self.name)

if __name__=='__main__':
    receptionist=Receptionist()
    observe1=StockObserve('张三',receptionist)
    observe2=StockObserve('李四',receptionist)
    receptionist.attach(observe1)
    receptionist.attach(observe2)

    receptionist.status='老板来了'
    receptionist.notify()

8 Table-Driven