缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之

委派模式

委派模式(Delegate Pattern)又叫作委托模式,基本作用就是负责任务的调用和分配,是一种特殊的静态代理模式,可以理解为全权代理模式,但是代理模式注重过程,而委派模式注重结果。
委派模式有3个参与角色。
(1)抽象任务角色(ITask):定义一个抽象接口,它有若干实现类。
(2)委派者角色(Delegate):负责在各个具体角色实例之间做出决策,判断并调用具体实现的方法。
(3)具体任务角色(Concrete):真正执行任务的角色。
_

场景

  • 某消息处理系统, 需要处理客户端请求的各种消息
  • 为方便后续扩展统一的消息日志/审计/权限/安全等功能, 根据委派模式, 所有消息由全局调度器统一调度
  • 调度器根据消息的类型, 委派给具体的消息处理器

设计

  • IMsg: 定义消息接口
  • BaseMsg: 消息的基类, 实现IMsg接口
  • EchoMsg: 表示原样返回的消息, 用于PING/PONG心跳. 继承自BaseMsg
  • TimeMsg: 表示获取服务器时间的消息. 继承自BaseMsg
  • IMsgHandler: 消息处理器接口. 调度器和具体消息处理器, 均需要实现此接口.
  • tMsgDispatchDelegate: 全局消息调度器, 是所有客户端消息的统一入口. 用于注册消息处理器, 按类型分发消息.
  • tEchoMsgHandler: 专门处理EchoMsg消息的处理器. 实现IMsgHandler接口.
  • tTimeMsgHandler: 专门处理TimeMsg消息的处理器, 实现IMsgHandler接口.

单元测试

delegate_pattern_test.go

  1. package behavioral_patterns_test
  2. import (
  3. "fmt"
  4. "learning/gooop/behavioral_patterns/delegate"
  5. "testing"
  6. )
  7. func Test_DelegatePattern(t *testing.T) {
  8. dispatcher := delegate.GlobalMsgDispatcher
  9. vEchoMsg := delegate.NewEchoMsg("msg-1", "this is an echo msg")
  10. response := dispatcher.Handle(vEchoMsg)
  11. fmt.Printf(" echo response: id=%v, cls=%v, content=%v\n", response.ID(), response.Class(), response.Content())
  12. vTimeMsg := delegate.NewTimeMsg("msg-2")
  13. response = dispatcher.Handle(vTimeMsg)
  14. fmt.Printf(" time response: id=%v, cls=%v, content=%v\n", response.ID(), response.Class(), response.Content())
  15. }

测试输出

  1. $ go test -v delegate_pattern_test.go
  2. === RUN Test_DelegatePattern
  3. tMsgDispatchDelegate.Handle, handler=, id=msg-1, cls=EchoMsg
  4. tEchoMsgHandler.Handle, id=msg-1, cls=EchoMsg
  5. echo response: id=msg-1, cls=EchoMsg, content=this is an echo msg
  6. tMsgDispatchDelegate.Handle, handler=, id=msg-2, cls=TimeMsg
  7. tTimeMsgHandler.Handle, id=msg-2, cls=TimeMsg
  8. time response: id=msg-2, cls=TimeMsg, content=2021-02-05T09:18:45
  9. --- PASS: Test_DelegatePattern (0.00s)
  10. PASS
  11. ok command-line-arguments 0.002s

IMsg.go

定义消息接口

  1. package delegate
  2. type IMsg interface {
  3. ID() string
  4. Class() string
  5. Content() string
  6. }

BaseMsg.go

消息的基类, 实现IMsg接口

  1. package delegate
  2. type BaseMsg struct {
  3. sID string
  4. sClass string
  5. sContent string
  6. }
  7. func NewBaseMsg(id string, cls string, content string) *BaseMsg {
  8. return &BaseMsg{
  9. id, cls, content,
  10. }
  11. }
  12. func (me *BaseMsg) ID() string {
  13. return me.sID
  14. }
  15. func (me *BaseMsg) Class() string {
  16. return me.sClass
  17. }
  18. func (me *BaseMsg) Content() string {
  19. return me.sContent
  20. }

EchoMsg.go

表示原样返回的消息, 用于PING/PONG心跳. 继承自BaseMsg

  1. package delegate
  2. type EchoMsg struct {
  3. BaseMsg
  4. }
  5. func NewEchoMsg(id string, content string) *EchoMsg {
  6. return &EchoMsg{
  7. *NewBaseMsg(id, "EchoMsg", content),
  8. }
  9. }

TimeMsg.go

表示获取服务器时间的消息. 继承自BaseMsg

  1. package delegate
  2. import "time"
  3. type TimeMsg struct {
  4. BaseMsg
  5. }
  6. func NewTimeMsg(id string) *TimeMsg {
  7. return &TimeMsg{
  8. *NewBaseMsg(id, "TimeMsg", time.Now().Format("2006-01-02T15:04:05")),
  9. }
  10. }

IMsgHandler.go

消息处理器接口. 调度器和具体消息处理器, 均需要实现此接口.

  1. package delegate
  2. type IMsgHandler interface {
  3. Handle(request IMsg) IMsg
  4. }

tMsgDispatchDelegate.go

全局消息调度器, 是所有客户端消息的统一入口. 用于注册消息处理器, 按类型分发消息. 实现IMsgHandler接口.

  1. package delegate
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type tMsgDispatchDelegate struct {
  7. mSubHandlers map[string]IMsgHandler
  8. }
  9. func (me *tMsgDispatchDelegate) Register(cls string, handler IMsgHandler) {
  10. me.mSubHandlers[cls] = handler
  11. }
  12. func newMsgDispatchDelegate() IMsgHandler {
  13. it := &tMsgDispatchDelegate{
  14. mSubHandlers: make(map[string]IMsgHandler, 16),
  15. }
  16. it.Register("EchoMsg", newEchoMsgHandler())
  17. it.Register("TimeMsg", newTimeMsgHandler())
  18. return it
  19. }
  20. func (me *tMsgDispatchDelegate) Handle(request IMsg) IMsg {
  21. if request == nil {
  22. return nil
  23. }
  24. handler, ok := me.mSubHandlers[request.Class()]
  25. if !ok {
  26. fmt.Printf("tMsgDispatchDelegate.Handle, handler not found: id=%v, cls=%v\n", request.ID(), request.Class())
  27. return nil
  28. }
  29. fmt.Printf("tMsgDispatchDelegate.Handle, handler=%v, id=%v, cls=%v\n", reflect.TypeOf(handler).Name(), request.ID(), request.Class())
  30. return handler.Handle(request)
  31. }
  32. var GlobalMsgDispatcher = newMsgDispatchDelegate()

tEchoMsgHandler.go

专门处理EchoMsg消息的处理器. 实现IMsgHandler接口.

  1. package delegate
  2. import "fmt"
  3. type tEchoMsgHandler struct {
  4. }
  5. func newEchoMsgHandler() IMsgHandler {
  6. return &tEchoMsgHandler{}
  7. }
  8. func (me *tEchoMsgHandler) Handle(request IMsg) IMsg {
  9. fmt.Printf(" tEchoMsgHandler.Handle, id=%v, cls=%v\n", request.ID(), request.Class())
  10. return request
  11. }

tTimeMsgHandler.go

专门处理TimeMsg消息的处理器, 实现IMsgHandler接口.

  1. package delegate
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. type tTimeMsgHandler struct {
  7. }
  8. func newTimeMsgHandler() IMsgHandler {
  9. return &tTimeMsgHandler{}
  10. }
  11. func (me *tTimeMsgHandler) Handle(request IMsg) IMsg {
  12. fmt.Printf(" tTimeMsgHandler.Handle, id=%v, cls=%v\n", request.ID(), request.Class())
  13. timeMsg := request.(*TimeMsg)
  14. timeMsg.sContent = time.Now().Format("2006-01-02T15:04:05")
  15. return timeMsg
  16. }

委派模式小结

委派模式的优点
通过任务委派能够将一个大型任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,加快任务执行的效率。
委派模式的缺点
任务委派方式需要根据任务的复杂程度进行不同的改变,在任务比较复杂的情况下,可能需要进行多重委派,容易造成紊乱。
_
(end)