軟體系統的設計過程中,經常需要整合不同的元件或擴充套件現有物件的功能。結構設計模式提供了一系列解決方案,以有效地組織程式碼結構並提高程式碼的可維護性和可擴充套件性。介面卡模式專注於解決介面不相容的問題,允許不同的系統或元件無縫協作。而裝飾模式則提供了一種動態地為物件新增新功能的方式,而無需修改其原始結構。這兩種模式在實際開發中都有廣泛的應用,例如整合舊有的支付系統、統一管理不同型別的表演者,以及最佳化遞迴函式的效能。理解並應用這些模式可以有效地提升軟體設計的品質和效率。
結構設計模式
在前一章中,我們探討了建立型模式和物件導向程式設計模式,這些模式幫助我們處理物件的建立過程。接下來,我們將介紹結構設計模式。結構設計模式提出了一種組合物件以提供新功能的方法。
在本章中,我們將涵蓋以下主要主題:
- 介面卡模式
- 裝飾模式
- 橋接模式
- 外觀模式
- 享元模式
- 代理模式
在本章結束時,您將具備使用結構設計模式有效地組織程式碼的技能。
介面卡模式
介面卡模式是一種結構設計模式,幫助我們使兩個不相容的介面相容。這到底是什麼意思?如果我們有一個舊的元件,想要在新的系統中使用,或者有一個新的元件,想要在舊的系統中使用,那麼這兩個元件很少能夠直接進行通訊而不需要修改程式碼。但是,修改程式碼並不總是可行的,因為我們可能無法存取程式碼,或者修改起來很不實際。在這種情況下,我們可以寫一個額外的層,稱為介面卡,來進行所有必要的修改,以實作兩個介面之間的通訊。
實作介面卡模式
假設我們有一個舊的系統,它使用一個名為 OldSystem 的類別,而我們想要在新的系統中使用它。新的系統需要一個名為 NewSystem 的介面。我們可以建立一個介面卡類別 Adapter,它實作了 NewSystem 介面,並包裝了一個 OldSystem 物件。
# 舊系統的類別
class OldSystem:
def __init__(self):
self.data = None
def read_data(self):
return self.data
def write_data(self, data):
self.data = data
# 新系統的介面
class NewSystem:
def read(self):
raise NotImplementedError
def write(self, data):
raise NotImplementedError
# 介面卡類別
class Adapter(NewSystem):
def __init__(self, old_system: OldSystem):
self.old_system = old_system
def read(self):
return self.old_system.read_data()
def write(self, data):
self.old_system.write_data(data)
# 使用介面卡
old_system = OldSystem()
adapter = Adapter(old_system)
adapter.write("Hello, World!")
print(adapter.read()) # 輸出:Hello, World!
內容解密:
此程式碼展示瞭如何使用介面卡模式使舊系統與新系統相容。我們定義了一個 Adapter 類別,它實作了 NewSystem 介面,並包裝了一個 OldSystem 物件。Adapter 類別將 NewSystem 介面的方法對映到 OldSystem 物件的對應方法,從而實作了兩個介面之間的相容。
圖表翻譯:
此圖示展示了介面卡模式的結構。客戶端透過介面卡與舊系統進行互動。介面卡實作了新系統介面,並包裝了舊系統物件。這樣,客戶端就可以透過介面卡使用舊系統的功能,而無需修改舊系統的程式碼。
裝飾模式
裝飾模式是一種結構設計模式,允許我們在不修改原有物件的情況下,動態地新增新的功能。
實作裝飾模式
假設我們有一個名為 Coffee 的類別,表示一杯咖啡。我們可以建立裝飾類別 MilkDecorator 和 SugarDecorator,來為咖啡新增牛奶和糖。
# 咖啡類別
class Coffee:
def cost(self):
raise NotImplementedError
def ingredients(self):
raise NotImplementedError
# 具體咖啡類別
class SimpleCoffee(Coffee):
def cost(self):
return 1
def ingredients(self):
return "Coffee"
# 裝飾類別
class CoffeeDecorator(Coffee):
def __init__(self, coffee: Coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost()
def ingredients(self):
return self.coffee.ingredients()
# 牛奶裝飾類別
class MilkDecorator(CoffeeDecorator):
def cost(self):
return super().cost() + 0.5
def ingredients(self):
return super().ingredients() + ", Milk"
# 糖裝飾類別
class SugarDecorator(CoffeeDecorator):
def cost(self):
return super().cost() + 0.2
def ingredients(self):
return super().ingredients() + ", Sugar"
# 使用裝飾模式
coffee = SimpleCoffee()
coffee = MilkDecorator(coffee)
coffee = SugarDecorator(coffee)
print(coffee.cost()) # 輸出:1.7
print(coffee.ingredients()) # 輸出:Coffee, Milk, Sugar
內容解密:
此程式碼展示瞭如何使用裝飾模式為咖啡新增不同的配料。我們定義了 Coffee 類別和具體咖啡類別 SimpleCoffee,以及裝飾類別 CoffeeDecorator 和其子類別 MilkDecorator 和 SugarDecorator。裝飾類別包裝了咖啡物件,並增加了新的功能。客戶端可以透過裝飾類別來建立具有不同配料的咖啡。
圖表翻譯:
此圖示展示了裝飾模式的結構。客戶端透過咖啡介面與具體咖啡類別或裝飾類別進行互動。裝飾類別包裝了咖啡物件,並增加了新的功能。這樣,客戶端就可以建立具有不同配料的咖啡,而無需修改原有的咖啡類別。
結構設計模式:介面卡模式的應用與實作
在軟體開發過程中,經常會遇到需要將不同介面進行整合的情況。介面卡模式(Adapter Pattern)是一種常見的結構設計模式,能夠有效地解決介面不相容的問題。本文將深入探討介面卡模式的原理、應用場景以及在Python中的實作方法。
介面卡模式的基本概念
介面卡模式的主要目的是將一個類別的介面轉換成客戶端所期望的另一個介面。透過這種方式,可以讓原本由於介面不相容而無法一起工作的類別能夠協同工作。
現實生活中的例子
在現實生活中,介面卡模式的應用非常普遍。例如,當我們從大多數歐洲國家旅行到英國或美國時,需要使用插頭介面卡來為我們的筆記型電腦充電。同樣地,某些裝置需要特定的介面卡才能連線到電腦,如USB介面卡。
介面卡模式的應用場景
通常,兩個不相容的介面中,有一個是外部或舊有的介面。如果介面是外部的,意味著我們無法存取其原始碼。如果是舊有的介面,通常也不太實際進行重構。使用介面卡模式可以在不修改現有程式碼的情況下使它們協同工作。
適用情況
- 當需要使用一個現有的類別,但其介面不符合需求時。
- 當需要重複使用一些現有的子類別,但這些子類別的介面不一致時。
- 當需要將多個不同的類別整合到一個統一的介面下時。
介面卡模式的實作
適配舊有的支付系統
假設我們有一個舊有的支付系統和一個新的支付閘道。舊有的支付系統使用make_payment()方法,而新的支付閘道使用execute_payment()方法。我們可以透過介面卡模式使它們協同工作。
# 舊有的支付系統
class OldPaymentSystem:
def __init__(self, currency):
self.currency = currency
def make_payment(self, amount):
print(f"[OLD] Pay {amount} {self.currency}")
# 新的支付閘道
class NewPaymentGateway:
def __init__(self, currency):
self.currency = currency
def execute_payment(self, amount):
print(f"Execute payment of {amount} {self.currency}")
# 介面卡類別
class PaymentAdapter:
def __init__(self, system):
self.system = system
def make_payment(self, amount):
self.system.execute_payment(amount)
# 測試程式碼
def main():
old_system = OldPaymentSystem("euro")
print(old_system)
new_system = NewPaymentGateway("euro")
print(new_system)
adapter = PaymentAdapter(new_system)
adapter.make_payment(100)
if __name__ == "__main__":
main()
圖表翻譯:
此圖示展示瞭如何根據不同的支付系統選擇合適的處理流程。如果選擇舊有的支付系統,直接使用其make_payment()方法;如果選擇新的支付閘道,則透過PaymentAdapter來呼叫execute_payment()方法。最終,無論使用哪種支付系統,都能完成支付流程。
將多個類別適配到統一介面
假設我們有一個俱樂部類別(Club),它需要與不同的表演者(如音樂家和舞者)合作。我們可以透過介面卡模式將不同的表演者類別適配到俱樂部所需的介面。
# 俱樂部類別
class Club:
def __init__(self, name):
self.name = name
def __str__(self):
return f"the club {self.name}"
def organize_event(self, performer):
print(f"{self} hires {performer} to {performer.perform()}")
# 音樂家類別
class Musician:
def __init__(self, name):
self.name = name
def __str__(self):
return f"the musician {self.name}"
def play(self):
return "plays music"
# 舞者類別
class Dancer:
def __init__(self, name):
self.name = name
def __str__(self):
return f"the dancer {self.name}"
def dance(self):
return "does a dance performance"
# 介面卡類別
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
# 測試程式碼
def main():
club = Club("Melody")
musician = Musician("John")
dancer = Dancer("Jane")
musician_adapter = Adapter(musician, {'perform': musician.play})
dancer_adapter = Adapter(dancer, {'perform': dancer.dance})
club.organize_event(musician_adapter)
club.organize_event(dancer_adapter)
if __name__ == "__main__":
main()
圖表翻譯:
此圖示展示了俱樂部如何與不同的表演者合作。透過使用介面卡模式,可以將音樂家和舞者這兩種不同的表演者類別適配到俱樂部所需的介面,從而實作統一的排程管理。
結構設計模式:擴充套件物件功能
在軟體開發過程中,經常需要擴充套件現有物件的功能而不改變其結構。結構設計模式提供瞭解決這一問題的有效方法。本文將重點介紹兩種重要的結構設計模式:介面卡模式(Adapter Pattern)和裝飾器模式(Decorator Pattern)。
介面卡模式:實作介面相容
介面卡模式是一種常用的結構設計模式,用於將現有類別的介面轉換為客戶端所期望的介面。該模式透過引入一個介面卡類別來解決介面不相容的問題,使得原本無法協同工作的類別能夠一起工作。
實際案例:舞臺表演管理
假設我們正在開發一個舞臺表演管理系統,需要處理不同型別的表演者,如音樂家和舞者。這些表演者具有不同的介面,分別包含 play() 和 dance() 方法。為了統一管理這些表演者,我們可以使用介面卡模式將他們的介面轉換為統一的 organize_event() 方法。
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
def main():
objects = [
Club("Jazz Cafe"),
Musician("Roy Ayers"),
Dancer("Shane Sparks"),
]
for obj in objects:
if hasattr(obj, "play") or hasattr(obj, "dance"):
if hasattr(obj, "play"):
adapted_methods = dict(organize_event=obj.play)
elif hasattr(obj, "dance"):
adapted_methods = dict(organize_event=obj.dance)
obj = Adapter(obj, adapted_methods)
# 使用統一的介面進行處理
print(obj.organize_event())
if __name__ == "__main__":
main()
內容解密:
此程式碼實作了介面卡模式,用於統一不同表演者的介面。透過 Adapter 類別,我們可以將 Musician 和 Dancer 類別的 play() 和 dance() 方法適配為統一的 organize_event() 方法,從而實作對不同表演者的統一管理。
圖表翻譯:
此圖示展示了介面卡模式的工作流程。客戶端程式碼首先檢查物件的介面是否相容。如果相容,則直接呼叫對應的方法;如果不相容,則使用介面卡將物件的介面轉換為相容的介面,然後再進行方法呼叫。
裝飾器模式:動態擴充套件物件功能
裝飾器模式是一種允許在執行階段動態地為物件新增功能的設計模式。該模式透過包裝現有物件並新增額外功能,而不改變其原始結構。
實際案例:快取最佳化
在開發數學計算模組時,我們經常需要對遞迴函式進行最佳化,以避免重複計算。裝飾器模式提供了一種優雅的解決方案:使用快取裝飾器來記憶已經計算過的結果。
def memoize(func):
cache = dict()
def memoized_func(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return memoized_func
@memoize
def number_sum(n):
if n == 0:
return 0
else:
return n + number_sum(n-1)
if __name__ == "__main__":
from timeit import Timer
t = Timer("number_sum(300)", "from __main__ import number_sum")
print("Time: ", t.timeit())
內容解密:
此程式碼實作了一個簡單的快取裝飾器 memoize,用於最佳化遞迴函式 number_sum 的效能。透過快取已經計算過的結果,避免了重複計算,大大提高了函式的執行效率。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 結構設計模式介面卡與裝飾模式
package "物件導向程式設計" {
package "核心概念" {
component [類別 Class] as class
component [物件 Object] as object
component [屬性 Attribute] as attr
component [方法 Method] as method
}
package "三大特性" {
component [封裝
Encapsulation] as encap
component [繼承
Inheritance] as inherit
component [多型
Polymorphism] as poly
}
package "設計原則" {
component [SOLID] as solid
component [DRY] as dry
component [KISS] as kiss
}
}
class --> object : 實例化
object --> attr : 資料
object --> method : 行為
class --> encap : 隱藏內部
class --> inherit : 擴展功能
inherit --> poly : 覆寫方法
solid --> dry : 設計模式
note right of solid
S: 單一職責
O: 開放封閉
L: 里氏替換
I: 介面隔離
D: 依賴反轉
end note
@enduml
圖表翻譯:
此圖示展示了裝飾器模式的執行流程。客戶端呼叫裝飾後的方法,裝飾器再呼叫原始物件的方法,並在傳回結果前新增額外的功能(如快取),最後將最終結果傳回給客戶端。