代理模式和責任鏈模式是兩種常見的軟體設計模式,它們都能有效提升程式碼的彈性和可維護性。代理模式主要用於控制對物件的存取,例如保護敏感資訊或延遲初始化。責任鏈模式則允許多個物件有機會處理同一個請求,降低物件之間的耦合度。在使用者管理系統中,代理模式可以限制未授權使用者對敏感資料的存取。而責任鏈模式則可以在事件處理機制中,讓不同的物件處理不同型別的事件,簡化程式碼邏輯。本文將分別以 Python 程式碼為例,說明如何實作這兩種模式,並探討它們的優缺點和應用場景。
懶初始化的實作
下面是一個簡單的例子,展示如何使用 Python 來實作懶初始化:
class Test:
def __init__(self):
self._resource = None
@property
def resource(self):
if self._resource is None:
self._resource = tuple(range(5)) # expensive
return self._resource
def main():
t = Test()
print(t.resource)
print(t.resource)
if __name__ == "__main__":
main()
在這個例子中,Test
類別的 resource
屬性被定義為一個 @property
,它會在第一次被存取時初始化 _resource
變數。之後的存取將直接傳回已經初始化的 _resource
。
懶初始化的優點
懶初始化可以帶來以下優點:
- 效率提升:延遲初始化可以避免不必要的計算或資源分配,從而提高程式的效率。
- 記憶體節省:如果某些資源或物件不需要立即初始化,則可以節省記憶體空間。
懶初始化的實際應用
懶初始化在許多實際應用中非常有用,例如:
- 資料函式庫連線:延遲建立資料函式庫連線,直到真正需要存取資料。
- 影像處理:延遲載入影像,直到需要顯示或處理。
- 網路請求:延遲傳送網路請求,直到需要取得資料。
代理模式(Proxy Pattern)簡介
代理模式是一種結構型設計模式,允許您為物件提供一個替代物件,以控制對原始物件的存取。這個模式可以用於實作多種功能,例如延遲初始化、存取控制和遠端存取。
代理模式的種類
代理模式可以分為四種主要型別:
- 虛擬代理(Virtual Proxy):用於延遲初始化,直到物件真正需要時才建立。
- 保護代理(Protection Proxy):用於控制對原始物件的存取,例如根據使用者許可權進行存取控制。
- 遠端代理(Remote Proxy):用於提供對遠端物件的存取,例如在分散式系統中。
- 智慧代理(Smart Proxy):用於提供額外的功能,例如記錄存取或實作執行緒安全。
代理模式的實作
以下是一個簡單的代理模式實作,使用 Python 語言:
class SensitiveInfo:
def __init__(self):
self.users = ['nick', 'tom', 'ben', 'mike']
def read(self):
nb = len(self.users)
print(f"There are {nb} users: {' '.join(self.users)}")
def add(self, user):
self.users.append(user)
print(f"User {user} added!")
class ProtectionProxy:
def __init__(self, sensitive_info):
self.sensitive_info = sensitive_info
self.secret_message = "secret"
def read(self):
self.sensitive_info.read()
def add(self, user, message):
if message == self.secret_message:
self.sensitive_info.add(user)
else:
print("Access denied!")
# 使用代理模式
sensitive_info = SensitiveInfo()
proxy = ProtectionProxy(sensitive_info)
proxy.read() # 讀取使用者列表
proxy.add("john", "secret") # 新增新使用者
proxy.add("jane", "wrong") # 新增新使用者(存取被拒絕)
在這個例子中,SensitiveInfo
類別代表原始物件,ProtectionProxy
類別代表保護代理。代理模式控制對原始物件的存取,要求提供正確的秘密訊息才能新增新使用者。
代理模式的優點
代理模式提供了多種優點,包括:
- 延遲初始化:代理模式可以延遲初始化,直到物件真正需要時才建立。
- 存取控制:代理模式可以控制對原始物件的存取,例如根據使用者許可權進行存取控制。
- 遠端存取:代理模式可以提供對遠端物件的存取,例如在分散式系統中。
- 額外功能:代理模式可以提供額外的功能,例如記錄存取或實作執行緒安全。
代理模式的應用場景
代理模式的應用場景包括:
- 分散式系統:代理模式可以用於提供對遠端物件的存取,例如在分散式系統中。
- 存取控制:代理模式可以用於控制對原始物件的存取,例如根據使用者許可權進行存取控制。
- 延遲初始化:代理模式可以用於延遲初始化,直到物件真正需要時才建立。
- 記錄存取:代理模式可以用於記錄存取,例如記錄使用者存取原始物件的次數。
圖表翻譯:
classDiagram class SensitiveInfo { - users: list + read() + add(user) } class ProtectionProxy { - sensitive_info: SensitiveInfo - secret_message: str + read() + add(user, message) } SensitiveInfo <|-- ProtectionProxy
這個圖表顯示了 SensitiveInfo
類別和 ProtectionProxy
類別之間的關係,ProtectionProxy
類別是 SensitiveInfo
類別的代理。
代理模式(Proxy Pattern)在使用者管理中的應用
代理模式是一種結構型設計模式,讓你能夠提供對原始物件的替代物件,並控制對原始物件的存取。這種模式在使用者管理中非常有用,特別是在需要保護敏感資訊或限制存取的情況下。
保護代理(Protection Proxy)
保護代理是一種代理模式,主要用於控制對原始物件的存取,通常是為了保護敏感資訊。以下是一個使用保護代理的例子:
class SensitiveInfo:
def __init__(self):
self.users = []
def add(self, user):
self.users.append(user)
print(f'Added user {user}')
def read(self):
print('Sensitive info:', self.users)
class Info:
'''保護代理到 SensitiveInfo'''
def __init__(self):
self.protected = SensitiveInfo()
self.secret = '0xdeadbeef'
def read(self):
self.protected.read()
def add(self, user):
sec = input('請輸入密碼:')
if sec == self.secret:
self.protected.add(user)
else:
print("密碼錯誤!")
def main():
info = Info()
while True:
print("1. 讀取使用者列表")
print("2. 新增新使用者")
print("3. 離開")
choice = input('請選擇:')
if choice == '1':
info.read()
elif choice == '2':
user = input('請輸入新使用者名稱:')
info.add(user)
elif choice == '3':
break
else:
print("無效選擇,請重新選擇。")
if __name__ == '__main__':
main()
在這個例子中,Info
類別是 SensitiveInfo
的保護代理。Info
類別控制對 SensitiveInfo
的存取,要求使用者輸入密碼才能新增新使用者。這樣可以保護敏感資訊,避免未經授權的存取。
實際應用
在實際應用中,保護代理可以用於各種需要控制存取的情況,例如:
- 用於保護敏感資料,例如使用者密碼或信用卡號碼。
- 用於限制存取,例如只有管理員才能新增新使用者。
- 用於記錄存取,例如記錄誰何時存取了敏感資料。
使用者管理系統
系統功能
使用者管理系統是一個簡單的命令列介面(CLI)應用程式,提供使用者管理功能。系統功能包括:
- 顯示使用者列表
- 新增使用者
- 離開系統
程式碼實作
以下是使用者管理系統的程式碼實作:
class Info:
def __init__(self):
self.users = ["nick", "tom", "ben", "mike"]
self.secret = "12345"
def read(self):
print(f"There are {len(self.users)} users: {', '.join(self.users)}")
def add(self, name):
password = input("what is the secret? ")
if password == self.secret:
self.users.append(name)
print(f"User {name} added successfully!")
else:
print("That's wrong!")
def main():
info = Info()
while True:
print('1. read list |==| 2. add user |==| 3. quit')
key = input('choose option: ')
if key == '1':
info.read()
elif key == '2':
name = input('choose username: ')
info.add(name)
elif key == '3':
break
else:
print(f'unknown option: {key}')
if __name__ == "__main__":
main()
系統流程
- 初始化使用者管理系統,建立一個
Info
物件,包含使用者列表和密碼。 - 顯示系統選單,提供使用者選擇功能。
- 根據使用者選擇,執行相應功能:
- 顯示使用者列表:呼叫
read()
方法,顯示使用者列表。 - 新增使用者:呼叫
add()
方法,新增使用者。 - 離開系統:離開迴圈,結束系統。
- 顯示使用者列表:呼叫
- 如果使用者選擇未知選項,顯示錯誤訊息。
範例使用
- 執行系統,顯示選單。
- 選擇「1. read list」,顯示使用者列表。
- 選擇「2. add user」,新增使用者,輸入使用者名稱和密碼。
- 如果密碼正確,新增使用者成功,顯示成功訊息。
- 如果密碼錯誤,顯示錯誤訊息。
- 選擇「3. quit」,離開系統。
瞭解責任鏈模式
責任鏈模式是一種設計模式,主要用於處理多個物件之間的請求或事件。它允許將請求或事件傳遞給一系列的物件,每個物件都有機會處理請求或事件。這種模式尤其適合於事件驅動系統、購買系統和運輸系統等應用場景。
責任鏈模式的優點
責任鏈模式具有以下優點:
- 解耦: 客戶端和處理請求的物件之間的耦合度降低。
- 靈活性: 可以動態地新增或刪除處理請求的物件。
- 可擴充套件性: 可以輕易地增加新的請求或事件處理。
實際應用
以下是一個簡單的例項,展示瞭如何使用責任鏈模式來處理使用者的請求:
from abc import ABC, abstractmethod
# 定義抽象的處理者
class Handler(ABC):
@abstractmethod
def set_next(self, handler):
pass
@abstractmethod
def handle(self, request):
pass
# 定義具體的處理者
class ConcreteHandler1(Handler):
def __init__(self):
self.next_handler = None
def set_next(self, handler):
self.next_handler = handler
def handle(self, request):
if request == "request1":
print("ConcreteHandler1: 處理請求1")
elif self.next_handler:
self.next_handler.handle(request)
class ConcreteHandler2(Handler):
def __init__(self):
self.next_handler = None
def set_next(self, handler):
self.next_handler = handler
def handle(self, request):
if request == "request2":
print("ConcreteHandler2: 處理請求2")
elif self.next_handler:
self.next_handler.handle(request)
# 客戶端程式碼
def client_code(handler):
requests = ["request1", "request2", "request3"]
for request in requests:
handler.handle(request)
# 建立處理者鏈
handler1 = ConcreteHandler1()
handler2 = ConcreteHandler2()
handler1.set_next(handler2)
# 執行客戶端程式碼
client_code(handler1)
連鎖責任模式
連鎖責任模式是一種設計模式,允許多個物件有機會處理一個請求。這種模式通常用於當我們不知道哪個物件應該處理請求時,或是當多個物件需要處理同一個請求時。
連鎖責任模式的工作原理
連鎖責任模式的工作原理是建立一個物件鏈,當一個請求發生時,鏈中的每個物件都有機會處理這個請求。如果一個物件不能處理這個請求,它會將請求轉發給下一個物件,直到鏈中的所有物件都被嘗試過為止。
連鎖責任模式的優點
連鎖責任模式的優點是它允許我們將請求的處理與請求的傳送者分離,從而提高了系統的靈活性和可擴充套件性。另外,連鎖責任模式也可以幫助我們避免多個物件之間的耦合,從而提高了系統的維護性。
連鎖責任模式的實際應用
連鎖責任模式在許多實際應用中都有使用,例如:
- 自動櫃員機(ATM)系統:當使用者插入一張金融卡時,系統會將請求轉發給多個物件,包括驗證物件、查詢物件和取款物件等。
- 事件驅動系統:當一個事件發生時,系統會將事件轉發給多個物件,包括事件處理物件、日誌記錄物件和通知物件等。
連鎖責任模式的實作
連鎖責任模式可以透過多種方式實作,包括:
- 使用連結串列:建立一個連結串列,每個節點代表一個物件,當一個請求發生時,連結串列中的每個節點都有機會處理這個請求。
- 使用樹形結構:建立一個樹形結構,每個節點代表一個物件,當一個請求發生時,樹形結構中的每個節點都有機會處理這個請求。
連鎖責任模式的程式碼實作
以下是連鎖責任模式的一個簡單程式碼實作:
from abc import ABC, abstractmethod
# 事件類別
class Event:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
# 物件類別
class Widget(ABC):
def __init__(self, parent=None):
self.parent = parent
@abstractmethod
def handle_event(self, event):
pass
# 具體物件類別
class MsgText(Widget):
def handle_event(self, event):
if event.name == "msg_text":
print("處理 msg_text 事件")
else:
if self.parent:
self.parent.handle_event(event)
# 連鎖責任模式的實作
class ChainOfResponsibility:
def __init__(self):
self.widgets = []
def add_widget(self, widget):
self.widgets.append(widget)
def handle_event(self, event):
for widget in self.widgets:
widget.handle_event(event)
# 測試
chain = ChainOfResponsibility()
widget1 = MsgText()
widget2 = MsgText(widget1)
chain.add_widget(widget2)
chain.handle_event(Event("msg_text"))
Responsibility Chain 模式的實作
Responsibility Chain 模式是一種行為設計模式,允許將請求的處理分配給多個物件。這些物件形成了一個鏈式結構,請求會沿著這個鏈式傳遞,直到找到能夠處理它的物件。
Responsibility Chain 模式的結構
Responsibility Chain 模式的結構包括以下幾個部分:
- Handler:定義了請求的處理介面。
- ConcreteHandler:實作了 Handler 介面,負責處理特定的請求。
- Client:提交請求的客戶端。
Responsibility Chain 模式的實作
以下是 Responsibility Chain 模式的實作:
from abc import ABC, abstractmethod
# Handler
class Handler(ABC):
@abstractmethod
def handle(self, event):
pass
# ConcreteHandler
class Widget(Handler):
def __init__(self, parent=None):
self.parent = parent
def handle(self, event):
handler = f'handle_{event}'
if hasattr(self, handler):
method = getattr(self, handler)
method(event)
elif self.parent is not None:
self.parent.handle(event)
elif hasattr(self, 'handle_default'):
self.handle_default(event)
# ConcreteHandler
class MainWindow(Widget):
def handle_close(self, event):
print(f'MainWindow: {event}')
# Client
class Event:
def __init__(self, name):
self.name = name
# 使用 Responsibility Chain 模式
if __name__ == '__main__':
main_window = MainWindow()
event = Event('close')
main_window.handle(event.name)
Responsibility Chain 模式的優點
Responsibility Chain 模式的優點包括:
- 解耦: Responsibility Chain 模式可以解耦請求的傳送者和接收者。
- 靈活性: Responsibility Chain 模式可以增加或刪除處理請求的物件。
- 重用性: Responsibility Chain 模式可以重用處理請求的物件。
Responsibility Chain 模式的缺點
Responsibility Chain 模式的缺點包括:
- 複雜性: Responsibility Chain 模式可以增加系統的複雜性。
- 效能: Responsibility Chain 模式可以影響系統的效能。
Responsibility Chain 模式的應用場景
Responsibility Chain 模式的應用場景包括:
- 事件處理: Responsibility Chain 模式可以用於事件處理。
- 請求處理: Responsibility Chain 模式可以用於請求處理。
- 錯誤處理: Responsibility Chain 模式可以用於錯誤處理。
圖表翻譯:
flowchart TD A[請求] --> B[Handler] B --> C[ConcreteHandler] C --> D[Client] D --> E[事件處理] E --> F[請求處理] F --> G[錯誤處理]
Responsibility Chain 模式是一種行為設計模式,允許將請求的處理分配給多個物件。這種模式可以解耦請求的傳送者和接收者,增加系統的靈活性和重用性。然而,Responsibility Chain 模式也可以增加系統的複雜性和影響系統的效能。因此,需要根據具體的應用場景選擇合適的設計模式。
事件處理機制
在事件驅動的應用程式中,事件處理機制扮演著至關重要的角色。它們使得應用程式能夠對使用者的互動、系統的變化以及其他事件做出反應。以下是事件處理機制的一個簡單示例,展示瞭如何建立事件和將其傳遞給不同的部件。
事件類別
首先,我們需要定義事件類別。這些類別代表了不同的事件型別,例如點選事件、繪製事件等。
class Event:
def __init__(self, type):
self.type = type
def __str__(self):
return self.type
部件類別
接下來,我們定義部件類別。每個部件都有自己的事件處理方法,根據事件型別不同,部件會有不同的反應。
class Widget:
def handle(self, event):
pass
class MainWindow(Widget):
def handle(self, event):
print(f'MainWindow Default: {event}')
class SendDialog(Widget):
def handle_paint(self, event):
print(f'SendDialog: {event}')
class MsgText(Widget):
def handle_down(self, event):
print(f'MsgText: {event}')
事件處理
現在,我們需要實作事件處理的機制。這涉及到建立事件、將事件傳遞給部件以及部件對事件的反應。
def main():
mw = MainWindow()
sd = SendDialog()
sd.parent = mw # 設定父級關係
msg = MsgText()
msg.parent = sd # 設定父級關係
for e in ('down', 'paint', 'unhandled', 'close'):
evt = Event(e)
print(f'Sending event -{evt}- to MainWindow')
mw.handle(evt)
# 將事件傳遞給子部件
if hasattr(sd, f'handle_{evt.type}'):
getattr(sd, f'handle_{evt.type}')(evt)
if hasattr(msg, f'handle_{evt.type}'):
getattr(msg, f'handle_{evt.type}')(evt)
if __name__ == '__main__':
main()
結果
當我們執行這個程式時,MainWindow 會對所有事件做出反應,而 SendDialog 和 MsgText 則只對特定的事件做出反應。
Sending event -down- to MainWindow
MainWindow Default: down
MsgText: down
Sending event -paint- to MainWindow
MainWindow Default: paint
SendDialog: paint
Sending event -unhandled- to MainWindow
MainWindow Default: unhandled
Sending event -close- to MainWindow
MainWindow Default: close
這個示例展示了事件處理機制的基本概念,包括事件的建立、傳遞和部件的反應。它還強調了父級關係在事件傳遞中的重要性。
命令模式(Command Pattern)
命令模式是一種設計模式,允許我們將操作(如複製和貼上)封裝為物件。這種模式也非常適合於群組多個命令,實作巨集(macros)、多級復原(multilevel undoing)和事務(transactions)。在本章中,我們將探討將操作視為物件的概念,並使用命令模式來處理應用程式事務。
從技術架構視角來看,本文探討了多種設計模式,包含懶初始化、代理模式、責任鏈模式和命令模式,涵蓋了從物件建立到行為組織的各種導向。分析這些模式的實作方式可以發現,它們的核心價值在於解耦合、提升程式碼彈性和擴充套件性。代理模式和責任鏈模式都著重於處理請求的流程管理,但代理模式強調單一物件的存取控制,而責任鏈模式則關注多個物件協同處理請求。責任鏈模式在事件驅動系統中展現出高度靈活性,但也需要注意處理鏈過長可能帶來的效能損耗。命令模式則將操作封裝成物件,方便組合和管理,為實作複雜的巨集和復原功能提供了基礎。展望未來,隨著軟體系統日趨複雜,這些設計模式的重要性將更加凸顯。對於追求高品質程式碼的開發者而言,深入理解並靈活運用這些模式將是提升系統架構能力的關鍵。玄貓認為,在實際專案中,應根據具體需求選擇合適的設計模式,切勿盲目套用,才能真正發揮設計模式的威力。