淺談設計模式及python實現

  • 2019 年 10 月 3 日
  • 筆記

設計模式及Python實現

 

設計模式是什麼?

Christopher Alexander:“每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而不必做重複勞動。”

設計模式是經過總結、優化的,對我們經常會碰到的一些編程問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣能夠直接作用於我們的程式碼。反之,設計模式更為高級,它是一種必須在特定情形下實現的一種方法模板。設計模式不會綁定具體的程式語言。一個好的設計模式應該能夠用大部分程式語言實現(如果做不到全部的話,具體取決於語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。

起初,你會認為“模式”就是為了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是通過很多人一起工作,從不同的角度看待問題進而形成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,但是你的解決方案很可能沒有模式這麼完備。

雖然被稱為“設計模式”,但是它們同“設計“領域並非緊密聯繫。設計模式同傳統意義上的分析、設計與實現不同,事實上設計模式將一個完整的理念根植於程式中,所以它可能出現在分析階段或是更高層的設計階段。很有趣的是因為設計模式的具體體現是程式程式碼,因此可能會讓你認為它不會在具體實現階段之前出現(事實上在進入具體實現階段之前你都沒有意識到正在使用具體的設計模式)。

可以通過程式設計的基本概念來理解模式:增加一個抽象層。抽象一個事物就是隔離任何具體細節,這麼做的目的是為了將那些不變的核心部分從其他細節中分離出來。當你發現你程式中的某些部分經常因為某些原因改動,而你不想讓這些改動的部分引發其他部分的改動,這時候你就需要思考那些不會變動的設計方法了。這麼做不僅會使程式碼可維護性更高,而且會讓程式碼更易於理解,從而降低開發成本。

三種最基本的設計模式:

  1. 創建模式,提供實例化的方法,為適合的狀況提供相應的對象創建方法。
  2. 結構化模式,通常用來處理實體之間的關係,使得這些實體能夠更好地協同工作。
  3. 行為模式,用於在不同的實體建進行通訊,為實體之間的通訊提供更容易,更靈活的通訊方法。

設計模式六大原則

  • 開閉原則:一個軟體實體如類、模組和函數應該對擴展開放,對修改關閉。即軟體實體應盡量在不修改原有程式碼的情況下進行擴展。
  • 里氏(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)

PaymentFactory簡單工廠

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)

1. 使用__new__方法

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)

2. 裝飾器方法

# 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))

3. import方法

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)

4. 使用metaclass

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()

模仿js事件處理

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實現》——李瓊羽

 

 
 
 

設計模式是什麼?

Christopher Alexander:“每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而不必做重複勞動。”

設計模式是經過總結、優化的,對我們經常會碰到的一些編程問題的可重用解決方案。一個設計模式並不像一個類或一個庫那樣能夠直接作用於我們的程式碼。反之,設計模式更為高級,它是一種必須在特定情形下實現的一種方法模板。設計模式不會綁定具體的程式語言。一個好的設計模式應該能夠用大部分程式語言實現(如果做不到全部的話,具體取決於語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。

起初,你會認為“模式”就是為了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是通過很多人一起工作,從不同的角度看待問題進而形成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,但是你的解決方案很可能沒有模式這麼完備。

雖然被稱為“設計模式”,但是它們同“設計“領域並非緊密聯繫。設計模式同傳統意義上的分析、設計與實現不同,事實上設計模式將一個完整的理念根植於程式中,所以它可能出現在分析階段或是更高層的設計階段。很有趣的是因為設計模式的具體體現是程式程式碼,因此可能會讓你認為它不會在具體實現階段之前出現(事實上在進入具體實現階段之前你都沒有意識到正在使用具體的設計模式)。

可以通過程式設計的基本概念來理解模式:增加一個抽象層。抽象一個事物就是隔離任何具體細節,這麼做的目的是為了將那些不變的核心部分從其他細節中分離出來。當你發現你程式中的某些部分經常因為某些原因改動,而你不想讓這些改動的部分引發其他部分的改動,這時候你就需要思考那些不會變動的設計方法了。這麼做不僅會使程式碼可維護性更高,而且會讓程式碼更易於理解,從而降低開發成本。

三種最基本的設計模式:

  1. 創建模式,提供實例化的方法,為適合的狀況提供相應的對象創建方法。
  2. 結構化模式,通常用來處理實體之間的關係,使得這些實體能夠更好地協同工作。
  3. 行為模式,用於在不同的實體建進行通訊,為實體之間的通訊提供更容易,更靈活的通訊方法。

設計模式六大原則

  • 開閉原則:一個軟體實體如類、模組和函數應該對擴展開放,對修改關閉。即軟體實體應盡量在不修改原有程式碼的情況下進行擴展。
  • 里氏(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)

PaymentFactory簡單工廠

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)

1. 使用__new__方法

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)

2. 裝飾器方法

# 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))

3. import方法

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)

4. 使用metaclass

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()

模仿js事件處理

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實現》——李瓊羽