結構型模式:適配器模式、橋模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式。python
將一個類的接口轉換成客戶但願的另外一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的的那些類能夠一塊兒工做。安全
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # abstract class @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) class BankPay: def cost(self, money): # 沒有使用pay方法 print("銀聯支付%d元." % money) # 第一種適配器模式:使用多繼承 class NewBankPay(Payment, BankPay): def pay(self, money): # 把原本不兼容的接口cost轉爲兼容的接口pay self.cost(money) p = NewBankPay() p.pay(100) # 銀聯支付100元.
使用這種適配器模式,就將原本不兼容的接口cost轉變爲兼容的接口pay。微信
組合:在一個類裏放入另一個類的對象。app
class A: pass class B: def __init__(self): self.a = A()
根據組合來實現代碼複用,實現多個不兼容接口的適配:函數
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # abstract class @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) class BankPay: def cost(self, money): # 沒有使用pay方法 print("銀聯支付%d元." % money) class ApplePay: def cost(self, money): print("蘋果支付%d元" % money) # 第一種適配器模式:使用多繼承 # class NewBankPay(Payment, BankPay): # def pay(self, money): # 把原本不兼容的接口cost轉爲兼容的接口pay # self.cost(money) # 第二種適配器模式:使用組合 class PaymentAdapter(Payment): def __init__(self, payment): self.payment = payment def pay(self, money): self.payment.cost(money) p = PaymentAdapter(ApplePay()) # 使用適配器進行蘋果支付 p.pay(100) # 蘋果支付100元
這種使用組合來實現的適配器模式就叫作對象適配器。測試
角色:微信支付
適用場景:優化
將一個事物的兩個維度分離,使其均可以獨立地變化。spa
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) # 給paint函數傳入本身(形狀:長方形) class Circle(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) shape = Rectangle(Red()) shape.draw() # 紅色的長方形 shape2 = Circle(Green()) shape2.draw() # 綠色的圓形
如上所示:組合的方式讓形狀和顏色實現耦合(鬆耦合),在形狀和顏色這兩個維度上均可以任意去擴展。代理
角色:
應用場景:
優勢:
將對象組合成樹形結構以表示「部分——總體」的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。
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)) # 把本身經過__str__方法變成字符串打印 # 葉子組件 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)) # 把本身經過__str__方法變成字符串打印 # 複合組件 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(1,1), Point(2,2)) l2 = Line(Point(3,4), Point(2,8)) pic1 = Picture([p1, l1, l2]) p2 = Point(4,4) l3 = Line(Point(1,1), Point(0,0)) pic2 = Picture([p2, l3]) pic = Picture([pic1, pic2]) pic.draw() """ ------複合圖形------ ------複合圖形------ 點(2, 3) 線段[點(1, 1), 點(2, 2)] 線段[點(3, 4), 點(2, 8)] ------複合圖形------ ------複合圖形------ 點(4, 4) 線段[點(1, 1), 點(0, 0)] ------複合圖形------ ------複合圖形------ """
角色:
適用場景:
優勢:
爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
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: # 外觀 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() computer = Computer() computer.run() computer.stop() """ CPU開始運行 硬盤開始工做 內存開始工做 CPU中止運行 硬盤中止工做 內存中止工做 """
外觀模式比較簡單,目的是讓用戶不直接去操做子系統中方法。封裝一個高級的外觀來進行調用。Client就不須要知道是哪幾部分及哪幾部分的哪幾個方法來實現的。
縮小了底層代碼和高層代碼的耦合。子系統須要改造的話,只需修改外觀模式對好接口,就不會出現問題。
角色:
優勢:
爲其餘對象提供一種代理以控制對這個對象的訪問。
略
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() # 虛代理預期:構造對象時不佔用空間保存content,在調用get_content方法時才真正從文件讀取信息。 class VirtualProxy(Subject): # 虛代理也須要實現Subject接口 def __init__(self, filename): self.filename = filename self.subj = None def get_content(self): if not self.subj: # 第一次訪問,self.subj爲None self.subj = RealSubject(self.filename) # 組合方式傳遞self.filename給RealSubject,由它來建立真實對象 return self.subj.get_content() def set_content(self, content): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.set_content() # subj = RealSubject("test.txt") # 僅僅執行到這裏,content也已經在佔用內存了 # subj.get_content() sub2 = VirtualProxy("test.txt") # 此時不佔用內存 print(sub2.get_content()) """ 讀取文件內容 測試代理模式 """
虛代理在構造對象時不佔用空間保存content,在調用get_content方法時才真正從文件讀取信息。
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("無寫入權限") subj3 = ProtectedProxy("test.txt") print(subj3.get_content()) # 讀文件沒有問題 subj3.set_content() # 寫文件直接拋出異常
保護代理容許再訪問對象時有一些不一樣訪問權限。
角色:
優勢: