Goでデザインパターン (Mediatorパターン)

Goデザインパターン

Mediatorパターンとは

振る舞いに関するデザインパターンの一つ。オブジェクト間の複雑な依存性をMediatorに分離することで複雑さを軽減する。またオブジェクト間の通信を制限し、Mediatorを通した作業を強制することができる。ユーザー登録フォーム等で各コンポーネント間(テキストフィールドやチェックボックス)の状態管理に利用される。

長所

  • Component間の通信に対する責任を分離することができる。(単一責任の原則)
  • 新規Mediatorの追加に対してクライアント側処理の変更が必要ない。(開放閉鎖の原則)
  • Component間の依存を軽減できる。
  • 個々のComponentの再利用が容易になる。
    (Mediator未導入の場合はComponent内に他Componentとの通信処理が必要)

短所

  • Component間の依存性を一か所で管理する都合上、Mediatorが神オブジェクトになる可能性がある。

利用場面

  • 他Componentへの依存度が高く、再利用が難しい場合。
  • Component間の違う関係性のためにサブクラスを必要とする場合。
    (チェックボックスの選択状態に依存していたのを、テキストボックスの入力状況に依存するようにしたい等)

クラス図

Mediatorパターン
  • Mediator
    様々なイベントに関して、ComponentからMediatorへ通知を行うために使用する各種メソッドを宣言する。
    通常は通知メソッド一つを宣言する。
  • Concrete Mediator
    各Component間の関係を隠蔽し、調整を行うメソッドを実装する。各種イベントに対するComponentの状態更新等の処理を行う。
  • Component
    MediatorとMediatorインターフェースを通して通信を行うメソッドを宣言する。
    Meditatorインターフェースに対してのみ依存するため、多種のConcrete Mediatorと利用可能。
  • Concrete Components
    Componentインターフェースで宣言されたインターフェースを実装する。
    各Component間は全く通信を行わない。Component間の関係はすべてConcrete Mediator内で実装される。

実装例

Mediator

package main

type Mediator interface {
	canRequest(IRequest) bool
	notifyRequestFinished()  // Component役からMediator役へ処理の完了を通知するためのメソッド。
}

Concrete Mediator

package main

// Mediatorインターフェースを実装する。
// リクエスト処理が同時に走らないように制御を行う。
type Manager struct {
	isRequesting bool
	queue        []IRequest
}

func NewManger() *Manager {
	return &Manager{}
}

func (m *Manager) canRequest(r IRequest) bool {
	if m.isRequesting {
		m.queue = append(m.queue, r)
		return false
	}
	m.isRequesting = true
	return true
}

// Componentからの処理完了通知用メソッド。
// キューから次にリクエストを送るComponentを取り出し、リクエスト処理の許可を与える。 
func (m *Manager) notifyRequestFinished() {
	if m.isRequesting {
		m.isRequesting = false
	}
	if len(m.queue) > 0 {
		firstQueue := m.queue[0]
		m.queue = m.queue[1:]
		firstQueue.permitSending()
	}
}

Component

package main

type IRequest interface {
	Send()
	permitSending()  // Mediatorからリクエスト許可をもらうためのメソッド
}

Concrete Component

package main

import (
	"fmt"
	"time"
)

type Request struct {
	url      string
	mediator Mediator  // Mediatorオブジェクトを保持する。
}

func NewRequest(url string, mediator Mediator) *Request {
	return &Request{
		url:      url,
		mediator: mediator,
	}
}

func (r *Request) Send() {
	if !r.mediator.canRequest(r) {
		fmt.Printf("[url: %s] Another request is sending.\n", r.url)
		return
	}
	fmt.Printf("[url: %s] Sending request.\n", r.url)
	time.Sleep(time.Second * 1)
	fmt.Printf("[url: %s] Sending request is finished.\n", r.url)

	// 処理の完了をMediatorに通知する。
	r.mediator.notifyRequestFinished()
}

func (r *Request) permitSending() {
	fmt.Printf("[url: %s] Request sending is permitted.\n", r.url)
	r.Send()
}

動作確認

package main

import (
	"sync"
)

func main() {
	urls := []string{
		"https://www.yahoo.co.jp/",
		"https://www.google.com/",
		"https://github.com/",
	}
	var wg sync.WaitGroup

	manager := NewManger()

	request := func(url string) {
		wg.Add(1)
		defer wg.Done()
		r := NewRequest(url, manager)
		r.Send()
	}

	for _, url := range urls {
		go request(url)
	}
	wg.Wait()
}
>> go run .
[url: https://github.com/] Sending request.
[url: https://www.google.com/] Another request is sending. 
[url: https://www.yahoo.co.jp/] Another request is sending.
[url: https://github.com/] Sending request is finished.
[url: https://www.yahoo.co.jp/] Request sending is permitted.
[url: https://www.yahoo.co.jp/] Sending request.
[url: https://www.yahoo.co.jp/] Sending request is finished.
[url: https://www.google.com/] Request sending is permitted.
[url: https://www.google.com/] Sending request.
[url: https://www.google.com/] Sending request is finished.

コメント

タイトルとURLをコピーしました