設計模式 in Go: Mediator

行爲模式旨在解決對象間通信和交互相關的問題,專注於定義那些複雜到無法靜態設計的協作策略,這些協作策略使得程序可以在運行時動態地進行職責派遣以實現更好的擴展。

今天我們開始第 7 個行爲模式的學習 —— Mediator(中介者模式)。

問題背景:

中介模式用於當一組對象需要以鬆散耦合的方式相互通信時。在複雜系統中,對象之間的直接通信會導致緊耦合和增加複雜性。中介模式有助於集中和控制對象間的通信,減少依賴關係,使系統更易於維護。

解決方案:

中介模式引入了一個封裝一組對象之間通信邏輯的中介對象。對象不再直接相互通信,而是通過中介進行通信。這降低了對象間的耦合,並使系統的修改和擴展更加容易。

中介充當中央樞紐,接收來自對象的消息並將其分發給適當的接收者。它瞭解各個對象及其職責,從而能夠協調它們的交互。這樣,對象無需直接瞭解彼此,減少了依賴關係,並使系統更加靈活。

Fitting Room Mediator 例如,假設你是一名試衣間中介,有許多顧客和有限的試衣間。你需要檢查是否有空閒的試衣間,並告知顧客可以進入或需要等待。如果某個試衣間被佔用超過五分鐘,你還需通知該顧客請儘快一些 :)
Mediator Pattern

示例代碼:

package mediator

import (
"log"
"sync"
"sync/atomic"
"time"
)

// Mediator defines the fitting room mediator behavior
type Mediatorinterface {
// Wait let customer wait untils a fitting room released
 Wait(*Customer) <-chan *FittingRoom

// Enter let customer enter a fitting room
 Enter(*Customer)

// Leave let customer leave a fitting room
 Leave(*Customer)
}

const (
 idle =iota
 reserved
 inused
)

type FittingRoomstruct {
 noint
 stateint
 customer *Customer
 last time.Time
}

// a customer who waits for a fitting room
type waiterstruct {
 *Customer
 chchan *FittingRoom
}

// NewFittingRoomMediator create a mediator who manages `cap` fitting rooms
funcNewFittingRoomMediator(capint) *fittingRoomMediator {
 rooms :=make([]*FittingRoom,cap)
for i :=0; i <cap; i++ {
 rooms[i] = &FittingRoom{
 no: i +1,
 state: idle,
 customer:nil,
 }
 }
 m := &fittingRoomMediator{
 Rooms: rooms,
 WaitingCustomers:make(chan *waiter,64),
 }
return m
}

type fittingRoomMediatorstruct {
 sync.Mutex
 Rooms []*FittingRoom

 WaitingCustomerschan *waiter
}

func(m *fittingRoomMediator) Wait(customer *Customer) <-chan *FittingRoom {
 m.Lock()
defer m.Unlock()

 ch :=make(chan *FittingRoom,1)
for _, room :=range m.Rooms {
if room.state == idle ||
 (room.state == reserved && time.Since(room.last) > time.Minute*5) {
 room.state = reserved
 room.customer = customer
 room.last = time.Now()
 ch <- room
return ch
 }
 }

 m.WaitingCustomers <- &waiter{Customer: customer, ch: ch}
return ch
}

func(m *fittingRoomMediator) Enter(customer *Customer) {
 m.Lock()
defer m.Unlock()

for _, room :=range m.Rooms {
if room.state != reserved {
continue
 }
if room.customer.sequence == customer.sequence {
 room.state = inused
 room.last = time.Now()
return
 }
 }
 log.Printf("%s no reserved room found\n", customer.Name)
}

func(m *fittingRoomMediator) Leave(customer *Customer) {
 m.Lock()
defer m.Unlock()

for _, room :=range m.Rooms {
if room.state != inused {
continue
 }
if room.customer.sequence == customer.sequence {
 room.state = idle
 room.customer =nil
 room.last = time.Now()
 log.Printf("%s will leave room-%d\n", customer.Name, room.no)
// let next waiting customer enter
select {
case waiter := <-m.WaitingCustomers:
 log.Printf("%s will be notified to use room-%d\n", waiter.Name, room.no)
 room.state = reserved
 room.customer = waiter.Customer
 room.last = time.Now()
 waiter.ch <- room
close(waiter.ch)
default:
 }
 }
 }
}

var sequence atomic.Int64

funcNewCustomer(namestring) *Customer {
return &Customer{
 Name: name,
 sequence:int(sequence.Add(1)),
 }
}

type Customerstruct {
 Namestring
 sequenceint
 mediator Mediator
}

func(c *Customer) SetMediator(m Mediator) *Customer {
 c.mediator = m
return c
}

func(c *Customer) Enter() {
 room, ok := <-c.mediator.Wait(c)
if !ok {
 log.Printf("%s wait too long, it's best to quit\n", c.Name)
return
 }
 log.Printf("%s could use reserved room-%d\n", c.Name, room.no)

 c.mediator.Enter(c)
 log.Printf("%s now is using room-%d\n", c.Name, room.no)
}

func(c *Customer) Leave() {
 c.mediator.Leave(c)
 log.Printf("%s has left\n", c.Name)
}

運行以下測試以演示:

package mediator

import (
"testing"
"time"
)

funcTestMediator(t *testing.T) {
 mediator := NewFittingRoomMediator(2)

 c1 := NewCustomer("zhang").SetMediator(mediator)
 c2 := NewCustomer("yang").SetMediator(mediator)
 c3 := NewCustomer("wang").SetMediator(mediator)

 ch :=make(chanint,2)
gofunc() {
 c1.Enter()
 c2.Enter()
 c3.Enter()
 ch <-1
 }()
gofunc() {
 time.Sleep(time.Second *5)
 c1.Leave()
 c2.Leave()
 time.Sleep(time.Second *5)
 c3.Leave()
 ch <-1
 }()
 <-ch
 <-ch
}
Running tool: /usr/local/go/bin/gotest -timeout 30s -run ^TestMediator$ mediator -v -count=1

=== RUN TestMediator
2024/11/14 14:34:10 zhang could use reserved room-1
2024/11/14 14:34:10 zhang now is using room-1
2024/11/14 14:34:10 yang could use reserved room-2
2024/11/14 14:34:10 yang now is using room-2
2024/11/14 14:34:15 zhang will leave room-1
2024/11/14 14:34:15 wang will be notified to use room-1
2024/11/14 14:34:15 zhang has left
2024/11/14 14:34:15 yang will leave room-2
2024/11/14 14:34:15 yang has left
2024/11/14 14:34:15 wang could use reserved room-1
2024/11/14 14:34:15 wang now is using room-1
2024/11/14 14:34:20 wang will leave room-1
2024/11/14 14:34:20 wang has left
--- PASS: TestMediator (10.01s)
PASS
ok mediator 10.009s

ps:此處我們優先考慮可讀性,不會太關注編碼標準,如註釋、camelCase 類型名等。我們將多個文件的代碼組織到一個 codeblock 中僅僅是爲了方便閱讀,如果您想測試可以通過 git 下載源碼 github.com/hitzhangjie/go-patterns。

中介模式的變體:

中介模式有幾種變體,可以根據系統的具體需求來使用:

與其他模式關係:

中介模式可以與以下其他模式相關聯:

本文總結:

中介模式提供了一種集中和控制系統中對象間通信的方式。它促進鬆散耦合,減少依賴關係,並提高系統的可維護性。可以根據系統的具體需求以不同的變體來實現中介模式。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/VlWo5umWKVzKsqhO4ZllFA