設計模式是什麼?node
設計模式是通過總結、優化的,對咱們常常會碰到的一些編程問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣可以直接做用於咱們的代碼。反之,設計模式更爲高級,它是一種必須在特定情形下實現的一種方法模板。設計模式不會綁定具體的編程語言。一個好的設計模式應該可以用大部分編程語言實現(若是作不到所有的話,具體取決於語言特性)。最爲重要的是,設計模式也是一把雙刃劍,若是設計模式被用在不恰當的情形下將會形成災難,進而帶來無窮的麻煩。然而若是設計模式在正確的時間被用在正確地地方,它將是你的救星。python
起初,你會認爲「模式」就是爲了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是經過不少人一塊兒工做,從不一樣的角度看待問題進而造成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,可是你的解決方案極可能沒有模式這麼完備。正則表達式
雖然被稱爲「設計模式」,可是它們同「設計「領域並不是緊密聯繫。設計模式同傳統意義上的分析、設計與實現不一樣,事實上設計模式將一個完整的理念根植於程序中,因此它可能出如今分析階段或是更高層的設計階段。頗有趣的是由於設計模式的具體體現是程序代碼,所以可能會讓你認爲它不會在具體實現階段以前出現(事實上在進入具體實現階段以前你都沒有意識到正在使用具體的設計模式)。算法
能夠經過程序設計的基本概念來理解模式:增長一個抽象層。抽象一個事物就是隔離任何具體細節,這麼作的目的是爲了將那些不變的核心部分從其餘細節中分離出來。當你發現你程序中的某些部分常常由於某些緣由改動,而你不想讓這些改動的部分引起其餘部分的改動,這時候你就須要思考那些不會變更的設計方法了。這麼作不只會使代碼可維護性更高,並且會讓代碼更易於理解,從而下降開發成本。編程
這裏列舉了三種最基本的設計模式:設計模式
建立模式,提供實例化的方法,爲適合的情況提供相應的對象建立方法。 結構化模式,一般用來處理實體之間的關係,使得這些實體可以更好地協同工做。 行爲模式,用於在不一樣的實體建進行通訊,爲實體之間的通訊提供更容易,更靈活的通訊方法。
建立型api
Factory Method(工廠方法)數據結構
Abstract Factory(抽象工廠)app
Builder(建造者)dom
Prototype(原型)
Singleton(單例)
結構型
Adapter Class/Object(適配器)
Bridge(橋接)
Composite(組合)
Decorator(裝飾)
Facade(外觀)
Flyweight(享元)
Proxy(代理)
行爲型
Interpreter(解釋器)
Template Method(模板方法)
Chain of Responsibility(責任鏈)
Command(命令)
Iterator(迭代器)
Mediator(中介者)
Memento(備忘錄)
Observer(觀察者)
State(狀態)
Strategy(策略)
Visitor(訪問者)
建立型
1.Factory Method(工廠方法)
意圖:
定義一個用於建立對象的接口,讓子類決定實例化哪個類。Factory Method 使一個類的實例化延遲到其子類。
適用性:
當一個類不知道它所必須建立的對象的類的時候。
當一個類但願由它的子類來指定它所建立的對象的時候。
當類將建立對象的職責委託給多個幫助子類中的某一個,而且你但願將哪個幫助子類是代理者這一信息局部化的時候。
實現:
!/usr/bin/python
coding:utf8
'
Factory Method
'''
class ChinaGetter:
"""A simple localizer a la gettext"""
def init(self):
self.trans = dict(dog=u"小狗", cat=u"小貓")
def get(self, msgid): """We'll punt if we don't have a translation""" try: return self.trans[msgid] except KeyError: return str(msgid)
class EnglishGetter:
"""Simply echoes the msg ids"""
def get(self, msgid):
return str(msgid)
def get_localizer(language="English"):
"""The factory method"""
languages = dict(English=EnglishGetter, China=ChinaGetter)
return languageslanguage
e, g = get_localizer("English"), get_localizer("China")
for msgid in "dog parrot cat bear".split():
print(e.get(msgid), g.get(msgid))
'''
意圖:
提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
適用性:
一個系統要獨立於它的產品的建立、組合和表示時。
一個系統要由多個產品系列中的一個來配置時。
當你要強調一系列相關的產品對象的設計以便進行聯合使用時。
當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。
'''
Abstract Factory
'''
import random
class PetShop:
"""A pet shop"""
def __init__(self, animal_factory=None): """pet_factory is our abstract factory. We can set it at will.""" self.pet_factory = animal_factory def show_pet(self): """Creates and shows a pet using the abstract factory""" pet = self.pet_factory.get_pet() print("This is a lovely", str(pet)) print("It says", pet.speak()) print("It eats", self.pet_factory.get_food())
Stuff that our factory makes
class Dog:
def speak(self):
return "woof"
def __str__(self): return "Dog"
class Cat:
def speak(self):
return "meow"
def __str__(self): return "Cat"
####### Factory classes
class DogFactory:
def get_pet(self):
return Dog()
def get_food(self): return "dog food"
class CatFactory:
def get_pet(self):
return Cat()
def get_food(self): return "cat food"
Create the proper family
def get_factory():
"""Let's be dynamic!"""
return random.choice([DogFactory, CatFactory])()
Show pets with various factories
if name == "main":
shop = PetShop()
for i in range(3):
shop.pet_factory = get_factory()
shop.show_pet()
print("=" * 20)
意圖:
將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。
適用性:
當建立複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
當構造過程必須容許被構造的對象有不一樣的表示時。
"""
Builder
"""
Director
class Director(object):
def init(self):
self.builder = None
def construct_building(self): self.builder.new_building() self.builder.build_floor() self.builder.build_size() def get_building(self): return self.builder.building
Abstract Builder
class Builder(object):
def init(self):
self.building = None
def new_building(self): self.building = Building()
Concrete Builder
class BuilderHouse(Builder):
def build_floor(self):
self.building.floor = 'One'
def build_size(self): self.building.size = 'Big'
class BuilderFlat(Builder):
def build_floor(self):
self.building.floor = 'More than One'
def build_size(self): self.building.size = 'Small'
Product
class Building(object):
def init(self):
self.floor = None
self.size = None
def __repr__(self): return 'Floor: %s | Size: %s' % (self.floor, self.size)
Client
if name == "main":
director = Director()
director.builder = BuilderHouse()
director.construct_building()
building = director.get_building()
print(building)
director.builder = BuilderFlat()
director.construct_building()
building = director.get_building()
print(building)
意圖:
用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。
適用性:
當要實例化的類是在運行時刻指定時,例如,經過動態裝載;或者爲了不建立一個與產品類層次平行的工廠類層次時;或者當一個類的實例只能有幾個不一樣狀態組合中的一種時。創建相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
'''
Prototype
'''
import copy
class Prototype:
def init(self):
self._objects = {}
def register_object(self, name, obj): """Register an object""" self._objects[name] = obj def unregister_object(self, name): """Unregister an object""" del self._objects[name] def clone(self, name, **attr): """Clone a registered object and update inner attributes dictionary""" obj = copy.deepcopy(self._objects.get(name)) obj.__dict__.update(attr) return obj
def main():
class A:
def str(self):
return "I am A"
a = A() prototype = Prototype() prototype.register_object('a', a) b = prototype.clone('a', a=1, b=2, c=3) print(a) print(b.a, b.b, b.c)
if name == 'main':
main()
意圖:
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
適用性:
當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。
當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。
實現:
'''
Singleton
'''
class Singleton(object):
''''' A python style singleton '''
def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): org = super(Singleton, cls) cls._instance = org.__new__(cls, *args, **kw) return cls._instance
if name == 'main':
class SingleSpam(Singleton):
def init(self, s):
self.s = s
def __str__(self): return self.s s1 = SingleSpam('spam') print id(s1), s1 s2 = SingleSpam('spa') print id(s2), s2 print id(s1), s1
結構型
意圖:
將一個類的接口轉換成客戶但願的另一個接口。Adapter 模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
適用性:
你想使用一個已經存在的類,而它的接口不符合你的需求。
你想建立一個能夠複用的類,該類能夠與其餘不相關的類或不可預見的類(即那些接口可能不必定兼容的類)協同工做。
(僅適用於對象Adapter )你想使用一些已經存在的子類,可是不可能對每個都進行子類化以匹配它們的接口。對象適配器能夠適配它的父類接口。
'''
Adapter
'''
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%s" % ("!" * octane_level)
class Adapter(object):
"""
Adapts an object by replacing methods.
Usage:
dog = Dog
dog = Adapter(dog, dict(make_noise=dog.bark))
"""
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, dict(make_noise=dog.bark)))
cat = Cat()
objects.append(Adapter(cat, dict(make_noise=cat.meow)))
human = Human()
objects.append(Adapter(human, dict(make_noise=human.speak)))
car = Car()
car_noise = lambda: car.make_noise(3)
objects.append(Adapter(car, dict(make_noise=car_noise)))
for obj in objects: print "A", obj.name, "goes", obj.make_noise()
if name == "main":
main()
意圖:
將抽象部分與它的實現部分分離,使它們均可以獨立地變化。
適用性:
你不但願在抽象和它的實現部分之間有一個固定的綁定關係。例如這種狀況多是由於,在程序運行時刻實現部分應能夠被選擇或者切換。
類的抽象以及它的實現都應該能夠經過生成子類的方法加以擴充。這時Bridge 模式使你能夠對不一樣的抽象接口和實現部分進行組合,並分別對它們進行擴充。
對一個抽象的實現部分的修改應對客戶不產生影響,即客戶的代碼沒必要從新編譯。
(C++)你想對客戶徹底隱藏抽象的實現部分。在C++中,類的表示在類接口中是可見的。
有許多類要生成。這樣一種類層次結構說明你必須將一個對象分解成兩個部分。Rumbaugh 稱這種類層次結構爲「嵌套的普化」(nested generalizations )。
你想在多個對象間共享實現(可能使用引用計數),但同時要求客戶並不知道這一點。一個簡單的例子即是Coplien 的String 類[ Cop92 ],在這個類中多個對象能夠共享同一個字符串表示(StringRep)。
'''
Bridge
'''
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()
意圖:
將對象組合成樹形結構以表示「部分-總體」的層次結構。C o m p o s i t e 使得用戶對單個對象和組合對象的使用具備一致性。
適用性:
你想表示對象的部分-總體層次結構。
你但願用戶忽略組合對象與單個對象的不一樣,用戶將統一地使用組合結構中的全部對象。
"""
Composite
"""
class Component:
def init(self,strName):
self.m_strName = strName
def Add(self,com):
pass
def Display(self,nDepth):
pass
class Leaf(Component):
def Add(self,com):
print "leaf can't add"
def Display(self,nDepth):
strtemp = "-" * nDepth
strtemp=strtemp+self.m_strName
print strtemp
class Composite(Component):
def init(self,strName):
self.m_strName = strName
self.c = []
def Add(self,com):
self.c.append(com)
def Display(self,nDepth):
strtemp = "-"*nDepth
strtemp=strtemp+self.m_strName
print strtemp
for com in self.c:
com.Display(nDepth+2)
if name == "main":
p = Composite("Wong")
p.Add(Leaf("Lee"))
p.Add(Leaf("Zhao"))
p1 = Composite("Wu")
p1.Add(Leaf("San"))
p.Add(p1)
p.Display(1);
意圖:
動態地給一個對象添加一些額外的職責。就增長功能來講,Decorator 模式相比生成子類更爲靈活。
適用性:
在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責。
處理那些能夠撤消的職責。
當不能採用生成子類的方法進行擴充時。一種狀況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加。另外一種狀況多是由於類定義被隱藏,或類定義不能用於生成子類。
'''
Decorator
'''
class foo(object):
def f1(self):
print("original f1")
def f2(self): print("original f2")
class foo_decorator(object):
def init(self, decoratee):
self._decoratee = decoratee
def f1(self): print("decorated f1") self._decoratee.f1() def __getattr__(self, name): return getattr(self._decoratee, name)
u = foo()
v = foo_decorator(u)
v.f1()
v.f2()
意圖:
爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
適用性:
當你要爲一個複雜子系統提供一個簡單接口時。子系統每每由於不斷演化而變得愈來愈複雜。大多數模式使用時都會產生更多更小的類。這使得子系統更具可重用性,也更容易對子系統進行定製,但這也給那些不須要定製子系統的用戶帶來一些使用上的困難。Facade 能夠提供一個簡單的缺省視圖,這一視圖對大多數用戶來講已經足夠,而那些須要更多的可定製性的用戶能夠越過facade層。
客戶程序與抽象類的實現部分之間存在着很大的依賴性。引入facade 將這個子系統與客戶以及其餘的子系統分離,能夠提升子系統的獨立性和可移植性。
當你須要構建一個層次結構的子系統時,使用facade模式定義子系統中每層的入口點。若是子系統之間是相互依賴的,你可讓它們僅經過facade進行通信,從而簡化了它們之間的依賴關係。
'''
Decorator
'''
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()
意圖:
運用共享技術有效地支持大量細粒度的對象。
適用性:
一個應用程序使用了大量的對象。
徹底因爲使用大量的對象,形成很大的存儲開銷。
對象的大多數狀態均可變爲外部狀態。
若是刪除對象的外部狀態,那麼能夠用相對較少的共享對象取代不少組對象。
應用程序不依賴於對象標識。因爲Flyweight 對象能夠被共享,對於概念上明顯有別的對象,標識測試將返回真值。
'''
Flyweight
'''
import weakref
class Card(object):
"""The object pool. Has builtin reference counting"""
_CardPool = weakref.WeakValueDictionary()
"""Flyweight implementation. If the object exists in the pool just return it (instead of creating a new one)""" def __new__(cls, value, suit): obj = Card._CardPool.get(value + suit, None) if not obj: obj = object.__new__(cls) Card._CardPool[value + suit] = obj obj.value, obj.suit = value, suit return obj # def __init__(self, value, suit): # self.value, self.suit = value, suit def __repr__(self): return "<Card: %s%s>" % (self.value, self.suit)
if name == 'main':
# comment new and uncomment init to see the difference
c1 = Card('9', 'h')
c2 = Card('9', 'h')
print(c1, c2)
print(c1 == c2)
print(id(c1), id(c2))
意圖:
爲其餘對象提供一種代理以控制對這個對象的訪問。
適用性:
在須要用比較通用和複雜的對象指針代替簡單的指針的時候,使用Proxy模式。下面是一 些可使用Proxy 模式常見狀況:
1) 遠程代理(Remote Proxy )爲一個對象在不一樣的地址空間提供局部表明。 NEXTSTEP[Add94] 使用NXProxy 類實現了這一目的。Coplien[Cop92] 稱這種代理爲「大使」 (Ambassador )。
2 )虛代理(Virtual Proxy )根據須要建立開銷很大的對象。在動機一節描述的ImageProxy 就是這樣一種代理的例子。
3) 保護代理(Protection Proxy )控制對原始對象的訪問。保護代理用於對象應該有不一樣 的訪問權限的時候。例如,在Choices 操做系統[ CIRM93]中KemelProxies爲操做系統對象提供 了訪問保護。
4 )智能指引(Smart Reference )取代了簡單的指針,它在訪問對象時執行一些附加操做。 它的典型用途包括:對指向實際對象的引用計數,這樣當該對象沒有引用時,能夠自動釋放它(也稱爲SmartPointers[Ede92 ] )。
當第一次引用一個持久對象時,將它裝入內存。
在訪問一個實際對象前,檢查是否已經鎖定了它,以確保其餘對象不能改變它。
'''
Proxy
'''
import time
class SalesManager:
def work(self):
print("Sales Manager working...")
def talk(self): print("Sales Manager ready to talk")
class Proxy:
def init(self):
self.busy = 'No'
self.sales = None
def work(self): print("Proxy checking for Sales Manager availability") if self.busy == 'No': self.sales = SalesManager() time.sleep(2) self.sales.talk() else: time.sleep(2) print("Sales Manager is busy")
if name == 'main':
p = Proxy()
p.work()
p.busy = 'Yes'
p.work()
行爲型
意圖:
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
適用性:
當有一個語言須要解釋執行, 而且你可將該語言中的句子表示爲一個抽象語法樹時,可以使用解釋器模式。而當存在如下狀況時該模式效果最好:
該文法簡單對於複雜的文法, 文法的類層次變得龐大而沒法管理。此時語法分析程序生成器這樣的工具是更好的選擇。它們無需構建抽象語法樹便可解釋表達式, 這樣能夠節省空間並且還可能節省時間。
效率不是一個關鍵問題最高效的解釋器一般不是經過直接解釋語法分析樹實現的, 而是首先將它們轉換成另外一種形式。例如,正則表達式一般被轉換成狀態機。但即便在這種狀況下, 轉換器仍可用解釋器模式實現, 該模式還是有用的。
'''
Interpreter
'''
class Context:
def init(self):
self.input=""
self.output=""
class AbstractExpression:
def Interpret(self,context):
pass
class Expression(AbstractExpression):
def Interpret(self,context):
print "terminal interpret"
class NonterminalExpression(AbstractExpression):
def Interpret(self,context):
print "Nonterminal interpret"
if name == "main":
context= ""
c = []
c = c + [Expression()]
c = c + [NonterminalExpression()]
c = c + [Expression()]
c = c + [Expression()]
for a in c:
a.Interpret(context)
意圖:
定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
適用性:
一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。這是Opdyke 和Johnson所描述過的「重分解以通常化」的一個很好的例子[ OJ93 ]。首先識別現有代碼中的不一樣之處,而且將不一樣之處分離爲新的操做。最後,用一個調用這些新的操做的模板方法來替換這些不一樣的代碼。
控制子類擴展。模板方法只在特定點調用「hook 」操做(參見效果一節),這樣就只容許在這些點進行擴展。
'''
Template Method
'''
ingredients = "spam eggs apple"
line = '-' * 10
Skeletons
def iter_elements(getter, action):
"""Template skeleton that iterates items"""
for element in getter():
action(element)
print(line)
def rev_elements(getter, action):
"""Template skeleton that iterates items in reverse order"""
for element in getter()[::-1]:
action(element)
print(line)
Getters
def get_list():
return ingredients.split()
def get_lists():
return [list(x) for x in ingredients.split()]
Actions
def print_item(item):
print(item)
def reverse_item(item):
print(item[::-1])
Makes templates
def make_template(skeleton, getter, action):
"""Instantiate a template method with getter and action"""
def template():
skeleton(getter, action)
return template
Create our template functions
templates = [make_template(s, g, a)
for g in (get_list, get_lists)
for a in (print_item, reverse_item)
for s in (iter_elements, rev_elements)]
Execute them
for template in templates:
template()
意圖:
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
適用性:
有多個的對象能夠處理一個請求,哪一個對象處理該請求運行時刻自動肯定。
你想在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。
可處理一個請求的對象集合應被動態指定。
"""
Chain
"""
class Handler:
def successor(self, successor):
self.successor = successor
class ConcreteHandler1(Handler):
def handle(self, request):
if request > 0 and request <= 10:
print("in handler1")
else:
self.successor.handle(request)
class ConcreteHandler2(Handler):
def handle(self, request):
if request > 10 and request <= 20:
print("in handler2")
else:
self.successor.handle(request)
class ConcreteHandler3(Handler):
def handle(self, request):
if request > 20 and request <= 30:
print("in handler3")
else:
print('end of chain, no handler for {}'.format(request))
class Client:
def init(self):
h1 = ConcreteHandler1()
h2 = ConcreteHandler2()
h3 = ConcreteHandler3()
h1.successor(h2) h2.successor(h3) requests = [2, 5, 14, 22, 18, 3, 35, 27, 20] for request in requests: h1.handle(request)
if name == "main":
client = Client()
意圖:
將一個請求封裝爲一個對象,從而使你可用不一樣的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤消的操做。
適用性:
抽象出待執行的動做以參數化某對象,你可用過程語言中的回調(call back)函數表達這種參數化機制。所謂回調函數是指函數先在某處註冊,而它將在稍後某個須要的時候被調用。Command 模式是回調機制的一個面向對象的替代品。
在不一樣的時刻指定、排列和執行請求。一個Command對象能夠有一個與初始請求無關的生存期。若是一個請求的接收者可用一種與地址空間無關的方式表達,那麼就可將負責該請求的命令對象傳送給另外一個不一樣的進程並在那兒實現該請求。
支持取消操做。Command的Excute 操做可在實施操做前將狀態存儲起來,在取消操做時這個狀態用來消除該操做的影響。Command 接口必須添加一個Unexecute操做,該操做取消上一次Execute調用的效果。執行的命令被存儲在一個歷史列表中。可經過向後和向前遍歷這一列表並分別調用Unexecute和Execute來實現重數不限的「取消」和「重作」。
支持修改日誌,這樣當系統崩潰時,這些修改能夠被重作一遍。在Command接口中添加裝載操做和存儲操做,能夠用來保持變更的一個一致的修改日誌。從崩潰中恢復的過程包括從磁盤中從新讀入記錄下來的命令並用Execute操做從新執行它們。
用構建在原語操做上的高層操做構造一個系統。這樣一種結構在支持事務( transaction)的信息系統中很常見。一個事務封裝了對數據的一組變更。Command模式提供了對事務進行建模的方法。Command有一個公共的接口,使得你能夠用同一種方式調用全部的事務。同時使用該模式也易於添加新事務以擴展系統。
"""
Command
"""
import os
class MoveFileCommand(object):
def init(self, src, dest):
self.src = src
self.dest = dest
def execute(self): self() def __call__(self): print('renaming {} to {}'.format(self.src, self.dest)) os.rename(self.src, self.dest) def undo(self): print('renaming {} to {}'.format(self.dest, self.src)) os.rename(self.dest, self.src)
if name == "main":
command_stack = []
# commands are just pushed into the command stack command_stack.append(MoveFileCommand('foo.txt', 'bar.txt')) command_stack.append(MoveFileCommand('bar.txt', 'baz.txt')) # they can be executed later on for cmd in command_stack: cmd.execute() # and can also be undone at will for cmd in reversed(command_stack): cmd.undo()
意圖:
提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。
適用性:
訪問一個聚合對象的內容而無需暴露它的內部表示。
支持對聚合對象的多種遍歷。
爲遍歷不一樣的聚合結構提供一個統一的接口(即, 支持多態迭代)。
'''
Interator
'''
def count_to(count):
"""Counts by word numbers, up to a maximum of five"""
numbers = ["one", "two", "three", "four", "five"]
# enumerate() returns a tuple containing a count (from start which
# defaults to 0) and the values obtained from iterating over sequence
for pos, number in zip(range(count), numbers):
yield number
Test the generator
count_to_two = lambda: count_to(2)
count_to_five = lambda: count_to(5)
print('Counting to two...')
for number in count_to_two():
print number
print " "
print('Counting to five...')
for number in count_to_five():
print number
print " "
意圖:
用一箇中介對象來封裝一系列的對象交互。中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。
適用性:
一組對象以定義良好可是複雜的方式進行通訊。產生的相互依賴關係結構混亂且難以理解。
一個對象引用其餘不少對象而且直接與這些對象通訊,致使難以複用該對象。
想定製一個分佈在多個類中的行爲,而又不想生成太多的子類。
'''
Mediator
'''
"""http://dpip.testingperspective.com/?p=28"""
import time
class TC:
def init(self):
self._tm = tm
self._bProblem = 0
def setup(self): print("Setting up the Test") time.sleep(1) self._tm.prepareReporting() def execute(self): if not self._bProblem: print("Executing the test") time.sleep(1) else: print("Problem in setup. Test not executed.") def tearDown(self): if not self._bProblem: print("Tearing down") time.sleep(1) self._tm.publishReport() else: print("Test not executed. No tear down required.") def setTM(self, TM): self._tm = tm def setProblem(self, value): self._bProblem = value
class Reporter:
def init(self):
self._tm = None
def prepare(self): print("Reporter Class is preparing to report the results") time.sleep(1) def report(self): print("Reporting the results of Test") time.sleep(1) def setTM(self, TM): self._tm = tm
class DB:
def init(self):
self._tm = None
def insert(self): print("Inserting the execution begin status in the Database") time.sleep(1) #Following code is to simulate a communication from DB to TC import random if random.randrange(1, 4) == 3: return -1 def update(self): print("Updating the test results in Database") time.sleep(1) def setTM(self, TM): self._tm = tm
class TestManager:
def init(self):
self._reporter = None
self._db = None
self._tc = None
def prepareReporting(self): rvalue = self._db.insert() if rvalue == -1: self._tc.setProblem(1) self._reporter.prepare() def setReporter(self, reporter): self._reporter = reporter def setDB(self, db): self._db = db def publishReport(self): self._db.update() rvalue = self._reporter.report() def setTC(self, tc): self._tc = tc
if name == 'main':
reporter = Reporter()
db = DB()
tm = TestManager()
tm.setReporter(reporter)
tm.setDB(db)
reporter.setTM(tm)
db.setTM(tm)
# For simplification we are looping on the same test.
# Practically, it could be about various unique test classes and their
# objects
while (True):
tc = TC()
tc.setTM(tm)
tm.setTC(tc)
tc.setup()
tc.execute()
tc.tearDown()
意圖:
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就可將該對象恢復到原先保存的狀態。
適用性:
必須保存一個對象在某一個時刻的(部分)狀態, 這樣之後須要時它才能恢復到先前的狀態。
若是一個用接口來讓其它對象直接獲得這些狀態,將會暴露對象的實現細節並破壞對象的封裝性。
'''
Memento
'''
import copy
def Memento(obj, deep=False):
state = (copy.copy, copy.deepcopy)bool(deep)
def Restore(): obj.__dict__.clear() obj.__dict__.update(state) return Restore
class Transaction:
"""A transaction guard. This is really just
syntactic suggar arount a memento closure.
"""
deep = False
def __init__(self, *targets): self.targets = targets self.Commit() def Commit(self): self.states = [Memento(target, self.deep) for target in self.targets] def Rollback(self): for st in self.states: st()
class transactional(object):
"""Adds transactional semantics to methods. Methods decorated with
@transactional will rollback to entry state upon exceptions.
"""
def init(self, method):
self.method = method
def __get__(self, obj, T): def transaction(*args, **kwargs): state = Memento(obj) try: return self.method(obj, *args, **kwargs) except: state() raise return transaction
class NumObj(object):
def init(self, value):
self.value = value
def __repr__(self): return '<%s: %r>' % (self.__class__.__name__, self.value) def Increment(self): self.value += 1 @transactional def DoStuff(self): self.value = '1111' # <- invalid value self.Increment() # <- will fail and rollback
if name == 'main':
n = NumObj(-1)
print(n)
t = Transaction(n)
try:
for i in range(3):
n.Increment()
print(n)
t.Commit()
print('-- commited')
for i in range(3):
n.Increment()
print(n)
n.value += 'x' # will fail
print(n)
except:
t.Rollback()
print('-- rolled back')
print(n)
print('-- now doing stuff ...')
try:
n.DoStuff()
except:
print('-> doing stuff failed!')
import traceback
traceback.print_exc(0)
pass
print(n)
意圖:
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 全部依賴於它的對象都獲得通知並被自動更新。
適用性:
當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。
當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。
當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。
'''
Observer
'''
class Subject(object):
def init(self):
self._observers = []
def attach(self, observer): if not observer in self._observers: self._observers.append(observer) def detach(self, observer): try: self._observers.remove(observer) except ValueError: pass def notify(self, modifier=None): for observer in self._observers: if modifier != observer: observer.update(self)
Example usage
class Data(Subject):
def init(self, name=''):
Subject.__init__(self)
self.name = name
self._data = 0
@property def data(self): return self._data @data.setter def data(self, value): self._data = value self.notify()
class HexViewer:
def update(self, subject):
print('HexViewer: Subject %s has data 0x%x' %
(subject.name, subject.data))
class DecimalViewer:
def update(self, subject):
print('DecimalViewer: Subject %s has data %d' %
(subject.name, subject.data))
Example usage...
def main():
data1 = Data('Data 1')
data2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
print("Setting Data 1 = 10") data1.data = 10 print("Setting Data 2 = 15") data2.data = 15 print("Setting Data 1 = 3") data1.data = 3 print("Setting Data 2 = 5") data2.data = 5 print("Detach HexViewer from data1 and data2.") data1.detach(view2) data2.detach(view2) print("Setting Data 1 = 10") data1.data = 10 print("Setting Data 2 = 15") data2.data = 15
if name == 'main':
main()
意圖:
容許一個對象在其內部狀態改變時改變它的行爲。對象看起來彷佛修改了它的類。
適用性:
一個對象的行爲取決於它的狀態, 而且它必須在運行時刻根據狀態改變它的行爲。
一個操做中含有龐大的多分支的條件語句,且這些分支依賴於該對象的狀態。這個狀態一般用一個或多個枚舉常量表示。一般, 有多個操做包含這一相同的條件結構。State模式將每個條件分支放入一個獨立
'''
State
'''
class State(object):
"""Base state. This is to share functionality"""
def scan(self): """Scan the dial to the next station""" self.pos += 1 if self.pos == len(self.stations): self.pos = 0 print("Scanning... Station is", self.stations[self.pos], self.name)
class AmState(State):
def init(self, radio):
self.radio = radio
self.stations = ["1250", "1380", "1510"]
self.pos = 0
self.name = "AM"
def toggle_amfm(self): print("Switching to FM") self.radio.state = self.radio.fmstate
class FmState(State):
def init(self, radio):
self.radio = radio
self.stations = ["81.3", "89.1", "103.9"]
self.pos = 0
self.name = "FM"
def toggle_amfm(self): print("Switching to AM") self.radio.state = self.radio.amstate
class Radio(object):
"""A radio. It has a scan button, and an AM/FM toggle switch."""
def init(self):
"""We have an AM state and an FM state"""
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self): self.state.toggle_amfm() def scan(self): self.state.scan()
Test our radio out
if name == 'main':
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
actions = actions * 2
for action in actions: action()
意圖:
定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
適用性:
許多相關的類僅僅是行爲有異。「策略」提供了一種用多個行爲中的一個行爲來配置一個類的方法。
須要使用一個算法的不一樣變體。例如,你可能會定義一些反映不一樣的空間/時間權衡的算法。當這些變體實現爲一個算法的類層次時[H087] ,可使用策略模式。
算法使用客戶不該該知道的數據。可以使用策略模式以免暴露覆雜的、與算法相關的數據結構。
一個類定義了多種行爲, 而且這些行爲在這個類的操做中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。
"""
Strategy
In most of other languages Strategy pattern is implemented via creating some base strategy interface/abstract class and
subclassing it with a number of concrete strategies (as we can see at http://en.wikipedia.org/wiki/Strategy_pattern),
however Python supports higher-order functions and allows us to have only one class and inject functions into it's
instances, as shown in this example.
"""
import types
class StrategyExample:
def init(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func, self)
def execute(self): print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if name == 'main':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1) strat1.name = 'Strategy Example 1' strat2 = StrategyExample(execute_replacement2) strat2.name = 'Strategy Example 2' strat0.execute() strat1.execute() strat2.execute()
意圖:
定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
適用性:
一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。這是Opdyke和Johnson所描述過的「重分解以通常化」的一個很好的例子[OJ93]。首先識別現有代碼中的不一樣之處,而且將不一樣之處分離爲新的操做。最後,用一個調用這些新的操做的模板方法來替換這些不一樣的代碼。
控制子類擴展。模板方法只在特定點調用「hook 」操做(參見效果一節),這樣就只容許在這些點進行擴展。
'''
Visitor
'''
class Node(object):
pass
class A(Node):
pass
class B(Node):
pass
class C(A, B):
pass
class Visitor(object):
def visit(self, node, *args, **kwargs):
meth = None
for cls in node.__class__.__mro__:
meth_name = 'visit_'+cls.__name__
meth = getattr(self, meth_name, None)
if meth:
break
if not meth: meth = self.generic_visit return meth(node, *args, **kwargs) def generic_visit(self, node, *args, **kwargs): print('generic_visit '+node.__class__.__name__) def visit_B(self, node, *args, **kwargs): print('visit_B '+node.__class__.__name__)
a = A() b = B() c = C() visitor = Visitor() visitor.visit(a) visitor.visit(b) visitor.visit(c)