Go言語でChain of Responsibilityパターン


はじめに

デザインパターンの1種であるChain of Responsibilityパターンの例をGo言語で書いてみました。

Chain of Responsibilityパターンとは

こちらより

このパターンは, ある要求の受け取り対象となる複数のオブジェクトに鎖状の関係を構築し, 要求を処理することが可能なオブジェクトに渡るまで, 順次, 構築した鎖状の関係に沿って要求を受流していくパターン.
このパターンを適用すると, [この要求はこのオブジェクトが処理する] などという司令塔的な役割 (結びつき) を利用者側が意識しなくて良くなり, 利用者側は [連鎖関係にある任意のオブジェクトに要求を投げるだけ], 処理側は [流れてきた要求が自身で処理できる場合は処理し, できない場合は, その要求を次のオブジェクトに渡すだけ] という役割分担が明確となる. (利用者側は, 処理の詳細まで意識する必要はない)

サンプルコード

Chain of Responsibilityパターンの登場人物

  • Handler(Approver)
    • リクエストを処理するためのインターフェースを定義する
  • ConcreteHandler (Director, VicePresident, President)
    • 責任範囲のリクエスト処理をする
    • 後続のHandler(successor)へアクセス可能
    • 自身で処理できるならば処理し、そうでない場合は後続へ転送する
  • Client (Main)
    • 先頭のHandlerへリクエストを渡す

コード

下記リンクでも確認することができます。
The Go Playground

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    // Chain of Responsibilityのセット
    firstApprover := CreateChain()
    // リクエストのセット
    requests := []int{2, 5, 14, 32, 18, 3, 27, 20}

    fmt.Println("複数の承認フローを開始します。")
    var wg sync.WaitGroup
    for _, req := range requests {
        wg.Add(1)
        go func(req int) {
            defer wg.Done()
            ok, reason := firstApprover.ProcessRequest(req) // Directorを先頭に承認フローを開始
            if !ok {
                fmt.Printf("Request %d was rejected becasue %s.\n", req, reason.Error())
            } else {
                fmt.Printf("Request %d was approved.\n", req)
            }
        }(req)
    }
    wg.Wait()
    fmt.Println("すべての承認フローが終了しました。")
}

// CreateChainは処理チェーンを構築し、利用者がコールする先頭の承認者を返します
func CreateChain() Approver {
    president := NewPresident(nil)
    vicePresident := NewVicePresident(president)
    director := NewDirector(vicePresident)
    return director
}

type Approver interface {
    ProcessRequest(req int) (bool, error)
}

// Directorは1番目の承認者です
type Director struct {
    successor Approver
}

func NewDirector(s Approver) *Director {
    return &Director{successor: s}
}

func (a *Director) ProcessRequest(req int) (bool, error) {
    if req < 10 {
        return false, errors.New("Director rejected the request")
    } else if a.successor != nil {
        return a.successor.ProcessRequest(req)
    }
    return true, nil
}

// VicePresidentは2番目の承認者です
type VicePresident struct {
    successor Approver
}

func NewVicePresident(s Approver) *VicePresident {
    return &VicePresident{successor: s}
}

func (a *VicePresident) ProcessRequest(req int) (bool, error) {
    if req < 20 {
        return false, errors.New("VicePresident rejected the request")
    } else if a.successor != nil {
        return a.successor.ProcessRequest(req)
    }
    return true, nil
}

// Presidentは3番目の承認者です
type President struct {
    successor Approver
}

func NewPresident(s Approver) *President {
    return &President{successor: s}
}

func (a *President) ProcessRequest(req int) (bool, error) {
    if req < 30 {
        return false, errors.New("President rejected the request")
    } else if a.successor != nil {
        return a.successor.ProcessRequest(req)
    }
    return true, nil
}

実行結果

複数の承認フローを開始します。
Request 20 was rejected becasue President rejected the request.
Request 2 was rejected becasue Director rejected the request.
Request 5 was rejected becasue Director rejected the request.
Request 14 was rejected becasue VicePresident rejected the request.
Request 32 was approved.
Request 18 was rejected becasue VicePresident rejected the request.
Request 3 was rejected becasue Director rejected the request.
Request 27 was rejected becasue President rejected the request.
すべての承認フローが終了しました。

参考