結構型模式

  結構型模式:適配器模式、橋模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式。python

1、適配器模式

  將一個類的接口轉換成客戶但願的另外一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的的那些類能夠一塊兒工做。安全

一、適配器模式實現

(1)類適配器:使用多繼承

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。微信

(2)對象適配器:使用組合

  組合:在一個類裏放入另一個類的對象。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元

  這種使用組合來實現的適配器模式就叫作對象適配器。測試

二、適配器模式總結

角色:微信支付

  • 目標接口(Target)
  • 待適配的類(Adaptee)
  • 適配器(Adapter)

適用場景:優化

  • 想使用一個已經存在的類,而它的接口不符合你的要求。
  • (對象適配器)想使用一些已經存在的子類,但不可能對每個都進行子類化以匹配它們的接口,對象適配器能夠適配它的父類接口。

2、橋模式

  將一個事物的兩個維度分離,使其均可以獨立地變化。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()  # 綠色的圓形

  如上所示:組合的方式讓形狀和顏色實現耦合(鬆耦合),在形狀和顏色這兩個維度上均可以任意去擴展。代理

二、橋模式總結

角色:

  • 抽象(Abstraction):Shape
  • 細化抽象(RefinedAbstraction):Rectangle、Circle....
  • 實現者(Implementor):Color
  • 具體實現者(ConcreteImplementor):Red、Green...

應用場景:

  • 當事物有兩個維度上的表現,兩個維度均可能擴展時。

優勢:

  • 抽象和實現相分離
  • 優秀的擴展能力

3、組合模式

  將對象組合成樹形結構以表示「部分——總體」的層次結構。組合模式使得用戶對單個對象組合對象的使用具備一致性

一、組合模式示例

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)]
------複合圖形------
------複合圖形------
"""

 

二、組合模式總結

角色:

  • 抽象組件(Component)
  • 葉子組件(Leaf)
  • 複合組件(Composite)
  • 客戶端(Client)

適用場景:

  • 表示對象的「部分——總體」層次結構(特別是結構是遞歸的)
  • 但願用戶忽略組合對象與單個對象的不一樣,用戶統一地使用組合結構中的全部對象

優勢:

  • 定義了包含基本對象和組合對象的類層次結構
  • 簡化客戶端代碼,即客戶端能夠一致地適用組合對象和單個對象
  • 更容易增長新類型的組件(好比添加圓形等)

4、外觀模式

  爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

一、外觀模式示例

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就不須要知道是哪幾部分及哪幾部分的哪幾個方法來實現的。

  縮小了底層代碼和高層代碼的耦合。子系統須要改造的話,只需修改外觀模式對好接口,就不會出現問題。

二、外觀模式總結

角色:

  • 外觀(facade)
  • 子系統類(subsystem classes)

優勢:

  • 減小了系統相互依賴(解耦)
  • 提升了靈活性(更加方便和靈活提供客戶端調用)
  • 提升了安全性(防止了遺漏)

5、代理模式

  爲其餘對象提供一種代理以控制對這個對象的訪問。

一、應用場景

(1)遠程代理:爲遠程的對象提供代理

  略

(2)虛代理:根據須要建立很大的對象

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方法時才真正從文件讀取信息。

(3)保護代理:控制對原始對象的訪問,用於對象有不一樣訪問權限時

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()  # 寫文件直接拋出異常

 

  保護代理容許再訪問對象時有一些不一樣訪問權限。

二、代理模式總結

角色:

  • 抽象實體(Subject)
  • 實體(RealSubject)
  • 代理(Proxy)

優勢:

  • 遠程代理:能夠隱藏對象位於遠程地址空間的事實
  • 虛代理:能夠進行優化,例如根據要求建立對象
  • 保護代理:容許在訪問一個對象時有一些父級的內務處理
相關文章
相關標籤/搜索