4.結構型模式

ThankCAT發表於2024-03-26

@

目錄
  • 1. 介面卡模式
  • 2. 橋模式
  • 3. 組合模式
  • 4. 外觀模式
  • 5. 代理模式

1. 介面卡模式

將一個類的介面轉換成客戶希望的另外一個介面,介面卡使得原本由於介面不相容而不能一起工作的那些類可以一起工作。實現介面卡的兩種方式,類介面卡使用多繼承,物件介面卡使用組合組合就是一個類中放入另一類的物件。 先來看下組合:

class A:
	pass
	
class B:
	def __init__():
		self.a = A()

類介面卡模式使用示例:

# 類介面卡模式使用示例:
from abc import ABCMeta, abstractmethod

# 目標介面
class Payment(object, metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass

class Alipay(Payment):
    def pay(self, money):
        print('支付了%d' % money)

# 待適配的類
class BankPay():
    def cost(self, money):
        print('銀聯支付了%d' % money)

# 類介面卡
class PaymentAdapter(Payment, BankPay):
    """
    把不相容cost轉換成pay
    """

    def pay(self, money):
        self.cost(money)

p = PaymentAdapter()
p.pay(100)
"""
銀聯支付了100
"""

物件介面卡模式使用示例:

# 類介面卡模式使用示例:
from abc import ABCMeta, abstractmethod

# 目標介面
class Payment(object, metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass

class Alipay(Payment):
    def pay(self, money):
        print('支付了%d' % money)

# 待適配的類
class BankPay():
    def cost(self, money):
        print('銀聯支付了%d' % money)

# 待適配的類
class ApplePay():
    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)

p = PaymentAdapter(ApplePay())
p.pay(100)
p = PaymentAdapter(BankPay())
p.pay(100)
"""
蘋果支付了100
銀聯支付了100
"""

介面卡模式有三種角色,分別是目標介面、待適配的類和介面卡。適用場景是:想使用一個已存在的類,而它的介面不符合你的要求。想使用一些已經存在的類,但是不可能對每一個都進行子類化以匹配它們的介面。物件介面卡可以適配它的父類介面。

2. 橋模式

橋模式是將一個事物的兩個維度分離,使其都可以獨立地變化。當事物有兩個維度的表現,兩個維度都可能擴充套件時使用。優點是:抽象和實現相分離,擴充套件能力強。如果不使用橋模式,在任何維度進行擴充套件,需要改好多程式碼,因為使用到了繼承:

class Shape:
	pass

class Rectangle(Shape):
	pass

class Circle(Shape):
	pass

class RedRectangle(Rectangle):
	pass

class GreenRectangle(Rectangle):
	pass
	
class RedCircle(Circle):
	pass

class GreenCircle(Circle):
	pass

以上程式碼形狀和顏色兩個維度是透過類的繼承關係緊密結合在一起,是緊耦合。緊耦合是是不可取的,應用橋模式的思想,可以使用組合來實現(松耦合)。如果需要畫直線,直接加上直線的類。需要新顏色,直接加上顏色的類。兩個維度都可以自由擴充套件,不需要新增很多程式碼。這裡的角色有抽象、細化抽象、實現者和具體實現者:

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 Red(Color):
    def paint(self, shape):
        print('畫紅色的%s' % shape.name)

# 如果要擴充套件顏色,只需要新增顏色類
class Green(Color):
    def paint(self, shape):
        print('畫綠色的%s' % shape.name)

rectangle = Rectangle(Red())
rectangle.draw()
circle = Circle(Green())
circle.draw()
"""
畫紅色的長方形
畫綠色的圓形
"""
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(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(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):
        for g in self.children:
            g.draw()

# 簡單圖形
print('------簡單圖形------')
p = Point(1, 2)
l1 = Line(Point(1, 2), Point(3, 4))
l2 = Line(Point(5, 6), Point(7, 8))
print(p)
print(l1)
print(l2)
print('------複合圖形(p,l1,l2)------')
# 複合圖形
pic = Picture([p, l1, l2])
pic.draw()
4. 外觀模式

外觀模式為子系統中的一組介面提供一個一致的介面,外觀模式定義了一個高層的介面,這個介面使得這一子系統更加容易使用。外觀模式下的角色有外觀和子系統類,優點是:減少系統相互依賴,提高靈活性,提高了安全性。下面看一個例子:

# 子系統類
class CPU:
    def run(self):
        print('CPU start to run...')

    def stop(self):
        print('CPU stop to run...')

# 子系統類
class Disk:
    def run(self):
        print('Disk start to run...')

    def stop(self):
        print('Disk stop to run...')

# 子系統類
class Memory:
    def run(self):
        print('Memory start to run...')

    def stop(self):
        print('Memory stop to run...')

# 外觀
class Computer():
    def __init__(self):
        self.CPU = CPU()
        self.Disc = Disk()
        self.Member = Memory()

    def run(self):
        self.CPU.run()
        self.Disc.run()
        self.Member.run()

    def stop(self):
        self.CPU.stop()
        self.Disc.stop()
        self.Member.stop()

# 客戶端,高層程式碼
c = Computer()
c.run()
c.stop()
5. 代理模式

為其它物件提供一種代理以控制對這個物件的訪問。角色有抽象實體、實體和代理。應用場景有遠端代理:為遠端的物件提供代理(透過ORM向資料庫寫值,不用關注資料庫是在遠端);虛代理:根據需要建立很大的物件(需要的時候建立物件);保護代理:控制對原始物件的訪問,用於物件有不同的訪問許可權。下面是不使用虛代理的例子:

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
        print('讀取檔案內容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()

    def get_content(self):
        return self.content

    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)

subj = RealSubject('test.txt')
"""
讀取檔案內容!
"""

使用虛代理的例子:

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
        print('讀取檔案內容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()

    def get_content(self):
        return self.content

    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)

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')
print(subj.get_content())
"""
讀取檔案內容!
"""

不使用虛代理,只要是例項化 RealSubject 類,就會讀取這個檔案佔用記憶體。使用虛代理後,可以和根據需要建立物件,使用者不呼叫是不會建立 RealSubject 物件的,節省了記憶體的開銷。如果需要只有讀的許可權而沒有寫的許可權,可以使用保護代理:

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
        print('讀取檔案內容!')
        with open(self.filename, 'r', encoding='utf-8') as f:
            self.content = f.read()

    def get_content(self):
        return self.content

    def set_content(self, content):
        with open(self.filename, 'w', encoding='utf-8') as f:
            f.write(content)

class ProtectedSubject(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 = ProtectedSubject('test.txt')
print(subj.get_content())
subj.set_content('abc')
"""
讀取檔案內容!
test file!
Traceback (most recent call last):
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 42, in <module>
    subj.set_content('abc')
  File "/home/thanlon/projects/PycharmProjects/untitled/代理模式.py", line 37, in set_content
    raise PermissionError('無寫入許可權!')
PermissionError: 無寫入許可權!
"""

相關文章