LoginSignup
1

More than 3 years have passed since last update.

Golangで、デザインパターン「Mediator」を学ぶ

Last updated at Posted at 2020-03-29

GoFのデザインパターンをGolangで学習してみたいと思います。
今回は、Qiita記事: "Pythonで、デザインパターン「Mediator」を学ぶ"で取り上げた、Pythonベースの”Mediator”のサンプルアプリをGolangで実装し直してみました。

■ Mediator(メディエーター・パターン)

「Mediator」という英単語は、「仲介者」を意味します。
このパターンは、複雑に絡み合った複数のオブジェクト間の関係を、必ず「仲介者」を介して処理を行う様にすることで単純かつ明快なインタフェースを提供するパターンです。つまり、「Mediator」パターンとは、管轄下にある複数のオブジェクト各々からの問い合わせを受け、適宜判断を行い、管轄下にあるオブジェクト全体、または一部へ指示を出す「仲介人」の役割を果たすクラスを利用するパターンです。

UML class and sequence diagram

W3sDesign_Mediator_Design_Pattern_UML.jpg

UML class diagram

designpattern-mediator01.gif
(以上、「ITエンジニアのための技術支援サイト by IT専科」より引用)

■ "Mediator"のサンプルプログラム

実際に、Mediatorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。ここでは、"なんちゃって、ユーザ認証画面"を想像してください。

  • ユーザ名"hoge", パスワード"fuga"のユーザが存在するとする
  • ユーザ名とパスワードが入力されると、"login button"がアクティブになる
  • "login button"をクリックして、ユーザ認証が成功したか、どうか判定する

なお、サンプルプログラムでは、第一引数:ユーザ名、第二引数:パスワードを指定することにより、"なんちゃって、ユーザ認証画面"に、ユーザ名とパスワードを入力したものとします。

(事例1) 誤ったパスワードを指定して、ユーザ認証が失敗する

ユーザ名は正しく入力されたので、"login button"が有効になりましたが、ユーザ認証は失敗しました。

$ go run Main.go hoge huga
(Active login button)
(ID/PW is incorrect)
Login Failed!!

(事例2) パスワードが未指定だったので、ユーザ認証が失敗する

"login button"が有効ならずに、ユーザ認証は失敗しました。

$ go run Main.go hoge
Login Failed!!

(事例3) 正しいユーザ名, パスワードを指定して、ユーザ認証が成功する

ユーザ名は正しく入力されたので、"login button"が有効になり、ユーザ認証も成功しました。

$ go run Main.go hoge fuga
(Active login button)
(ID/PW is confirmed)
Login Succeed!!

以上で、想定どおり、サンプリプログラムが動作しました。

■ サンプルプログラムの詳細

Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Mediator

  • ディレクトリ構成
.
├── Main.go
└── mediator
    ├── colleague.go
    └── mediator.go

(1) Mediator(調停者、仲介者)の役

Mediator役は、Colleague役と通信を行なって、調整を行うためのインタフェースを定めます。
サンプルプログラムでは、Mediatorインタフェースが、この役を努めます。

mediator/mediator.go
package mediator

import "fmt"

// Mediator is interface
type Mediator interface {
    onChange(component *colleague)
    SetColleagues(inputIDObj, inputPwObj *ConcreteColleagueTextArea, buttonObj *ConcreteColleagueButton)
    getAuthentication() bool
}

(2) ConcreteMediator(具体的な調停者、仲介者)の役

ConcreteMediator役は、Mediator役のインタフェースを実装し、実際の調整を行います。
サンプルプログラムでは、ConcreteMediator構造体が、この役を努めます。

mediator/mediator.go
// ConcreteMediator is struct
type ConcreteMediator struct {
    authentication         bool
    inputIDObj, inputPwObj *ConcreteColleagueTextArea
    buttonObj              *ConcreteColleagueButton
}

// NewConcreteMediator func for initializing ConcreteMediator
func NewConcreteMediator() Mediator {
    return &ConcreteMediator{
        authentication: false,
    }
}

// SetColleagues func for setting Objects
func (c *ConcreteMediator) SetColleagues(inputIDObj, inputPwObj *ConcreteColleagueTextArea, buttonObj *ConcreteColleagueButton) {
    c.inputIDObj = inputIDObj
    c.inputPwObj = inputPwObj
    c.buttonObj = buttonObj
}

func (c *ConcreteMediator) onChange(component *colleague) {
    if component.name == "ID" || component.name == "PW" {
        c.refreshButton()
    } else if component.name == "Login" {
        c.tryAuthentication()
    }
}

func (c *ConcreteMediator) refreshButton() {
    if c.inputIDObj.text != "" && c.inputPwObj.text != "" {
        fmt.Println("(Active login button)")
        c.buttonObj.active = true
    }
}

func (c *ConcreteMediator) tryAuthentication() {
    if c.inputIDObj.text == "hoge" && c.inputPwObj.text == "fuga" {
        fmt.Println("(ID/PW is confirmed)")
        c.authentication = true
    } else {
        fmt.Println("(ID/PW is incorrect)")
    }
}

func (c *ConcreteMediator) getAuthentication() bool {
    return c.authentication
}

(3) Colleague(同僚)の役

Colleague役は、Mediator役と通信を行うインタフェースとを紐付けます。
サンプルプログラムでは、colleague構造体が、この役を努めます。

mediator/colleague.go
package mediator

type colleague struct {
    mediator Mediator
    name     string
}

func (c *colleague) onChange() {
    if c.mediator != nil {
        c.mediator.onChange(c)
    }
}

(4) ConcreteColleague(具体的な同僚)の役

ConcreteColleague役は、Colleague役と紐付けます。
サンプルプログラムでは、ConcreteColleagueButton構造体と、ConcreteColleagueTextArea構造体が、この役を努めます。

mediator/colleague.go
// ConcreteColleagueButton is struct
type ConcreteColleagueButton struct {
    *colleague
    active bool
}

// NewConcreteColleagueButton func for initializing ConcreteColleagueButton
func NewConcreteColleagueButton(mediatorObj Mediator, name string) *ConcreteColleagueButton {
    return &ConcreteColleagueButton{
        colleague: &colleague{
            mediator: mediatorObj,
            name:     name},
        active: false,
    }
}

// ClickButton func for detecting whether button is active or not
func (c *ConcreteColleagueButton) ClickButton() bool {
    if c.active {
        c.onChange()
    }
    return c.mediator.getAuthentication()
}

// CheckButtonStatus func for detecting whether button is active or not
func (c *ConcreteColleagueButton) CheckButtonStatus() bool {
    return c.active
}
mediator/colleague.go
// ConcreteColleagueTextArea is struct
type ConcreteColleagueTextArea struct {
    *colleague
    text string
}

// NewConcreteColleagueTextArea func for initializing ConcreteColleagueTextArea
func NewConcreteColleagueTextArea(mediatorObj Mediator, name string) *ConcreteColleagueTextArea {
    return &ConcreteColleagueTextArea{
        colleague: &colleague{
            mediator: mediatorObj,
            name:     name},
        text: "",
    }
}

// InputText func for putting text
func (c *ConcreteColleagueTextArea) InputText(text string) {
    c.text = text
    c.onChange()
}

(5) Client(依頼人)の役

サンプルプログラムでは、startMain関数が、この役を努めます。

Main.go
package main

import (
    "fmt"
    "os"

    "./mediator"
)

func startMain(userid, password string) {
    m := mediator.NewConcreteMediator()
    inputIDObj := mediator.NewConcreteColleagueTextArea(m, "ID")
    inputPwObj := mediator.NewConcreteColleagueTextArea(m, "PW")
    pushButtonObj := mediator.NewConcreteColleagueButton(m, "Login")
    m.SetColleagues(inputIDObj, inputPwObj, pushButtonObj)

    inputIDObj.InputText(userid)
    inputPwObj.InputText(password)
    if pushButtonObj.ClickButton() {
        fmt.Println("Login Succeed!!")
    } else {
        fmt.Println("Login Failed!!")
    }
}

func checkInputData(params []string) (userid, password string) {
    if len(params) == 3 {
        userid = params[1]
        password = params[2]
    } else if len(params) == 2 {
        userid = params[1]
        password = ""
    } else if len(params) == 1 {
        userid = ""
        password = ""
    }
    return userid, password
}

func main() {
    userid, password := checkInputData(os.Args)
    startMain(userid, password)
}

■ 参考URL

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1