設計模式及Python實現
設計模式是什麼?
Christopher Alexander:“每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而不必做重複勞動。”
設計模式是經過總結、優化的,對我們經常會碰到的一些程式設計問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣能夠直接作用於我們的程式碼。反之,設計模式更為高階,它是一種必須在特定情形下實現的一種方法模板。設計模式不會繫結具體的程式語言。一個好的設計模式應該能夠用大部分程式語言實現(如果做不到全部的話,具體取決於語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。
起初,你會認為“模式”就是為了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是通過很多人一起工作,從不同的角度看待問題進而形成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,但是你的解決方案很可能沒有模式這麼完備。
雖然被稱為“設計模式”,但是它們同“設計“領域並非緊密聯絡。設計模式同傳統意義上的分析、設計與實現不同,事實上設計模式將一個完整的理念根植於程式中,所以它可能出現在分析階段或是更高層的設計階段。很有趣的是因為設計模式的具體體現是程式程式碼,因此可能會讓你認為它不會在具體實現階段之前出現(事實上在進入具體實現階段之前你都沒有意識到正在使用具體的設計模式)。
可以通過程式設計的基本概念來理解模式:增加一個抽象層。抽象一個事物就是隔離任何具體細節,這麼做的目的是為了將那些不變的核心部分從其他細節中分離出來。當你發現你程式中的某些部分經常因為某些原因改動,而你不想讓這些改動的部分引發其他部分的改動,這時候你就需要思考那些不會變動的設計方法了。這麼做不僅會使程式碼可維護性更高,而且會讓程式碼更易於理解,從而降低開發成本。
三種最基本的設計模式:
- 建立模式,提供例項化的方法,為適合的狀況提供相應的物件建立方法。
- 結構化模式,通常用來處理實體之間的關係,使得這些實體能夠更好地協同工作。
- 行為模式,用於在不同的實體建進行通訊,為實體之間的通訊提供更容易,更靈活的通訊方法。
設計模式六大原則
- 開閉原則:一個軟體實體如類、模組和函式應該對擴充套件開放,對修改關閉。即軟體實體應儘量在不修改原有程式碼的情況下進行擴充套件。
- 里氏(Liskov)替換原則:所有引用基類(父類)的地方必須能透明地使用其子類的物件。
- 依賴倒置原則:高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。換言之,要針對介面程式設計,而不是針對實現程式設計。
- 介面隔離原則:使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。
- 迪米特法則:一個軟體實體應當儘可能少地與其他實體發生相互作用。
- 單一職責原則:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。
介面
介面:一種特殊的類,宣告瞭若干方法,要求繼承該介面的類必須實現這些方法。
作用:限制繼承介面的類的方法的名稱及呼叫方式;隱藏了類的內部實現。
介面就是一種抽象的基類(父類),限制繼承它的類必須實現介面中定義的某些方法。
Python中使用ABCMeta、abstractmethod的抽象類、抽象方法來實現介面的功能。介面類定義方法,不具體實現,限制子類必須有該方法。在介面子類中實現具體的功能。
# 通過抽象類和抽象方法,做抽象用 from abc import ABCMeta from abc import abstractmethod # 匯入抽象方法 class Father(metaclass=ABCMeta): # 建立抽象類 @abstractmethod def f1(self): pass @abstractmethod def f2(self): pass class F1(Father): def f1(self): pass def f2(self): pass def f3(self): pass obj = F1()
class Interface: def method(self, arg): raise NotImplementedError
建立型模式
1. 簡單工廠模式
內容:不直接向客戶端暴露物件建立的實現細節,而是通過一個工廠類來負責建立產品類的例項。
角色:
- 工廠角色(Creator)
- 抽象產品角色(Product)
- 具體產品角色(Concrete Product)
優點:
- 隱藏了物件建立的實現細節
- 客戶端不需要修改程式碼
缺點:
- 違反了單一職責原則,將建立邏輯幾種到一個工廠類裡
- 當新增新產品時,需要修改工廠類程式碼,違反了開閉原則
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class Alipay(Payment): def __init__(self, enable_yuebao=False): self.enable_yuebao = enable_yuebao def pay(self, money): if self.enable_yuebao: print("餘額寶支付%s元" % money) else: print("支付寶支付%s元" % money) class ApplePay(Payment): def pay(self, money): print("蘋果支付%s元" % money) class PaymentFactory: def create_payment(self, method): if method == "alipay": return Alipay() elif method == 'yuebao': return Alipay(enable_yuebao=True) elif method == "applepay": return ApplePay() else: raise NameError(method) f = PaymentFactory() p = f.create_payment("yuebao") p.pay(100)
2. 工廠方法模式(Factory Method)
內容:定義一個用於建立物件的介面(工廠介面),讓子類決定例項化哪一個產品類。
角色:
- 抽象工廠角色(Creator)
- 具體工廠角色(Concrete Creator)
- 抽象產品角色(Product)
- 具體產品角色(Concrete Product)
工廠方法模式相比簡單工廠模式將每個具體產品都對應了一個具體工廠。
適用場景:
- 需要生產多種、大量複雜物件的時候。
- 需要降低耦合度的時候。
- 當系統中的產品種類需要經常擴充套件的時候。
優點:
- 每個具體產品都對應一個具體工廠類,不需要修改工廠類程式碼
- 隱藏了物件建立的實現細節
缺點:
- 每增加一個具體產品類,就必須增加一個相應的具體工廠類
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class Alipay(Payment): def pay(self, money): print("支付寶支付%s元" % money) class ApplePay(Payment): def pay(self, money): print("蘋果支付%s元" % money) class PaymentFactory(metaclass=ABCMeta): @abstractmethod def create_payment(self): pass class AlipayFactory(PaymentFactory): def create_payment(self): return Alipay() class ApplePayFactory(PaymentFactory): def create_payment(self): return ApplePay() af = AlipayFactory() ali = af.create_payment() ali.pay(120)
3. 抽象工廠方法(Abstract Factory)
內容:定義一個工廠類介面,讓工廠子類來建立一系列相關或相互依賴的物件。
例:生產一部手機,需要手機殼、CPU、作業系統三類物件進行組裝,其中每類物件都有不同的種類。對每個具體工廠,分別生產一部手機所需要的三個物件。
角色:
- 抽象工廠角色(Creator)
- 具體工廠角色(Concrete Creator)
- 抽象產品角色(Product)
- 具體產品角色(Concrete Product)
- 客戶端(Client)
相比工廠方法模式,抽象工廠模式中的每個具體工廠都生產一套產品。
適用場景:
- 系統要獨立於產品的建立與組合時
- 強調一系列相關的產品物件的設計以便進行聯合使用時
- 提供一個產品類庫,想隱藏產品的具體實現時
優點:
- 將客戶端與類的具體實現相分離
- 每個工廠建立了一個完整的產品系列,使得易於交換產品系列
- 有利於產品的一致性(即產品之間的約束關係)
缺點:
- 難以支援新種類的(抽象)產品
from abc import abstractmethod, ABCMeta # ------抽象產品------ class PhoneShell(metaclass=ABCMeta): @abstractmethod def show_shell(self): pass class CPU(metaclass=ABCMeta): @abstractmethod def show_cpu(self): pass class OS(metaclass=ABCMeta): @abstractmethod def show_os(self): pass # ------抽象工廠------ class PhoneFactory(metaclass=ABCMeta): @abstractmethod def make_shell(self): pass @abstractmethod def make_cpu(self): pass @abstractmethod def make_os(self): pass # ------具體產品------ class SmallShell(PhoneShell): def show_shell(self): print("普通手機小手機殼") class BigShell(PhoneShell): def show_shell(self): print("普通手機大手機殼") class AppleShell(PhoneShell): def show_shell(self): print("蘋果手機殼") class SnapDragonCPU(CPU): def show_cpu(self): print("驍龍CPU") class MediaTekCPU(CPU): def show_cpu(self): print("聯發科CPU") class AppleCPU(CPU): def show_cpu(self): print("蘋果CPU") class Android(OS): def show_os(self): print("Android系統") class IOS(OS): def show_os(self): print("iOS系統") # ------具體工廠------ class MiFactory(PhoneFactory): def make_cpu(self): return SnapDragonCPU() def make_os(self): return Android() def make_shell(self): return BigShell() class HuaweiFactory(PhoneFactory): def make_cpu(self): return MediaTekCPU() def make_os(self): return Android() def make_shell(self): return SmallShell() class IPhoneFactory(PhoneFactory): def make_cpu(self): return AppleCPU() def make_os(self): return IOS() def make_shell(self): return AppleShell() # ------客戶端------ class Phone: def __init__(self, cpu, os, shell): self.cpu = cpu self.os = os self.shell = shell def show_info(self): print("手機資訊:") self.cpu.show_cpu() self.os.show_os() self.shell.show_shell() def make_phone(factory): cpu = factory.make_cpu() os = factory.make_os() shell = factory.make_shell() return Phone(cpu, os, shell) p1 = make_phone(HuaweiFactory()) p1.show_info()
4. 建造者模式(Builder)
內容:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
角色:
- 抽象建造者(Builder)
- 具體建造者(Concrete Builder)
- 指揮者(Director)
- 產品(Product)
建造者模式與抽象工廠模式相似,也用來建立複雜物件。主要區別是建造者模式著重一步步構造一個複雜物件,而抽象工廠模式著重於多個系列的產品物件。
適用場景:
- 當建立複雜物件的演算法(Director)應該獨立於該物件的組成部分以及它們的裝配方式(Builder)時
- 當構造過程允許被構造的物件有不同的表示時(不同Builder)。
優點:
- 隱藏了一個產品的內部結構和裝配過程
- 將構造程式碼與表示程式碼分開
- 可以對構造過程進行更精細的控制
from abc import abstractmethod, ABCMeta # ------產品------ class Player: def __init__(self, face=None, body=None, arm=None, leg=None): self.face = face self.arm = arm self.leg = leg self.body = body def __str__(self): return "%s, %s, %s, %s" % (self.face, self.arm, self.body, self.leg) # ------建造者------ class PlayerBuilder(metaclass=ABCMeta): @abstractmethod def build_face(self): pass @abstractmethod def build_arm(self): pass @abstractmethod def build_leg(self): pass @abstractmethod def build_body(self): pass @abstractmethod def get_player(self): pass class BeautifulWomanBuilder(PlayerBuilder): def __init__(self): self.player = Player() def build_face(self): self.player.face = "漂亮臉蛋" def build_arm(self): self.player.arm = "細胳膊" def build_body(self): self.player.body = "細腰" def build_leg(self): self.player.leg = "長腿" def get_player(self): return self.player class PlayerDirector: def build_player(self, builder): builder.build_body() builder.build_arm() builder.build_leg() builder.build_face() return builder.get_player() director = PlayerDirector() builder = BeautifulWomanBuilder() p = director.build_player(builder) print(p)
5. 單例模式(Singleton)
內容:保證一個類只有一個例項,並提供一個訪問它的全域性訪問點。
角色:
- 單例(Singleton)
適用場景
- 當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時
優點:
- 對唯一例項的受控訪問
- 單例相當於全域性變數,但防止了名稱空間被汙染。
與單例模式功能相似的概念:全域性變數、靜態變數(方法)
實現:
class Singleton(object): def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, ) return cls._instance class MyClass(Singleton): a = 1 def __init__(self, name): self.name = name one = MyClass('egon') two = MyClass('alex') print(id(one)) print(id(two)) print(one == two) print(one is two)
def singleton(cls, *args, **kw): instances = {} def get_instance(): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return get_instance @singleton class MyClass2: a = 1 one = MyClass2() two = MyClass2() print(id(one)) # 31495472 print(id(two)) # 31495472 print(one == two) print(one is two)
# Python的模組是天然的單例模式。 # module_name.py class MySingleton(object): def foo(self): print('danli') my_singleton = MySingleton() # to use from .module_name import my_singleton my_singleton.foo() print(id(my_singleton)) from .module_name import my_singleton my_singleton.foo() print(id(my_singleton))
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] # Python2 # class MyClass: # __metaclass__ = Singleton # Python3 class MyClass(metaclass=Singleton): pass one = MyClass() two = MyClass() print(id(one)) print(id(two)) print(one == two) print(one is two)
6. 原型模式(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()
建立型模式總結
使用抽象工廠(Abstract Factory)、原型(Prototype)或者建造者(Builder)的設計甚至比工廠方法(Factory Method)的那些設計更靈活,但它們也更加複雜。通常,設計以使用工廠方法(Factory Method)開始。並且當設計者發現需要更大的靈活性時,設計便會想其他建立模式煙花。當你在設計標準之間權衡的時候,瞭解多個模式可以給你提供給更多的選擇餘地。
依賴於繼承的建立型模式:工廠方法模式
依賴於組合的建立型模式:抽象工廠模式、建立者模式
結構性模式
1. 介面卡模式(Adapter Class/Object)
內容:將一個類的介面轉換成客戶希望的另一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
角色:
- 目標介面(Target)
- 待適配的類(Adaptee)
- 介面卡(Adapter)
兩種實現方式:
- 類介面卡:使用多繼承
- 物件介面卡:使用組合
適用場景:
- 想使用一個已經存在的類,而它的介面不符合你的要求
- (物件介面卡)想使用一些已經存在的子類,但不可能對每一個都進行子類化以匹配它們的介面。物件介面卡可以適配它的父類介面。
類介面卡:
- 用一個具體的Adapter類對Adaptee和Target進行匹配。結果是當我們想要匹配一個類以及所有他的子類時,類Adaptee將不能勝任工作。
- 使得Adapter可以重定義Adaptee的部分行為,因為Adapter是Adaptee的一個子類。
- 僅僅引入一個物件,並不需要額外的指標以間接得到Adaptee。
物件介面卡:
- 允許一個Adapter與多個Adaptee——即Adaptee本身以及它所有的子類(如果有子類的話)一同時工作。Adapter也可以一次給所有的Adaptee新增功能。
- 使得重定義Adaptee的行為比較困難。這酒需要生成Adaptee的子類並且使得Adapter引用這個子類而不是引用Adaptee本身。
from abc import abstractmethod, ABCMeta class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): raise NotImplementedError class Alipay(Payment): def pay(self, money): print("支付寶支付%s元" % money) class ApplePay(Payment): def pay(self, money): print("蘋果支付%s元" % money) # ------待適配類------ class WechatPay: def cost(self, money): print("微信支付%s元" % money) # 類介面卡 class RealWechatPay(WechatPay, Payment): def pay(self, money): return self.cost(money) # 物件介面卡 class RealWechatPay2(Payment): def __init__(self): self.payment = WechatPay() def pay(self, money): return self.payment.cost(money) p = RealWechatPay2() p.pay(111)
2. 組合模式(Composite)
內容:將物件組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性。
角色:
- 抽象元件(Component)
- 葉子元件(Leaf)
- 複合元件(Composite)
- 客戶端(Client)
適用場景:
- 表示物件的“部分-整體”層次結構(特別是結構是遞迴的)
- 希望使用者忽略組合物件與單個物件的不同,使用者統一地使用組合結構中的所有物件
優點:
- 定義了包含基本物件和組合物件的類層次結構
- 簡化客戶端程式碼,即客戶端可以一致地使用組合物件和單個物件
- 更容易增加新型別的元件
缺點:
- 很難限制組合中的元件
from abc import abstractmethod, ABCMeta class Graphic(metaclass=ABCMeta): @abstractmethod def draw(self): pass @abstractmethod def add(self, graphic): pass def getchildren(self): pass # 圖元 class Point(Graphic): def __init__(self, x, y): self.x = x self.y = y def draw(self): print(self) def add(self, graphic): raise TypeError def getchildren(self): raise TypeError def __str__(self): return "點(%s, %s)" % (self.x, self.y) class Line(Graphic): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def draw(self): print(self) def add(self, graphic): raise TypeError def getchildren(self): raise TypeError def __str__(self): return "線段[%s, %s]" % (self.p1, self.p2) class Picture(Graphic): def __init__(self): self.children = [] def add(self, graphic): self.children.append(graphic) def getchildren(self): return self.children def draw(self): print("------複合圖形------") for g in self.children: g.draw() print("------END------") pic1 = Picture() point = Point(2,3) pic1.add(point) pic1.add(Line(Point(1,2), Point(4,5))) pic1.add(Line(Point(0,1), Point(2,1))) pic2 = Picture() pic2.add(Point(-2,-1)) pic2.add(Line(Point(0,0), Point(1,1))) pic = Picture() pic.add(pic1) pic.add(pic2) pic.draw() #pic1.draw() #point.draw()
3. 代理模式 (Proxy)
內容:為其他物件提供一種代理以控制對這個物件的訪問。
角色:
- 抽象實體(Subject)
- 實體(RealSubject)
- 代理(Proxy)
適用場景:
- 遠端代理:為遠端的物件提供代理
- 虛代理:根據需要建立很大的物件
- 保護代理:控制對原始物件的訪問,用於物件有不同訪問許可權時
優點:
- 遠端代理:可以隱藏物件位於遠端地址空間的事實
- 虛代理:可以進行優化,例如根據要求建立物件
- 保護代理:允許在訪問一個物件時有一些附加的內務處理
from abc import ABCMeta, abstractmethod class Subject(metaclass=ABCMeta): @abstractmethod def get_content(self): pass def set_content(self, content): pass class RealSubject(Subject): def __init__(self, filename): self.filename = filename print("讀取%s檔案內容" % filename) f = open(filename) self.__content = f.read() f.close() def get_content(self): return self.__content def set_content(self, content): f = open(self.filename, 'w') f.write(content) self.__content = content f.close() # ---遠端代理 class ProxyA(Subject): def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): return self.subj.get_content() def set_content(self, content): return self.subj.set_content(content) # ---虛代理 class ProxyB(Subject): def __init__(self, filename): self.filename = filename self.subj = None def get_content(self): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.get_content() x = ProxyB('abc.txt') # print(x.get_content()) # ---保護代理 class ProxyC(Subject): def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): self.subj.get_content() def set_content(self, content): raise PermissionError # filename = "abc.txt" # username = input() # if username!="alex": # p = ProxyC(filename) # else: # p = ProxyA(filename) # # print(p.get_content())
行為模式
1. 責任鏈模式(Chain of Responsibility)
內容:使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
角色:
- 抽象處理者(Handler)
- 具體處理者(ConcreteHandler)
- 客戶端(Client)
例:
- 請假部門批准:leader—>部門經理—>總經理
- Javascript事件浮升機制
適用場景:
- 有多個物件可以處理一個請求,哪個物件處理由執行時決定
- 在不明確接收者的情況下,向多個物件中的一個提交一個請求
優點:
- 降低耦合度:一個物件無需知道是其他哪一個物件處理其請求
缺點:
- 請求不保證被接收:鏈的末端沒有處理或鏈配置錯誤
from abc import ABCMeta, abstractmethod class Handler(metaclass=ABCMeta): @abstractmethod def handle_leave(self, day): pass class GeneralManagerHandler(Handler): def handle_leave(self, day): if day < 10: print("總經理批准%d天假" % day) return True else: print("呵呵") return False class DepartmentManagerHandler(Handler): def __init__(self): self.successor = GeneralManagerHandler() def handle_leave(self, day): if day < 7: print("部門經理批准%d天假" % day) return True else: print("部門經理無權准假") return self.successor.handle_leave(day) class ProjectDirectorHandler(Handler): def __init__(self): self.successor = DepartmentManagerHandler() def handle_leave(self, day): if day < 3: print("專案主管批准%d天假" % day) return True else: print("專案主管無權准假") return self.successor.handle_leave(day) day = 11 h = ProjectDirectorHandler() print(h.handle_leave(day))
# --高階例子--模仿js事件處理 from abc import ABCMeta, abstractmethod class Handler(metaclass=ABCMeta): @abstractmethod def add_event(self, func): pass @abstractmethod def handle(self): pass class BodyHandler(Handler): def __init__(self): self.func = None def add_event(self, func): self.func = func def handle(self): if self.func: return self.func() else: print("已到最後一級,無法處理") class ElementHandler(Handler): def __init__(self, successor): self.func = None self.successor = successor def add_event(self, func): self.func = func def handle(self): if self.func: return self.func() else: return self.successor.handle() # 客戶端 # <body><div><a> body = {'type': 'body', 'name': 'body', 'children': [], 'father': None} div = {'type': 'div', 'name': 'div', 'children': [], 'father': body} a = {'type': 'a', 'name': 'a', 'children': [], 'father': div} body['children'].append(div) div['children'].append(a) # print(body) body['event_handler'] = BodyHandler() div['event_handler'] = ElementHandler(div['father']['event_handler']) a['event_handler'] = ElementHandler(a['father']['event_handler']) def attach_event(element, func): element['event_handler'].add_event(func) # test def func_a(): print("這是給a的函式") def func_div(): print("這是給div的函式") def func_body(): print("這是給body的函式") attach_event(a, func_a) attach_event(div, func_div) attach_event(body, func_body) a['event_handler'].handle()
2. 迭代器模式(Iterator)
內容:提供一種方法順序訪問一個聚合物件中的各個元素,而又不需要暴露該物件的內部表示。
適用場景:
- 訪問一個聚合物件的內容而無需暴露它的內部表示。
- 支援對聚合物件的多種遍歷。
- 為遍歷不同的聚合結構提供一個統一的介面(即, 支援多型迭代)
實現方法:__iter__、__next__
class LinkList: """連結串列 頭結點儲存連結串列的長度""" class Node: def __init__(self, item=None): self.item = item self.next = None class LinkListIterator: def __init__(self, node): self.node = node def __next__(self): if self.node: cur_node = self.node self.node = cur_node.next return cur_node.item else: raise StopIteration def __iter__(self): return self def __init__(self, iterable=None): self.head = LinkList.Node(0) self.tail = self.head self.extend(iterable) def append(self, obj): s = LinkList.Node(obj) self.tail.next = s self.tail = s self.head.item += 1 def extend(self, iterable): for obj in iterable: self.append(obj) def __iter__(self): return self.LinkListIterator(self.head.next) def __len__(self): return self.head.item def __str__(self): return "<<" + ", ".join(map(str, self)) + ">>" li = [i for i in range(100)] lk = LinkList(li) print(lk)
3. 觀察者模式(Observer)
內容:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時, 所有依賴於它的物件都得到通知並被自動更新。觀察者模式又稱“釋出-訂閱”模式
角色:
- 抽象主題(Subject)
- 具體主題(ConcreteSubject)——釋出者
- 抽象觀察者(Observer)
- 具體觀察者(ConcreteObserver)——訂閱者
適用場景:
- 當一個抽象模型有兩方面,其中一個方面依賴於另一個方面。將這兩者封裝在獨立物件中以使它們可以各自獨立地改變和複用。
- 當對一個物件的改變需要同時改變其它物件,而不知道具體有多少物件有待改變。
- 當一個物件必須通知其它物件,而它又不能假定其它物件是誰。換言之,你不希望這些物件是緊密耦合的。
優點:
- 目標和觀察者之間的抽象耦合最小
- 支援廣播通訊
缺點:
- 多個觀察者之間互不知道對方存在,因此一個觀察者對主題的修改可能造成錯誤的更新。
from abc import ABCMeta, abstractmethod class Observer(metaclass=ABCMeta): @abstractmethod def update(self, notice): pass class Notice: def __init__(self): self.observers = [] def attach(self, obs): self.observers.append(obs) def detach(self, obs): self.observers.remove(obs) # obs.company_info=None def notify(self): for obj in self.observers: obj.update(self) class ManagerNotice(Notice): def __init__(self, company_info=None): super().__init__() self.__company_info = company_info def detach(self, obs): super().detach(obs) obs.company_info = None @property def company_info(self): return self.__company_info @company_info.setter def company_info(self, info): self.__company_info = info self.notify() class Manager(Observer): def __init__(self): self.company_info = None def update(self, noti): self.company_info = noti.company_info notice = ManagerNotice() alex = Manager() wusir = Manager() print(alex.company_info) print(wusir.company_info) notice.attach(alex) notice.attach(wusir) notice.company_info = "公司執行良好" print(alex.company_info) print(wusir.company_info) notice.company_info = "公司將要上市" print(alex.company_info) print(wusir.company_info) notice.detach(wusir) notice.company_info = "公司要破產了,趕快跑路" print(alex.company_info) print(wusir.company_info) notice.company_info = "公司已經破產了" print(alex.company_info) print(wusir.company_info)
4. 策略模式(Strategy)
內容:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。
角色:
- 抽象策略(Strategy)
- 具體策略(ConcreteStrategy)
- 上下文(Context)
適用場景:
- 許多相關的類僅僅是行為有異
- 需要使用一個演算法的不同變體
- 演算法使用了客戶端無需知道的資料
- 一個類中的多種行為以多個條件語句的形式存在,可以將這些行為封裝如不同的策略類中。
優點:
- 定義了一系列可重用的演算法和行為
- 消除了一些條件語句
- 可以提供相同行為的不同實現
缺點:
- 客戶必須瞭解不同的策略
- 策略與上下文之間的通訊開銷
- 增加了物件的數目
from abc import ABCMeta, abstractmethod import random class Sort(metaclass=ABCMeta): @abstractmethod def sort(self, data): pass class QuickSort(Sort): def quick_sort(self, data, left, right): if left < right: mid = self.partition(data, left, right) self.quick_sort(data, left, mid - 1) self.quick_sort(data, mid + 1, right) def partition(self, data, left, right): tmp = data[left] while left < right: while left < right and data[right] >= tmp: right -= 1 data[left] = data[right] while left < right and data[left] <= tmp: left += 1 data[right] = data[left] data[left] = tmp return left def sort(self, data): print("快速排序") return self.quick_sort(data, 0, len(data) - 1) class MergeSort(Sort): def merge(self, data, low, mid, high): i = low j = mid + 1 ltmp = [] while i <= mid and j <= high: if data[i] <= data[j]: ltmp.append(data[i]) i += 1 else: ltmp.append(data[j]) j += 1 while i <= mid: ltmp.append(data[i]) i += 1 while j <= high: ltmp.append(data[j]) j += 1 data[low:high + 1] = ltmp def merge_sort(self, data, low, high): if low < high: mid = (low + high) // 2 self.merge_sort(data, low, mid) self.merge_sort(data, mid + 1, high) self.merge(data, low, mid, high) def sort(self, data): print("歸併排序") return self.merge_sort(data, 0, len(data) - 1) class Context: def __init__(self, data, strategy=None): self.data = data self.strategy = strategy def set_strategy(self, strategy): self.strategy = strategy def do_strategy(self): if self.strategy: self.strategy.sort(self.data) else: raise TypeError li = list(range(100000)) random.shuffle(li) context = Context(li, MergeSort()) context.do_strategy() # print(context.data) random.shuffle(context.data) context.set_strategy(QuickSort()) context.do_strategy()
5. 模板方法(Template Method)
內容:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
角色:
- 抽象類(AbstractClass):定義抽象的原子操作(鉤子操作);實現一個模板方法作為演算法的骨架。
- 具體類(ConcreteClass):實現原子操作
適用場景:
- 一次性實現一個演算法的不變的部分
- 各個子類中的公共行為應該被提取出來並集中到一個公共父類中以避免程式碼重複
- 控制子類擴充套件
from abc import ABCMeta, abstractmethod class IOHandler(metaclass=ABCMeta): @abstractmethod def open(self, name): pass @abstractmethod def deal(self, change): pass @abstractmethod def close(self): pass def process(self, name, change): self.open(name) self.deal(change) self.close() class FileHandler(IOHandler): def open(self, name): self.file = open(name, "w") def deal(self, change): self.file.write(change) def close(self): self.file.close() f = FileHandler() f.process("abc.txt", "Hello World")
參考文獻:
《二十三種設計模式及其python實現》——李瓊羽