手擼golang 行爲型設計模式 中介者模式

手擼golang 行爲型設計模式 中介者模式

緣起

最近複習設計模式
拜讀譚勇德的<<設計模式就該這樣學>>
本系列筆記擬採用golang練習之golang

中介者模式

中介者模式(Mediator Pattern)又叫做調解者模式或調停者模式。
用一箇中介對象封裝一系列對象交互,
中介者使各對象不須要顯式地相互做用,
從而使其耦合鬆散,
並且能夠獨立地改變它們之間的交互,
屬於行爲型設計模式。

中介者模式主要適用於如下應用場景。
(1)系統中對象之間存在複雜的引用關係,產生的相互依賴關係結構混亂且難以理解。
(2)交互的公共行爲,若是須要改變行爲,則能夠增長新的中介者類。

(摘自 譚勇德 <<設計模式就該這樣學>>)

場景

  • 某物聯網企業, 研發各類智能家居產品, 並配套手機app以便用戶集中控制
  • 一開始的設計是手機app經過本地局域網的廣播協議, 主動發現/註冊/控制各類智能設備
  • 後來智能設備的種類愈來愈多, 通訊協議多種多樣, 致使手機app須要頻繁升級, 集成過多驅動致使代碼膨脹
  • 研發部門痛定思痛, 決定採用中介者模式從新設計整個系統架構
  • 老架構: app -> 智能設備*N
  • 新架構: app -> 雲中心 -> 智能設備
  • 經過引入"雲中心" 做爲中介, 將app與設備驅動解耦
  • app與雲中心採用RESTFul協議通訊, 極大提高開發運維的效率

設計

  • MockPhoneApp: 虛擬的手機app, 用於跟雲中心通訊, 控制智能設備
  • ICloudMediator: 雲中心面向手機app的接口
  • ICloudCenter: 雲中心面向智能設備的註冊接口
  • ISmartDevice: 智能設備接口
  • tMockCloudMediator: 虛擬的雲中心服務類, 面向手機app實現ICloudMediator接口, 面向智能設備實現ICloudCenter接口
  • tMockSmartLight: 虛擬的智能燈設備, 實現ISmartDevice接口

單元測試

mediator_pattern_test.go設計模式

package behavioral_patterns

import (
    "learning/gooop/behavioral_patterns/mediator"
    "testing"
)

func Test_MediatorPattern(t *testing.T) {
    // 設備註冊
    center := mediator.DefaultCloudCenter
    light := mediator.NewMockSmartLight(1)
    center.Register(light)

    fnCallAndLog := func(fn func() error) {
        e := fn()
        if e != nil {
            t.Log(e)
        }
    }

    // 建立app
    app := mediator.NewMockPhoneApp(mediator.DefaultCloudMediator)

    // 設備控制測試
    fnCallAndLog(func() error {
        return app.LightOpen(1)
    })
    fnCallAndLog(func() error {
        return app.LightSwitchMode(1, 1)
    })
    fnCallAndLog(func() error {
        return app.LightSwitchMode(1, 2)
    })
    fnCallAndLog(func() error {
        return app.LightClose(1)
    })
}

測試輸出

t$ go test -v mediator_pattern_test.go 
=== RUN   Test_MediatorPattern
tMockSmartLight.open, id=1
tMockSmartLight.switchMode, id=1, mode=1
tMockSmartLight.switchMode, id=1, mode=2
tMockSmartLight.close, id=1
--- PASS: Test_MediatorPattern (0.00s)
PASS
ok      command-line-arguments  0.002s

MockPhoneApp.go

虛擬的手機app, 用於跟雲中心通訊, 控制智能設備架構

package mediator

import (
    "errors"
    "fmt"
)

type MockPhoneApp struct {
    mediator ICloudMediator
}

func NewMockPhoneApp(mediator ICloudMediator) *MockPhoneApp {
    return &MockPhoneApp{
        mediator,
    }
}

func (me *MockPhoneApp) LightOpen(id int) error {
    return me.lightCommand(id, "light open")
}

func (me *MockPhoneApp) LightClose(id int) error  {
    return me.lightCommand(id, "light close")
}

func (me *MockPhoneApp) LightSwitchMode(id int, mode int) error {
    return me.lightCommand(id, fmt.Sprintf("light switch_mode %v", mode))
}

func (me *MockPhoneApp) lightCommand(id int, cmd string) error {
    res := me.mediator.Command(id, cmd)
    if res != "OK" {
        return errors.New(res)
    }
    return nil
}

ICloudMediator.go

雲中心面向手機app的接口app

package mediator

type ICloudMediator interface {
    Command(id int, cmd string) string
}

ICloudCenter.go

雲中心面向智能設備的註冊接口運維

package mediator

type ICloudCenter interface {
    Register(dev ISmartDevice)
}

ISmartDevice.go

智能設備接口oop

package mediator


type ISmartDevice interface {
    ID() int
    Command(cmd string) string
}

tMockCloudMediator.go

虛擬的雲中心服務類, 面向手機app實現ICloudMediator接口, 面向智能設備實現ICloudCenter接口單元測試

package mediator

import "sync"

type tMockCloudMediator struct {
    mDevices map[int]ISmartDevice
    mRWMutex *sync.RWMutex
}


func newMockCloudMediator() ICloudMediator {
    return &tMockCloudMediator{
        make(map[int]ISmartDevice),
        new(sync.RWMutex),
    }
}

func (me *tMockCloudMediator) Register(it ISmartDevice) {
    me.mRWMutex.Lock()
    defer me.mRWMutex.Unlock()

    me.mDevices[it.ID()] = it
}

func (me *tMockCloudMediator) Command(id int, cmd string) string {
    me.mRWMutex.RLock()
    defer me.mRWMutex.RUnlock()

    it,ok := me.mDevices[id]
    if !ok {
        return "device not found"
    }
    return it.Command(cmd)
}

var DefaultCloudMediator = newMockCloudMediator()
var DefaultCloudCenter = DefaultCloudMediator.(ICloudCenter)

tMockSmartLight.go

虛擬的智能燈設備, 實現ISmartDevice接口測試

package mediator

import (
    "fmt"
    "strconv"
    "strings"
)

type tMockSmartLight struct {
    id int
}

func NewMockSmartLight(id int) ISmartDevice {
    return &tMockSmartLight{
        id,
    }
}

func (me *tMockSmartLight) ID() int {
    return me.id
}

func (me *tMockSmartLight) Command(cmd string) string {
    if cmd == "light open" {
        e := me.open()
        if e != nil {
            return e.Error()
        }
    } else if cmd == "light close" {
        e := me.close()
        if e != nil {
            return e.Error()
        }
    } else if strings.HasPrefix(cmd, "light switch_mode") {
        args := strings.Split(cmd, " ")
        if len(args) != 3 {
            return "invalid switch command"
        }

        n, e := strconv.Atoi(args[2])
        if e != nil {
            return "invalid mode number"
        }

        e = me.switchMode(n)
        if e != nil {
            return e.Error()
        }

    } else {
        return "unrecognized command"
    }

    return "OK"
}

func (me *tMockSmartLight) open() error {
    fmt.Printf("tMockSmartLight.open, id=%v\n", me.id)
    return nil
}

func (me *tMockSmartLight) close() error {
    fmt.Printf("tMockSmartLight.close, id=%v\n", me.id)
    return nil
}

func (me *tMockSmartLight) switchMode(mode int) error {
    fmt.Printf("tMockSmartLight.switchMode, id=%v, mode=%v\n", me.id, mode)
    return nil
}

中介者模式小結

中介者模式的優勢
(1)減小類間依賴,將多對多依賴轉化成一對多,下降了類間耦合。
(2)類間各司其職,符合迪米特法則。

中介者模式的缺點
中介者模式將本來多個對象直接的相互依賴變成了中介者和多個同事類的依賴關係。
當同事類越多時,中介者就會越臃腫,變得複雜且難以維護。

(摘自 譚勇德 <<設計模式就該這樣學>>)

(end)設計

相關文章
相關標籤/搜索