Help us understand the problem. What is going on with this article?

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

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

ttsubo
SDN分野に興味があります。 ただいま、golangを勉強中です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away