觀察者模式和狀態模式都是軟體設計中常用的行為型模式,它們分別解決了不同的問題。觀察者模式著重於物件間的通知機制,而狀態模式則關注物件內部狀態的管理和轉換。理解這兩種模式的差異和應用場景,有助於提升程式碼的可維護性和擴充套件性。實際開發中,我們常常需要處理物件狀態的變化以及物件之間的通訊,這兩種模式提供瞭解決這類別問題的有效方法。例如,在GUI程式設計中,觀察者模式可以讓不同的UI元件對資料模型的變化做出反應;而在遊戲開發中,狀態模式可以管理遊戲角色的不同狀態,例如行走、攻擊、防禦等。
觀察者模式(Observer Pattern)詳解
觀察者模式是一種行為設計模式,允許物件在狀態改變時通知其依賴物件。此模式提供了一種鬆耦合的方式,使得物件之間的依賴關係可以動態地新增或移除。
觀察者模式的結構
觀察者模式包含兩個主要角色:主題(Subject)和觀察者(Observer)。
- 主題是被觀察的物件,負責維護觀察者列表並在狀態改變時通知它們。
- 觀察者是依賴於主題的物件,當主題狀態改變時會收到通知。
程式碼實作
以下是一個使用 Python 實作的觀察者模式範例,用於模擬天氣站與多個顯示裝置之間的互動:
from abc import ABC, abstractmethod
# 定義觀察者介面
class Observer(ABC):
@abstractmethod
def update(self, temperature, humidity, pressure):
pass
# 定義主題類別
class WeatherStation:
def __init__(self):
self.observers = []
self.temperature = None
self.humidity = None
self.pressure = None
def add_observer(self, observer):
self.observers.append(observer)
def remove_observer(self, observer):
self.observers.remove(observer)
def notify_observers(self):
for observer in self.observers:
observer.update(self.temperature, self.humidity, self.pressure)
def set_weather_data(self, temperature, humidity, pressure):
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
self.notify_observers()
# 定義顯示裝置觀察者
class DisplayDevice(Observer):
def __init__(self, name):
self.name = name
def update(self, temperature, humidity, pressure):
print(f"{self.name} Display")
print(f" - Temperature: {temperature}°C, Humidity: {humidity}%, Pressure: {pressure}hPa")
# 定義天氣應用觀察者
class WeatherApp(Observer):
def __init__(self, name):
self.name = name
def update(self, temperature, humidity, pressure):
print(f"{self.name} App - Weather Update")
print(f" - Temperature: {temperature}°C, Humidity: {humidity}%, Pressure: {pressure}hPa")
def main():
# 建立天氣站例項
weather_station = WeatherStation()
# 建立並註冊觀察者
display1 = DisplayDevice("Living Room")
display2 = DisplayDevice("Bedroom")
app1 = WeatherApp("Mobile App")
weather_station.add_observer(display1)
weather_station.add_observer(display2)
weather_station.add_observer(app1)
# 模擬天氣資料變化
weather_station.set_weather_data(25.5, 60, 1013.2)
weather_station.set_weather_data(26.0, 58, 1012.8)
# 移除觀察者並模擬進一步的天氣資料變化
weather_station.remove_observer(display2)
weather_station.set_weather_data(27.2, 55, 1012.5)
if __name__ == "__main__":
main()
程式碼解析:
- 觀察者介面定義:
Observer類別定義了update方法,作為觀察者更新狀態的介面。 - 主題類別實作:
WeatherStation類別維護觀察者列表,並在天氣資料變化時通知所有註冊的觀察者。 - 觀察者類別實作:
DisplayDevice和WeatherApp類別是具體的觀察者,它們在收到通知時更新自己的狀態並列印相關資訊。 - 主函式測試:
main函式建立了天氣站和多個觀察者,演示了觀察者模式的工作流程。
狀態模式(State Pattern)簡介
狀態模式是一種行為設計模式,用於管理物件的狀態轉換。它允許物件在不同的狀態下表現出不同的行為。
狀態模式的核心概念
- 狀態機:狀態模式根據狀態機的概念,狀態機由狀態和轉換組成。狀態代表物件當前的狀況,而轉換則定義了狀態之間的切換規則。
- 狀態轉換:當物件接收到特定的事件或條件時,會觸發狀態轉換,從一個狀態切換到另一個狀態。
狀態模式的應用場景
- 販賣機:販賣機根據投入的金額和選擇的商品,處於不同的狀態,如待機、選擇商品、交付商品等。
- 交通燈:交通燈根據定時器或感測器的訊號,在紅、黃、綠三種狀態之間切換。
- 遊戲狀態:遊戲中的角色或系統可能處於不同的狀態,如開始、進行中、暫停、結束等。
狀態模式(State Pattern)實務應用與實作解析
狀態模式是一種常見的行為設計模式,主要用於實作有限狀態機(Finite State Machine, FSM)。它允許物件在內部狀態改變時改變其行為,使得程式碼更具彈性與可維護性。
狀態模式的適用場景
任何可以使用狀態機解決的問題都是狀態模式的適用場景。典型的例子包括:
- 作業系統或嵌入式系統的流程控制:不同狀態之間的轉換可以用狀態模式來管理。
- 程式語言編譯器的實作:詞法分析和語法分析可以使用狀態機來建立抽象語法樹(Abstract Syntax Tree, AST)。
- 事件驅動系統:狀態之間的轉換可以觸發事件或訊息,在電腦遊戲中特別常見。例如,當主角接近怪物時,怪物的狀態可以從「警戒」轉換為「攻擊」。
如同 Thomas Jaeger 在其文章中所說,狀態設計模式允許對上下文中的無限多個狀態進行完全封裝,從而實作了易於維護和靈活性。
狀態模式的實作
以下將展示如何使用 Python 的 state_machine 模組來建立一個根據狀態圖的狀態機,涵蓋一個流程的不同狀態及其轉換。
1. 定義狀態與轉換
首先,使用 @acts_as_state_machine 裝飾器定義 Process 類別,並定義其狀態:
@acts_as_state_machine
class Process:
created = State(initial=True)
waiting = State()
running = State()
terminated = State()
blocked = State()
swapped_out_waiting = State()
swapped_out_blocked = State()
接著,定義狀態之間的轉換事件:
wait = Event(from_states=(created, running, blocked, swapped_out_waiting), to_state=waiting)
run = Event(from_states=waiting, to_state=running)
terminate = Event(from_states=running, to_state=terminated)
block = Event(from_states=(running, swapped_out_blocked), to_state=blocked)
swap_wait = Event(from_states=waiting, to_state=swapped_out_waiting)
swap_block = Event(from_states=blocked, to_state=swapped_out_blocked)
#### 內容解密:
State類別用於定義不同的狀態,其中initial=True表示初始狀態。Event類別用於定義狀態轉換事件,透過from_states和to_state指定轉換的來源和目標狀態。- 狀態和事件的定義是根據實際需求進行的,例如流程的不同階段需要不同的狀態表示。
2. 新增流程資訊與事件處理
為每個流程新增名稱,並在事件發生前後執行特定動作:
def __init__(self, name):
self.name = name
@after("wait")
def wait_info(self):
print(f"{self.name} entered waiting mode")
@after("run")
def run_info(self):
print(f"{self.name} is running")
@before("terminate")
def terminate_info(self):
print(f"{self.name} terminated")
@after("block")
def block_info(self):
print(f"{self.name} is blocked")
@after("swap_wait")
def swap_wait_info(self):
print(f"{self.name} is swapped out and waiting")
@after("swap_block")
def swap_block_info(self):
print(f"{self.name} is swapped out and blocked")
#### 內容解密:
- 使用
@after和@before裝飾器,可以在事件發生前後執行特定的動作,例如輸出流程狀態變更的資訊。 - 這樣的設計使得程式碼更具可讀性和可維護性,能夠清晰地表達不同事件對應的處理邏輯。
3. 狀態轉換與查詢功能實作
實作 transition 函式來執行狀態轉換,並處理可能的錯誤:
def transition(proc, event, event_name):
try:
event()
except InvalidStateTransition:
msg = f"Transition of {proc.name} from {proc.current_state} to {event_name} failed"
print(msg)
同時,提供 state_info 函式來查詢當前流程的狀態:
def state_info(proc):
print(f"state of {proc.name}: {proc.current_state}")
#### 內容解密:
transition函式負責執行特定的事件,如果發生無效的狀態轉換,則捕捉異常並輸出錯誤訊息。state_info函式簡單輸出當前流程的名稱及其所處的狀態,方便進行除錯和狀態監控。
主程式範例與輸出結果
在 main 函式中,建立多個流程例項並進行狀態查詢和轉換:
def main():
p1, p2 = Process("process1"), Process("process2")
[state_info(p) for p in (p1, p2)]
# 繼續進行其他操作...
#### 內容解密:
- 建立兩個
Process例項,並查詢它們的初始狀態。 - 後續可以根據需求呼叫
transition函式來執行不同的事件,從而改變流程的狀態。
行為設計模式詳解:狀態模式與直譯器模式
行為設計模式是軟體設計模式中的一種類別,主要關注物件之間的互動和行為分配。本篇文章將探討兩種重要的行為設計模式:狀態模式(State Pattern)與直譯器模式(Interpreter Pattern)。
狀態模式(State Pattern)
狀態模式是一種允許物件在其內部狀態改變時改變其行為的設計模式。該模式主要用於當物件需要根據其當前狀態執行不同的操作時。
狀態模式的實際應用
考慮一個處理程式(Process)的例子。處理程式在其生命週期中會經歷多種狀態,如建立(CREATED)、執行(RUNNING)、等待(WAITING)、阻塞(BLOCKED)和終止(TERMINATED)。使用狀態模式,可以有效地管理這些狀態之間的轉換。
from state_machine import StateMachine, State
class Process:
created = State('created')
waiting = State('waiting')
running = State('running')
blocked = State('blocked')
terminated = State('terminated')
def __init__(self):
self.machine = StateMachine(self.created)
self.machine.add_transition(self.waiting, self.created, self.running)
self.machine.add_transition(self.running, self.waiting, self.blocked)
# 更多狀態轉換...
def wait(self):
self.machine.wait()
def run(self):
self.machine.run()
def block(self):
self.machine.block()
def terminate(self):
self.machine.terminate()
def state_info(process):
return f'state of process: {process.machine.current_state.name}'
# 建立兩個處理程式例項
p1 = Process()
p2 = Process()
# 進行狀態轉換並輸出當前狀態
print(state_info(p1))
print(state_info(p2))
# 嘗試不同的狀態轉換
p1.wait()
p2.terminate()
print(state_info(p1))
print(state_info(p2))
內容解密:
- 狀態定義:首先定義了處理程式的各個狀態,如
created、waiting、running、blocked和terminated。 - 狀態機初始化:在
Process類別的初始化方法中,建立了一個StateMachine例項,並設定初始狀態為created。 - 狀態轉換:透過
add_transition方法定義了允許的狀態轉換,例如從created到waiting或從running到blocked。 - 狀態操作方法:定義瞭如
wait、run、block和terminate等方法來觸發狀態轉換。 - 輸出當前狀態:
state_info函式用於輸出處理程式的當前狀態。
直譯器模式(Interpreter Pattern)
直譯器模式是一種用於實作特定領域語言(Domain-Specific Language, DSL)的設計模式。該模式涉及對 DSL 陳述式的解析和執行。
直譯器模式的實際應用
考慮一個音樂家解釋音樂符號的例子。音樂符號代表了聲音的音高和時長,音樂家可以根據這些符號準確地再現聲音。
在軟體開發中,直譯器模式可用於實作內部 DSL。例如,Python 中的 PyT 是一個用於生成 XHTML/HTML 的內部 DSL。
直譯器模式的使用場景
當需要為領域專家或高階使用者提供一種簡單的語言來解決問題時,可以使用直譯器模式。但是,直譯器模式僅適用於實作簡單的語言。如果語言需要外部 DSL 的特性,則有更好的工具可用(如 Yacc 和 Lex、Bison、ANTLR 等)。