(轉)二十三種設計模式及其python實現
本文原始碼寄方於github:https://github.com/w392807287/Design_pattern_of_python
參考文獻:
《大話設計模式》——吳強
《Python設計模式》——pythontip.com
《23種設計模式》——http://www.cnblogs.com/beijiguangyong/
設計模式是什麼?
設計模式是經過總結、優化的,對我們經常會碰到的一些程式設計問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣能夠直接作用於我們的程式碼。反之,設計模式更為高階,它是一種必須在特定情形下實現的一種方法模板。設計模式不會繫結具體的程式語言。一個好的設計模式應該能夠用大部分程式語言實現(如果做不到全部的話,具體取決於語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。
起初,你會認為“模式”就是為了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是通過很多人一起工作,從不同的角度看待問題進而形成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,但是你的解決方案很可能沒有模式這麼完備。
雖然被稱為“設計模式”,但是它們同“設計“領域並非緊密聯絡。設計模式同傳統意義上的分析、設計與實現不同,事實上設計模式將一個完整的理念根植於程式中,所以它可能出現在分析階段或是更高層的設計階段。很有趣的是因為設計模式的具體體現是程式程式碼,因此可能會讓你認為它不會在具體實現階段之前出現(事實上在進入具體實現階段之前你都沒有意識到正在使用具體的設計模式)。
可以通過程式設計的基本概念來理解模式:增加一個抽象層。抽象一個事物就是隔離任何具體細節,這麼做的目的是為了將那些不變的核心部分從其他細節中分離出來。當你發現你程式中的某些部分經常因為某些原因改動,而你不想讓這些改動的部分引發其他部分的改動,這時候你就需要思考那些不會變動的設計方法了。這麼做不僅會使程式碼可維護性更高,而且會讓程式碼更易於理解,從而降低開發成本。
這裡列舉了三種最基本的設計模式:
- 建立模式,提供例項化的方法,為適合的狀況提供相應的物件建立方法。
- 結構化模式,通常用來處理實體之間的關係,使得這些實體能夠更好地協同工作。
- 行為模式,用於在不同的實體建進行通訊,為實體之間的通訊提供更容易,更靈活的通訊方法
建立型
1. Factory Method(工廠方法)
2. Abstract Factory(抽象工廠)
3. Builder(建造者)
4. Prototype(原型)
5. Singleton(單例)
結構型
6. Adapter Class/Object(介面卡)
7. Bridge(橋接)
8. Composite(組合)
9. Decorator(裝飾)
10. Facade(外觀)
11. Flyweight(享元)
12. Proxy(代理)
行為型
13. Interpreter(直譯器)
14. Template Method(模板方法)
15. Chain of Responsibility(責任鏈)
16. Command(命令)
17. Iterator(迭代器)
18. Mediator(中介者)
19. Memento(備忘錄)
20. Observer(觀察者)
21. State(狀態)
22. Strategy(策略)
23. 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 languages[language]() # Create our localizers e, g = get_localizer("English"), get_localizer("China") # Localize some text for msgid in "dog parrot cat bear".split(): print(e.get(msgid), g.get(msgid))
2. Abstract Factory(抽象工廠)
意圖:
提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。
適用性:
一個系統要獨立於它的產品的建立、組合和表示時。
一個系統要由多個產品系列中的一個來配置時。
當你要強調一系列相關的產品物件的設計以便進行聯合使用時。
當你提供一個產品類庫,而只想顯示它們的介面而不是實現時。
#!/usr/bin/python #coding:utf8 ''' 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)
3. Builder(建造者)
意圖:
將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
適用性:
當建立複雜物件的演算法應該獨立於該物件的組成部分以及它們的裝配方式時。
當構造過程必須允許被構造的物件有不同的表示時。
#!/usr/bin/python #coding:utf8 """ 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)
4. Prototype(原型)
意圖:
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
適用性:
當要例項化的類是在執行時刻指定時,例如,通過動態裝載;或者為了避免建立一個與產品類層次平行的工廠類層次時;或者當一個類的例項只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工例項化該類更方便一些。
#!/usr/bin/python #coding:utf8 ''' 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()
5. Singleton(單例)
意圖:
保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
適用性:
當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時。
當這個唯一例項應該是通過子類化可擴充套件的,並且客戶應該無需更改程式碼就能使用一個擴充套件的例項時。
實現:
#!/usr/bin/python #coding:utf8 ''' 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
結構型
6. Adapter Class/Object(介面卡)
意圖:
將一個類的介面轉換成客戶希望的另外一個介面。Adapter 模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
適用性:
你想使用一個已經存在的類,而它的介面不符合你的需求。
你想建立一個可以複用的類,該類可以與其他不相關的類或不可預見的類(即那些介面可能不一定相容的類)協同工作。
(僅適用於物件Adapter )你想使用一些已經存在的子類,但是不可能對每一個都進行子類化以匹配它們的介面。物件介面卡可以適配它的父類介面。
#!/usr/bin/python #coding:utf8 ''' 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()
7. Bridge(橋接)
意圖:
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
適用性:
你不希望在抽象和它的實現部分之間有一個固定的繫結關係。例如這種情況可能是因為,在程式執行時刻實現部分應可以被選擇或者切換。
類的抽象以及它的實現都應該可以通過生成子類的方法加以擴充。這時Bridge 模式使你可以對不同的抽象介面和實現部分進行組合,並分別對它們進行擴充。
對一個抽象的實現部分的修改應對客戶不產生影響,即客戶的程式碼不必重新編譯。
(C++)你想對客戶完全隱藏抽象的實現部分。在C++中,類的表示在類介面中是可見的。
有許多類要生成。這樣一種類層次結構說明你必須將一個物件分解成兩個部分。Rumbaugh 稱這種類層次結構為“巢狀的普化”(nested generalizations )。
你想在多個物件間共享實現(可能使用引用計數),但同時要求客戶並不知道這一點。一個簡單的例子便是Coplien 的String 類[ Cop92 ],在這個類中多個物件可以共享同一個字串表示(StringRep)。
#!/usr/bin/python #coding:utf8 ''' 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()
8. Composite(組合)
意圖:
將物件組合成樹形結構以表示“部分-整體”的層次結構。C o m p o s i t e 使得使用者對單個物件和組合物件的使用具有一致性。
適用性:
你想表示物件的部分-整體層次結構。
你希望使用者忽略組合物件與單個物件的不同,使用者將統一地使用組合結構中的所有物件。
#!/usr/bin/python #coding:utf8 """ 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);
9. Decorator(裝飾)
意圖:
動態地給一個物件新增一些額外的職責。就增加功能來說,Decorator 模式相比生成子類更為靈活。
適用性:
在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責。
處理那些可以撤消的職責。
當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴充套件,為支援每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。
#!/usr/bin/python #coding:utf8 ''' 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()
10. Facade(外觀)
意圖:
為子系統中的一組介面提供一個一致的介面,Facade模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
適用性:
當你要為一個複雜子系統提供一個簡單介面時。子系統往往因為不斷演化而變得越來越複雜。大多數模式使用時都會產生更多更小的類。這使得子系統更具可重用性,也更容易對子系統進行定製,但這也給那些不需要定製子系統的使用者帶來一些使用上的困難。Facade 可以提供一個簡單的預設檢視,這一檢視對大多數使用者來說已經足夠,而那些需要更多的可定製性的使用者可以越過facade層。
客戶程式與抽象類的實現部分之間存在著很大的依賴性。引入facade 將這個子系統與客戶以及其他的子系統分離,可以提高子系統的獨立性和可移植性。
當你需要構建一個層次結構的子系統時,使用facade模式定義子系統中每層的入口點。如果子系統之間是相互依賴的,你可以讓它們僅通過facade進行通訊,從而簡化了它們之間的依賴關係。
#!/usr/bin/python #coding:utf8 ''' 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()
11. Flyweight(享元)
意圖:
運用共享技術有效地支援大量細粒度的物件。
適用性:
一個應用程式使用了大量的物件。
完全由於使用大量的物件,造成很大的儲存開銷。
物件的大多數狀態都可變為外部狀態。
如果刪除物件的外部狀態,那麼可以用相對較少的共享物件取代很多組物件。
應用程式不依賴於物件標識。由於Flyweight 物件可以被共享,對於概念上明顯有別的物件,標識測試將返回真值。
#!/usr/bin/python #coding:utf8 ''' 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))
12. Proxy(代理)
意圖:
為其他物件提供一種代理以控制對這個物件的訪問。
適用性:
在需要用比較通用和複雜的物件指標代替簡單的指標的時候,使用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 ] )。
當第一次引用一個持久物件時,將它裝入記憶體。
在訪問一個實際物件前,檢查是否已經鎖定了它,以確保其他物件不能改變它。
#!/usr/bin/python #coding:utf8 ''' 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()
行為型
13. Interpreter(直譯器)
意圖:
給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。
適用性:
當有一個語言需要解釋執行, 並且你可將該語言中的句子表示為一個抽象語法樹時,可使用直譯器模式。而當存在以下情況時該模式效果最好:
該文法簡單對於複雜的文法, 文法的類層次變得龐大而無法管理。此時語法分析程式生成器這樣的工具是更好的選擇。它們無需構建抽象語法樹即可解釋表示式, 這樣可以節省空間而且還可能節省時間。
效率不是一個關鍵問題最高效的直譯器通常不是通過直接解釋語法分析樹實現的, 而是首先將它們轉換成另一種形式。例如,正則表示式通常被轉換成狀態機。但即使在這種情況下, 轉換器仍可用直譯器模式實現, 該模式仍是有用的。
#!/usr/bin/python #coding:utf8 ''' 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)
14. Template Method(模板方法)
意圖:
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
適用性:
一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實現。
各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。這是Opdyke 和Johnson所描述過的“重分解以一般化”的一個很好的例子[ OJ93 ]。首先識別現有程式碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個呼叫這些新的操作的模板方法來替換這些不同的程式碼。
控制子類擴充套件。模板方法只在特定點呼叫“hook ”操作(參見效果一節),這樣就只允許在這些點進行擴充套件。
#!/usr/bin/python #coding:utf8 ''' 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()
15. Chain of Responsibility(責任鏈)
意圖:
使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
適用性:
有多個的物件可以處理一個請求,哪個物件處理該請求執行時刻自動確定。
你想在不明確指定接收者的情況下,向多個物件中的一個提交一個請求。
可處理一個請求的物件集合應被動態指定。
#!/usr/bin/python #coding:utf8 """ 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()
16. Command(命令)
意圖:
將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。
適用性:
抽象出待執行的動作以引數化某物件,你可用過程語言中的回撥(call back)函式表達這種引數化機制。所謂回撥函式是指函式先在某處註冊,而它將在稍後某個需要的時候被呼叫。Command 模式是回撥機制的一個面向物件的替代品。
在不同的時刻指定、排列和執行請求。一個Command物件可以有一個與初始請求無關的生存期。如果一個請求的接收者可用一種與地址空間無關的方式表達,那麼就可將負責該請求的命令物件傳送給另一個不同的程序並在那兒實現該請求。
支援取消操作。Command的Excute 操作可在實施操作前將狀態儲存起來,在取消操作時這個狀態用來消除該操作的影響。Command 介面必須新增一個Unexecute操作,該操作取消上一次Execute呼叫的效果。執行的命令被儲存在一個歷史列表中。可通過向後和向前遍歷這一列表並分別呼叫Unexecute和Execute來實現重數不限的“取消”和“重做”。
支援修改日誌,這樣當系統崩潰時,這些修改可以被重做一遍。在Command介面中新增裝載操作和儲存操作,可以用來保持變動的一個一致的修改日誌。從崩潰中恢復的過程包括從磁碟中重新讀入記錄下來的命令並用Execute操作重新執行它們。
用構建在原語操作上的高層操作構造一個系統。這樣一種結構在支援事務( transaction)的資訊系統中很常見。一個事務封裝了對資料的一組變動。Command模式提供了對事務進行建模的方法。Command有一個公共的介面,使得你可以用同一種方式呼叫所有的事務。同時使用該模式也易於新增新事務以擴充套件系統。
#!/usr/bin/python #coding:utf8 """ 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()
17. Iterator(迭代器)
意圖:
提供一種方法順序訪問一個聚合物件中各個元素, 而又不需暴露該物件的內部表示。
適用性:
訪問一個聚合物件的內容而無需暴露它的內部表示。
支援對聚合物件的多種遍歷。
為遍歷不同的聚合結構提供一個統一的介面(即, 支援多型迭代)。
#!/usr/bin/python #coding:utf8 ''' 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 " "
18. Mediator(中介者)
意圖:
用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。
適用性:
一組物件以定義良好但是複雜的方式進行通訊。產生的相互依賴關係結構混亂且難以理解。
一個物件引用其他很多物件並且直接與這些物件通訊,導致難以複用該物件。
想定製一個分佈在多個類中的行為,而又不想生成太多的子類。
#!/usr/bin/python #coding:utf8 ''' 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()
19. Memento(備忘錄)
意圖:
在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。
適用性:
必須儲存一個物件在某一個時刻的(部分)狀態, 這樣以後需要時它才能恢復到先前的狀態。
如果一個用介面來讓其它物件直接得到這些狀態,將會暴露物件的實現細節並破壞物件的封裝性。
#!/usr/bin/python #coding:utf8 ''' Memento ''' import copy def Memento(obj, deep=False): state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__) 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)
20. Observer(觀察者)
意圖:
定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時, 所有依賴於它的物件都得到通知並被自動更新。
適用性:
當一個抽象模型有兩個方面, 其中一個方面依賴於另一方面。將這二者封裝在獨立的物件中以使它們可以各自獨立地改變和複用。
當對一個物件的改變需要同時改變其它物件, 而不知道具體有多少物件有待改變。
當一個物件必須通知其它物件,而它又不能假定其它物件是誰。換言之, 你不希望這些物件是緊密耦合的。
#!/usr/bin/python #coding:utf8 ''' 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()
21. State(狀態)
意圖:
允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類。
適用性:
一個物件的行為取決於它的狀態, 並且它必須在執行時刻根據狀態改變它的行為。
一個操作中含有龐大的多分支的條件語句,且這些分支依賴於該物件的狀態。這個狀態通常用一個或多個列舉常量表示。通常, 有多個操作包含這一相同的條件結構。State模式將每一個條件分支放入一個獨立的類中。這使得你可以根據物件自身的情況將物件的狀態作為一個物件,這一物件可以不依賴於其他物件而獨立變化。
#!/usr/bin/python #coding:utf8 ''' 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()
22. Strategy(策略)
意圖:
定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。
適用性:
許多相關的類僅僅是行為有異。“策略”提供了一種用多個行為中的一個行為來配置一個類的方法。
需要使用一個演算法的不同變體。例如,你可能會定義一些反映不同的空間/時間權衡的演算法。當這些變體實現為一個演算法的類層次時[H087] ,可以使用策略模式。
演算法使用客戶不應該知道的資料。可使用策略模式以避免暴露覆雜的、與演算法相關的資料結構。
一個類定義了多種行為, 並且這些行為在這個類的操作中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。
#!/usr/bin/python #coding:utf8 """ 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()
23. Visitor(訪問者)
意圖:
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
適用性:
一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實現。
各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。這是Opdyke和Johnson所描述過的“重分解以一般化”的一個很好的例子[OJ93]。首先識別現有程式碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個呼叫這些新的操作的模板方法來替換這些不同的程式碼。
控制子類擴充套件。模板方法只在特定點呼叫“hook ”操作(參見效果一節),這樣就只允許在這些點進行擴充套件。
#!/usr/bin/python #coding:utf8 ''' 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)