設計模式是對軟體設計中普遍存在(反覆出現)的各種問題,所提出的解決方案。 ...
開發|設計模式
設計模式是對軟體設計中普遍存在(反覆出現)的各種問題,所提出的解決方案。
前言
目前所有的文章思想格式都是:知識+情感。
知識:對於所有的知識點的描述。力求不含任何的自我感情色彩。
情感:用我自己的方式,解讀知識點。力求通俗易懂,完美透析知識。
正文
設計模式有三大類:創建型模式、結構型模式、行為型模式。在進入設計模式之前,需要瞭解面向對象是什麼,介面是什麼,面向對象設計SOLID原則是什麼。
解釋:全文將先從三個問題入手,解決知識儲備問題。進而開始正式設計模式的三大類講解,內容量較大。示例代碼直接可以運行。
知識儲備
1.面向對象是什麼?
基本概念
面向對象的三大特性:封裝、繼承、多態。
封裝:
【封裝】
隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
【好處】
a.將變化隔離;
b.便於使用;
c.提高復用性;
d.提高安全性;
【封裝原則】
a.將不需要對外提供的內容都隱藏起來;
b.把屬性都隱藏,提供公共方法對其訪問。
繼承:
繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類。
多態:
多態指的是一類事物有多種形態。python的設計本身就是多態的語言。多態不是語法,是一種設計思想。
註意:三大特性的順序,儘量不要說錯,是有層級關係的。
2.介面是什麼?
基本概念
介面:若幹抽象方法的集合
作用:限制實現介面的類必須按照介面給定的調用方式實現這些方法;對高層模塊隱藏了類的內部實現。
解釋:介面,就是高層,也就是client的調用方式,必須和介面匹配。甚至client不需要關心介面是什麼實現的,只需要看介面就知道怎麼調用。
python實現介面
使用abstractmethod的抽象類方法實現介面。其中pay為介面,所有繼承Payment的類都需要實現該方法,不然那就會報錯,TypeError。
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
# ABCMeta == abstract class Meta
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
pass
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元." % money)
3.面向對象設計SOLID原則是什麼?
基本概念
S.O.L.I.D設計原則是由Robert C. Martin 在一篇文章中提出的"first five principles" 而來。
Single-responsibility principle :單一職責原則
Open-closed Principle:開閉原則
Liskov substitution principle:里式替換原則
Interface segregation principle:介面隔離原則
Dependency Inversion principle:依賴倒置原則
單一職責原則
英文:
The single responsibility principle revolves around the claim that a certain code module (most often, a class) should only have responsibility over one part of the functionality provided by the software.
中文:
描述:一個類只有一個變化因數,即一個類只有一個職責
解釋:
問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改 類T時,有可能會導致原本運行正常的職責P2功能發生故障。
解決方案:遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成職責P2功能。 這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。
開閉原則
英文:
This principle states: “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
中文:
描述:軟體實體(類,模塊,方法等)應該對擴展開放,對修改封閉
解釋:
問題由來:在軟體的生命周期內,因為變化、升級和維護等原因需要對軟體原有代碼進行修改時, 可能會給舊代碼中引入錯誤,也可能會使我們不得不對整個功能進行重構,並且需要原有代碼經過 重新測試。
解決方案:當軟體需要變化時,儘量通過擴展軟體實體的行為來實現變化,而不是通過修改已有的 代碼來實現變化。
里式替換原則
英文:
Liskov Substitution Principle states the following: “in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)”.
中文:
描述:如果類型S是類型T的子類型,那麼T的對象可以被S的對象所替換並且不會引發程式行為的變化 ,即1.子類可以完全替代父類。2.子類可以擴展父類的功能,但不能改變父類原有的功能。
解釋:
問題由來:有一功能P1,由類A完成。現需要將功能P1進行擴展,擴展後的功能為P,其中P由原有 功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會 導致原有功能P1發生故障。
解決方案:當使用繼承時,遵循里氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外 ,儘量不要重寫父類A的方法,也儘量不要重載父類A的方法。
介面隔離原則
英文:
This principle states that “Clients should not be forced to depend on methods that they do not use”.
中文:
描述:客戶端不應該依賴它不需要的介面,一個類對另一個類的依賴應該建立在最小的介面上。
解釋:
問題由來:類A通過介面I依賴類B,類C通過介面I依賴類D,如果介面I對於類B和類D來說不是最小 介面,則類B和類D必須去實現他們不需要的方法。
解決方案:將臃腫的介面I拆分為獨立的幾個介面,類A和類C分別與他們需要的介面建立依賴關係 。也就是採用介面隔離原則。
依賴倒置原則
英文:
The principle states:“A. High-level modules should not depend on low-level modules. Both shoulddepend on abstractions.B. Abstractions should not depend on details.Details should depend on abstractions”.
中文:
描述:1.高層的模塊不依賴底層的模塊,二者都應該依賴其抽象 2.抽象不依賴細節,細節依賴 抽象
解釋:
問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來達成。這種 場景下,類A一般是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作; 假如修改類A,會給程式帶來不必要的風險。
解決方案:將類A修改為依賴介面I,類B和類C各自實現介面I,類A通過介面I間接與類B或者類 C發生聯繫,則會大大降低修改類A的幾率。
知識正文
基本概念
設計模式分為三大類:
創建型模式:
工廠方法模式、抽象工廠模式、創建者模式、原型模式、單例模式
結構型模式:
適配器模式、橋模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式
行為型模式:
解釋器模式、責任鏈模式、命令模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、策略模式、訪問者模式、模板方法模式
創建型模式
簡單工廠模式
內容:不直接向客戶端暴露對象創建的實現細節,而是通過一個工廠類來負責創建產品類的實例。
角色:
工廠角色(Creator)
抽象產品角色(Product)
具體產品角色(Concrete Product)
優點:
隱藏了對象創建的實現細節
客戶端(上層調用者)不需要修改代碼
缺點:
違反了單一職責原則,將創建邏輯集中到一個工廠類里
當添加新產品時,需要修改工廠類代碼,違反了開閉原則
解釋:簡單工廠模式,首先實例化工廠,工廠內部對於對象進行分發調用,但是對於其他的調用都集中在一個工廠類中。
代碼實現:
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def __init__(self, use_huabei=False):
self.use_huaei = use_huabei
def pay(self, money):
if self.use_huaei:
print("花唄支付%d元." % money)
else:
print("支付寶餘額支付%d元." % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元." % money)
class PaymentFactory:
def create_payment(self, method):
if method == 'alipay':
return Alipay()
elif method == 'wechat':
return WechatPay()
elif method == 'huabei':
return Alipay(use_huabei=True)
else:
raise TypeError("No such payment named %s" % method)
# client
# 表示的是上層調用者,也叫做客戶端
pf = PaymentFactory()
p = pf.create_payment('huabei')
p.pay(100)
工廠方法模式
內容:定義一個用於創建對象的介面(工廠介面),讓子類決定實例化哪一個產品類。
角色:
抽象工廠角色(Creator)
具體工廠角色(Concrete Creator)
抽象產品角色(Product)
具體產品角色(Concrete Product)
優點:
每個具體產品都對應一個具體工廠類,不需要修改工廠類代碼
隱藏了對象創建的實現細節
缺點:
每增加一個具體產品類,就必須增加一個相應的具體工廠類
解釋:在簡單工廠模式中,進一步將工廠類進行拆分,使用一個類調用一個產品,並且使用介面進行約束。當新增加一個產品類的時候,需要增加一個工廠類與之相對應。
代碼實現:
from abc import ABCMeta, abstractmethod
# 產品類
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def __init__(self, use_huabei=False):
self.use_huaei = use_huabei
def pay(self, money):
if self.use_huaei:
print("花唄支付%d元." % money)
else:
print("支付寶餘額支付%d元." % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元." % money)
class BankPay(Payment):
def pay(self, money):
print("銀行卡支付%d元." % money)
# 工廠類
class PaymentFactory(metaclass=ABCMeta):
@abstractmethod
def create_payment(self):
pass
class AlipayFactory(PaymentFactory):
def create_payment(self):
return Alipay()
class WechatPayFactory(PaymentFactory):
def create_payment(self):
return WechatPay()
class HuabeiFactory(PaymentFactory):
def create_payment(self):
return Alipay(use_huabei=True)
class BankPayFactory(PaymentFactory):
def create_payment(self):
return BankPay()
# client
# 調用者
pf = HuabeiFactory()
p = pf.create_payment()
p.pay(100) # 花唄支付100元.
pf = BankPayFactory()
p = pf.create_payment()
p.pay(999) # 銀行卡支付999元.
抽象工廠模式
內容:定義一個工廠類介面,讓工廠子類來創建一系列相關或相互依賴的對象。
例:生產一部手機,需要手機殼、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 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 PhoneFactory(metaclass=ABCMeta):
@abstractmethod
def make_shell(self):
pass
@abstractmethod
def make_cpu(self):
pass
@abstractmethod
def make_os(self):
pass
# ------具體工廠------
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(IPhoneFactory())
p1.show_info()
# 手機信息:
# 蘋果CPU
# iOS系統
# 蘋果手機殼
p1 = make_phone(HuaweiFactory())
p1.show_info()
# 手機信息:
# 聯發科CPU
# Android系統
# 普通手機小手機殼
建造者模式
內容:將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
角色:
抽象建造者(Builder)
具體建造者(Concrete Builder)
指揮者(Director)
產品(Product)
對比:建造者模式與抽象工廠模式相似,也用來創建複雜對象。主要區別是建造者模式著重一步步構造一個複雜對象,而抽象工廠模式著重於多個系列的產品對象。
優點:
隱藏了一個產品的內部結構和裝配過程
將構造代碼與表示代碼分開
可以對構造過程進行更精細的控制
代碼實現:
from abc import ABCMeta, abstractmethod
# 產品
class Player:
def __init__(self, face=None, body=None, arm=None, leg=None):
self.face = face
self.body = body
self.arm = arm
self.leg = leg
def __str__(self):
return "%s, %s, %s, %s" % (self.face, self.body, self.arm, self.leg)
# 抽象建造者
class PlayerBuilder(metaclass=ABCMeta):
@abstractmethod
def build_face(self):
pass
@abstractmethod
def build_body(self):
pass
@abstractmethod
def build_arm(self):
pass
@abstractmethod
def build_leg(self):
pass
# 具體建造者
class SexyGirlBuilder(PlayerBuilder):
def __init__(self):
self.player = Player()
def build_face(self):
self.player.face = "漂亮臉蛋"
def build_body(self):
self.player.body = "苗條"
def build_arm(self):
self.player.arm = "漂亮胳膊"
def build_leg(self):
self.player.leg = "大長腿"
class Monster(PlayerBuilder):
def __init__(self):
self.player = Player()
def build_face(self):
self.player.face = "怪獸臉"
def build_body(self):
self.player.body = "怪獸身材"
def build_arm(self):
self.player.arm = "長毛的胳膊"
def build_leg(self):
self.player.leg = "長毛的腿"
# 指揮者
# 控制組裝順序
class PlayerDirector:
def build_player(self, builder):
builder.build_body()
builder.build_face()
builder.build_arm()
builder.build_leg()
return builder.player
# client
# 上層調用者
builder = Monster()
director = PlayerDirector()
p = director.build_player(builder)
print(p) # 怪獸臉, 怪獸身材, 長毛的胳膊, 長毛的腿
builder = SexyGirlBuilder()
director = PlayerDirector()
p = director.build_player(builder)
print(p) # 漂亮臉蛋, 苗條, 漂亮胳膊, 大長腿
單例模式
內容:保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
角色:
單例(Singleton)
優點:
對唯一實例的受控訪問
單例相當於全局變數,但防止了命名空間被污染
應用場景:日誌類,資料庫的連接
實現方式一:
直接書寫一個模塊,在裡面書寫一個類,實例化一個對象,在其他的地方調用的時候,調用實例化的對象既可實現單例模式。
實現方式二:
使用new方法對實例化的對象進行判斷,只要是存在這個實例化對象的時候,就直接將它返回,第一次實例化的時候,直接繼承父類進行創建實例化對象,以後都是直接返回第一次創建的對象。並且,修改值的時候,後面的實例化新類修改值之後,之前的數值也會變化。
代碼實現:
class Singleton:
"""實現單例模式"""
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
class MyClass(Singleton):
def __init__(self, a):
self.a = a
a = MyClass(10)
b = MyClass(20)
print(a.a) # 20
print(b.a) # 20
print(id(a), id(b)) # 1861748392568 1861748392568
總結
抽象工廠模式和建造者模式相比於簡單工廠模式和工廠方法模式而言更靈活也更複雜。
通常情況下、設計以簡單工廠模式或工廠方法模式開始,當你發現設計需要更大的靈活性時,則像更複雜的設計模式演化。
結構型模式
適配器模式
內容:將一個類的介面轉換成客戶希望的另一個介面。適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
兩種實現方式:
類適配器:使用多繼承
對象適配器:使用組合
角色:
目標介面(Target)
待適配的類(Adaptee)
適配器(Adapter)
適用場景:
想使用一個已經存在的類,而它的介面不符合你的要求
(對象適配器)想使用一些已經存在的子類,但不可能對每一個都進行子類化以匹配它們的介面。對象適配器可以適配它的父類介面。
代碼實現:
'''類適配器'''
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def pay(self, money):
print("支付寶支付%d元." % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元." % money)
# cost支付模式
class Cost(metaclass=ABCMeta):
@abstractmethod
def cost(self, money):
pass
class BankPay(Cost):
def cost(self, money):
print("銀聯支付%d元." % money)
class ApplePay(Cost):
def cost(self, money):
print("蘋果支付%d元." % money)
# 類適配器
class NewBankPay(Payment, BankPay):
def pay(self, money):
self.cost(money)
p = NewBankPay()
p.pay(100) # 銀聯支付100元.
'''對象適配器'''
from abc import ABCMeta, abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def pay(self, money):
print("支付寶支付%d元." % money)
class WechatPay(Payment):
def pay(self, money):
print("微信支付%d元." % money)
# cost支付模式
class Cost(metaclass=ABCMeta):
@abstractmethod
def cost(self, money):
pass
class BankPay(Cost):
def cost(self, money):
print("銀聯支付%d元." % money)
class ApplePay(Cost):
def cost(self, money):
print("蘋果支付%d元." % money)
# 對象適配器
class PaymentAdapter(Payment):
def __init__(self, payment):
self.payment = payment
def pay(self, money):
self.payment.cost(money)
# 頂層調用
# 統一實現pay方法
p = PaymentAdapter(BankPay())
p.pay(100) # 銀聯支付100元.
p = WechatPay()
p.pay(100) # 微信支付100元.
橋模式
內容:
將一個事物的兩個維度分離,使其都可以獨立地變化。
角色:
抽象(Abstraction)
細化抽象(RefinedAbstraction)
實現者(Implementor)
具體實現者(ConcreteImplementor)
應用場景:
當事物有兩個維度上的表現,兩個維度都可能擴展時。
優點:
抽象和實現相分離
優秀的擴展能力
代碼實現:
對於形狀與顏色在二維上進行擴展,使用橋模式,相互組合實現。
形狀屬於抽象
顏色屬於實現者
from abc import ABCMeta, abstractmethod
# 抽象介面
class Shape(metaclass=ABCMeta):
def __init__(self, color):
self.color = color
@abstractmethod
def draw(self):
pass
# 實現者介面
class Color(metaclass=ABCMeta):
@abstractmethod
def paint(self, shape):
pass
# 細化抽象
class Rectangle(Shape):
name = "長方形"
def draw(self):
# 長方形邏輯
self.color.paint(self)
class Circle(Shape):
name = "圓形"
def draw(self):
# 圓形邏輯
self.color.paint(self)
class Line(Shape):
name = "直線"
def draw(self):
# 直線邏輯
self.color.paint(self)
# 具體實現者
class Red(Color):
def paint(self, shape):
print("紅色的%s" % shape.name)
class Green(Color):
def paint(self, shape):
print("綠色的%s" % shape.name)
class Blue(Color):
def paint(self, shape):
print("藍色的%s" % shape.name)
# 頂層調用者
shape = Line(Blue())
shape.draw()
shape2 = Circle(Green())
shape2.draw()
組合模式
內容:將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
角色:
抽象組件(Component)
葉子組件(Leaf)
複合組件(Composite)
客戶端(Client)
適用場景:
表示對象的“部分-整體”層次結構(特別是結構是遞歸的)
希望用戶忽略組合對象與單個對象的不同,用戶統一地使用組合結構中的所有對象
優點:
定義了包含基本對象和組合對象的類層次結構
簡化客戶端代碼,即客戶端可以一致地使用組合對象和單個對象
更容易增加新類型的組件
代碼實現:
將點和線進行組合成為新的圖形。並且點,線,新的圖形具有同樣的方法。在組合的時候,可以進行遞歸使用,並且組合隨機多層組合。
from abc import ABCMeta, abstractmethod
# 抽象組件
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
# 葉子組件
class Point(Graphic):
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "點(%s, %s)" % (self.x, self.y)
def draw(self):
print(str(self))
# 葉子組件
class Line(Graphic):
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def __str__(self):
return "線段[%s, %s]" % (self.p1, self.p2)
def draw(self):
print(str(self))
# 複合組件
class Picture(Graphic):
def __init__(self, iterable):
self.children = []
for g in iterable:
self.add(g)
def add(self, graphic):
self.children.append(graphic)
def draw(self):
print("------複合圖形------")
for g in self.children:
g.draw()
print("------複合圖形------")
# 高層客戶端
p1 = Point(2, 3)
l1 = Line(Point(3, 4), Point(6, 7))
l2 = Line(Point(1, 5), Point(2, 8))
pic1 = Picture([p1, l1, l2]) # 組合成為pic1
p2 = Point(4, 4)
l3 = Line(Point(1, 1), Point(0, 0))
pic2 = Picture([p2, l3]) # 組合成為pic2
pic = Picture([pic1, pic2]) # 組合成為pic
pic.draw()
"""
------複合圖形------ # pic
------複合圖形------ #pic1
點(2, 3)
線段[點(3, 4), 點(6, 7)]
線段[點(1, 5), 點(2, 8)]
------複合圖形------
------複合圖形------ #pic2
點(4, 4)
線段[點(1, 1), 點(0, 0)]
------複合圖形------
------複合圖形------
"""
外觀模式
內容:為子系統中的一組介面提供一個一致的界面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
角色:
外觀(facade)
子系統類(subsystem classes)
優點:
減少系統相互依賴
提高了靈活性
提高了安全性
代碼實現:
直接使用一個類進行響應的封裝,直接調用,不用關心底層是怎麼實現的。
class CPU:
def run(self):
print("CPU開始運行")
def stop(self):
print("CPU停止運行")
class Disk:
def run(self):
print("硬碟開始工作")
def stop(self):
print("硬碟停止工作")
class Memory:
def run(self):
print("記憶體通電")
def stop(self):
print("記憶體斷電")
# 外觀
class Computer: # Facade
def __init__(self):
self.cpu = CPU()
self.disk = Disk()
self.memory = Memory()
def run(self):
self.cpu.run()
self.disk.run()
self.memory.run()
def stop(self):
self.cpu.stop()
self.disk.stop()
self.memory.stop()
# Client
# 客戶端調用
# 不需要關註到底裡面是怎麼實現的
computer = Computer()
computer.run()
computer.stop()
代理模式
內容:為其他對象提供一種代理以控制對這個對象的訪問。
應用場景:
遠程代理:為遠程的對象提供代理(資料庫操作,無圖模式)
虛代理:根據需要創建很大的對象(手機的無圖模式)
保護代理:控制對原始對象的訪問,用於對象有不同訪問許可權時(普通用戶與管理員的許可權不一樣,讀寫許可權)
角色:
抽象實體(Subject)
實體(RealSubject)
代理(Proxy)
優點:
遠程代理:可以隱藏對象位於遠程地址空間的事實
虛代理:可以進行優化,例如根據要求創建對象
保護代理:允許在訪問一個對象時有一些附加的內務處理
代碼實現:
'''虛代理'''
from abc import ABCMeta, abstractmethod
# 抽象實體
class Subject(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
pass
@abstractmethod
def set_content(self, content):
pass
# 實體
class RealSubject(Subject):
def __init__(self, filename):
self.filename = filename
f = open(filename, 'r', encoding='utf-8')
print("讀取文件內容")
self.content = f.read()
f.close()
def get_content(self):
return self.content
def set_content(self, content):
f = open(self.filename, 'w', encoding='utf-8')
f.write(content)
f.close()
# 虛代理
class VirtualProxy(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()
def set_content(self, content):
if not self.subj:
self.subj = RealSubject(self.filename)
return self.subj.set_content(content)
subj = VirtualProxy("test.txt") # 此時並沒有讀取文件,還沒有占用記憶體
ret = subj.get_content() # 此時讀取文件,返回數據
print(ret)
'''保護代理'''
from abc import ABCMeta, abstractmethod
# 抽象實體
class Subject(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
pass
@abstractmethod
def set_content(self, content):
pass
# 實體
class RealSubject(Subject):
def __init__(self, filename):
self.filename = filename
f = open(filename, 'r', encoding='utf-8')
print("讀取文件內容")
self.content = f.read()
f.close()
def get_content(self):
return self.content
def set_content(self, content):
f = open(self.filename, 'w', encoding='utf-8')
f.write(content)
f.close()
# 保護代理
class ProtectedProxy(Subject):
def __init__(self, filename):
self.subj = RealSubject(filename)
def get_content(self):
return self.subj.get_content()
def set_content(self, content):
raise PermissionError("無寫入許可權")
subj = ProtectedProxy("test.txt") # 直接就讀取了文件,占用記憶體
print(subj.get_content())
subj.set_content("abc") # 在寫入文件的時候,進行判斷許可權,PermissionError: 無寫入許可權
行為型模式
責任鏈模式
內容:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
角色:
抽象處理者(Handler)
具體處理者(ConcreteHandler)
客戶端(Client)
適用場景:
有多個對象可以處理一個請求,哪個對象處理由運行時決定
在不明確接收者的情況下,向多個對象中的一個提交一個請求
優點:
降低耦合度:一個對象無需知道是其他哪一個對象處理其請求
代碼實現:
from abc import ABCMeta, abstractmethod
# 抽象處理者
class Handler(metaclass=ABCMeta):
@abstractmethod
def handle_leave(self, day):
pass
# 最上層處理者
class GeneralManager(Handler):
def handle_leave(self, day):
if day <= 10:
print("總經理准假%d天" % day)
else:
print("你還是辭職吧")
# 中層處理者
class DepartmentManager(Handler):
def __init__(self):
self.next = GeneralManager()
def handle_leave(self, day):
if day <= 5:
print("部門經理准假%s天" % day)
else:
print("部門經理職權不足")
self.next.handle_leave(day)
# 底層處理者
class ProjectDirector(Handler):
def __init__(self):
self.next = DepartmentManager()
def handle_leave(self, day):
if day <= 3:
print("項目主管准假%d天" % day)
else:
print("項目主管職權不足")
self.next.handle_leave(day)
# Client
# 頂層調用
day = 2
h = ProjectDirector()
h.handle_leave(day) # 項目主管准假2天
day = 12
h = ProjectDirector()
h.handle_leave(day) # 項目主管職權不足
# 部門經理職權不足
# 你還是辭職吧
觀察者模式
內容:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 所有依賴於它的對象都得到通知並被自動更新。觀察者模式又稱“發佈-訂閱”模式
角色:
抽象主題(Subject)
具體主題(ConcreteSubject)——發佈者
抽象觀察者(Observer)
具體觀察者(ConcreteObserver)——訂閱者
適用場景:
當一個抽象模型有兩方面,其中一個方面依賴於另一個方面。將這兩者封裝在獨立對象中以使它們可以各自獨立地改變和復用。
當對一個對象的改變需要同時改變其它對象,而不知道具體有多少對象有待改變。
當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之,你不希望這些對象是緊密耦合的。
優點:
目標和觀察者之間的抽象耦合最小
支持廣播通信
代碼實現:
from abc import ABCMeta, abstractmethod
# 抽象訂閱者
class Observer(metaclass=ABCMeta):
@abstractmethod
def update(self, notice): # notice 是一個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)
def notify(self): # 迴圈訂閱者,進行推送
for obs in self.observers:
obs.update(self)
# 具體發佈者
class StaffNotice(Notice):
def __init__(self, company_info=None):
super().__init__()
self.__company_info = company_info
@property
def company_info(self): # get 數據的時候使用
return self.__company_info
@company_info.setter
def company_info(self, info): # set 數據的時候使用
self.__company_info = info
self.notify() # 進行推送
# 具體訂閱者
class Staff(Observer):
def __init__(self):
self.company_info = None
def update(self, notice):
self.company_info = notice.company_info
# Client
# 頂層調用
notice = StaffNotice("初始公司信息")
s1 = Staff()
s2 = Staff()
notice.attach(s1)
notice.attach(s2)
print(s1.company_info) # None
print(s2.company_info) # None
notice.company_info = "公司今年業績非常好,給大家發獎金!!!"
print(s1.company_info) # 公司今年業績非常好,給大家發獎金!!!
print(s2.company_info) # 公司今年業績非常好,給大家發獎金!!!
notice.detach(s2)
notice.company_info = "公司明天放假!!!"
print(s1.company_info) # 公司明天放假!!!
print(s2.company_info) # 公司今年業績非常好,給大家發獎金!!!
策略模式
內容:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。
角色:
抽象策略(Strategy)
具體策略(ConcreteStrategy)
上下文(Context)
優點:
定義了一系列可重用的演算法和行為
消除了一些條件語句
可以提供相同行為的不同實現
缺點:
客戶必須瞭解不同的策略
代碼實現:有兩種實現的策略。第一種計算速度快,但是不可靠。另一種計算速度玩,但是可靠。在高峰期與平常時間需要進行響應的切換。
from abc import ABCMeta, abstractmethod
# 抽象策略
class Strategy(metaclass=ABCMeta):
@abstractmethod
def execute(self, data):
pass
# 具體策略
class FastStrategy(Strategy):
def execute(self, data):
print("用較快的策略處理%s" % data)
class SlowStrategy(Strategy):
def execute(self, data):
print("用較慢的策略處理%s" % data)
# 上下文
class Context:
def __init__(self, strategy, data):
self.data = data
self.strategy = strategy
def set_strategy(self, strategy): # 設置
self.strategy = strategy
def do_strategy(self): # 執行
self.strategy.execute(self.data)
# Client
data = "[...]"
s1 = FastStrategy()
s2 = SlowStrategy()
context = Context(s1, data)
context.do_strategy() # 用較快的策略處理[...]
context.set_strategy(s2)
context.do_strategy() # 用較慢的策略處理[...]
模板方法模式
內容:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
角色:
抽象類(AbstractClass):定義抽象的原子操作(鉤子操作);實現一個模板方法作為演算法的骨架。
具體類(ConcreteClass):實現原子操作
適用場景:
一次性實現一個演算法的不變的部分
各個子類中的公共行為應該被提取出來並集中到一個公共父類中以避免代碼重覆
控制子類擴展
代碼實現:
from abc import ABCMeta, abstractmethod
from time import sleep
class Window(metaclass=ABCMeta):
@abstractmethod
def start(self):
pass
@abstractmethod
def repaint(self):
pass
@abstractmethod
def stop(self): # 原子操作/鉤子操作
pass
def run(self): # 模板方法
self.start()
while True:
try:
self.repaint()
sleep(1)
except KeyboardInterrupt:
break
self.stop()
class MyWindow(Window):
def __init__(self, msg):
self.msg = msg
def start(self):
print("視窗開始運行")
def stop(self):
print("視窗結束運行")
def repaint(self):
print(self.msg)
MyWindow("Hello...").run()
結束語
恭喜各位,看完了程式的設計模式,內容很多,量很足,這些設計模式必須對應相應的場景下才有意義,所以需要自己在以後的項目中靈活使用。
程式的設計模式,確實是打開了我的另一種眼界。目前個人用的多的就是工廠方法模式和單例模式。這也算是自己的第一篇知識總結,力求將我所學的知識,儘量整理成為體系知識。
總結:全文從設計模式開題;引入面向對象知識,引入介面定義;引入SOLID原則。正文主講三大設計模式,將每一種設計模式進行詳細說明講解。