Command Pattern
指令模式将指令包裹在對象中,并傳給調用對象。調用對象尋找處理該指令的合适對象,并把該指令傳給合适的對象,該對象執行指令。
例子
舉一個例子,在go裡邊調用C語言的代碼,來監聽鍵盤的輸入,并調用與輸入綁定的函數。
定義事件碼和事件結構。
const (
EVENT_CODE_KEY = iota
)
type EventCode rune
type EventData interface{}
type Event struct{
Code EventCode
Data EventData
}
為了簡單,隻寫一個按鍵的事件。
定義一個抽象的接口。
type Command interface {
Execute(EventData)
}
這個接口定義了指令的形式。
我們想讓使用者可以在外部傳入回調函數,那麼就需要定義一個接收事件的回調函數。
type EventFunc func(Event)
有了回調函數形式,我們還需要一個對象,包含這種函數。這種函數不是Command接口對象。但我們可以把它包裝成Command對象,這裡就用到了擴充卡模式。
type EventFunder struct {
f EventFunc
}
func (this EventFunder) Execute(data Event) {
this.f(data)
}
func eventFunder(eventFunc EventFunc) EventFunder {
return EventFunder{eventFunc}
}
eventFunder就是把一個EventFunc類型的對象轉換為EventFunder,EventFunder實作了Command接口,它屬于Command。
最後是EventSystem的實作。
type Hash map[EventCode]Command
type EventSystem struct {
hash Hash
}
func (this *EventSystem) Init() *EventSystem {
this.hash = make(Hash)
return this
}
func (this *EventSystem) Map(code EventCode, eventFunc EventFunc) {
this.hash[code] = eventFunder(eventFunc)
}
func (this *EventSystem) InspectKeyboard() {
go func() {
C.init_keyboard()
for {
if C.kbhit() > {
ch := EventCode(C.readch())
// TODO::generate key event
this.generateEvent(Event{EVENT_CODE_KEY, ch})
}
}
}()
}
func (this *EventSystem) generateEvent(e Event) {
v, ok := this.hash[e.Code]
if ok {
v.Execute(e)
}
}
func NewEventSystem() *EventSystem {
return (&EventSystem{}).Init()
}
EventSystem實際上是一個調用者,它隻接收Command對象。是以要把傳入的函數做适配。
優點
- 降低了系統耦合度。
- 新的指令可以很容易添加到系統中去。
缺點
- 使用指令模式可能會導緻某些系統有過多的具體指令類。
注意事項
系統需要支援指令的撤銷(Undo)操作和恢複(Redo)操作,也可以考慮使用指令模式,見指令模式的擴充。