設計模式及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)PaymentFactory簡單工廠
2. 工廠方法模式
內容:定義一個用於創建對象的接口(工廠接口),讓子類決定實例化哪一個產品類。
角色:
- 抽象工廠角色(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. 抽象工廠方法
內容:定義一個工廠類接口,讓工廠子類來創建一系列相關或相互依賴的對象。
例:生產一部手機,需要手機殼、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()抽象工廠
設計模式及Python實現