saeki’s blog

The limits of my code mean the limits of my world.

GoFデザインパターン ~Template Methodパターン~

こちらを読みながら進める。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

Template Methodパターンとは

抽象クラスで処理の枠組みを決め、実処理は具象クラスで行うというのがTemplate Methodパターン。下図でいうと、抽象クラスであるTemplateのexec()でmethodA(), methodB(), methodC()を利用するが、その具体的な実装はConcrete1, Concrete2クラスの責務となる。骨組みは抽象クラスで、肉付けは具象クラスでというイメージ。

f:id:t_saeki:20190702194943p:plain

実装

増補改訂版Java言語で学ぶデザインパターン入門で使われてる例をGoで実装してみる。
Template Methodパターンは抽象クラスを利用するが、Goには継承が存在しない(埋め込みで継承っぽいこともできるが、親構造体から子構造体のメソッドを呼べないなど厳密には継承とはいえない)ため、Javaなどの言語とは異なる実装になる。

ディレクトリ構成は以下

./3_template_method
├── main.go
└── printer
    ├── abstract_printer.go
    ├── char_display.go
    ├── printer.go
    ├── string_display.go

抽象クラス

package printer

type AbstractPrinter struct {
}

func (*AbstractPrinter) Display(printer Printer) string {
    result := printer.Open()
    result += printer.Print()
    result += printer.Close()
    return result
}

テンプレートメソッドである Display()の引数でPrinterインターフェース(を実装した子クラスのインスタンス)を受け取る。
継承は行なっていないが、こうすることで骨組みであるAbstractPrinterクラスのテンプレートメソッド(Display())で具象クラスのメソッドを呼ぶことができる。

具象クラス

具象クラスのインターフェース。継承のある言語では継承の機能を使って子クラスの実装を担保できるが、Goではインターフェースによって具象クラスの実装を担保する。
具象クラスに要求されるのは open() print() close() の実装。

package printer

type Printer interface {
    Open() string
    Print() string
    Close() string
}

AbstractPrinterの具象クラスであるCharDisplay, StringDisplay

package printer

type charDisplay struct {
    char string
}

func NewCharDisplay(char string) Printer {
    return &charDisplay{char: char}
}

func (*charDisplay) Open() string {
    return "<<"
}

func (cd *charDisplay) Print() string {
    return cd.char
}

func (*charDisplay) Close() string {
    return ">>"
}
package printer

type stringDisplay struct {
    char string
}

func NewStringDisplay(char string) Printer {
    return &stringDisplay{char: char}
}

func (*stringDisplay) Open() string {
    return "+--+"
}

func (cd *stringDisplay) Print() string {
    return "|" + cd.char + "|"
}

func (*stringDisplay) Close() string {
    return "+--+"
}

Main

Main実装

package main

import (
    "fmt"

    "github.com/saekis/go-design-pattern/3_template_method/printer"
)

func main() {
    ad := printer.AbstractPrinter{}
    fmt.Println(ad.Display(printer.NewCharDisplay("char_display")))
    fmt.Println(ad.Display(printer.NewStringDisplay("string_display")))
}

実行結果

$ go run main.go 
<<char_display>>
+--+|string_display|+--+

クラス図

f:id:t_saeki:20190709150210p:plain

実装したもの

github.com