LoginSignup
5
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-01

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Template Method」を学ぶ"

今回は、Pythonで実装した”Template Method”のサンプルアプリをGolangで実装し直してみました。

■ Template Method(テンプレート・メソッド・パターン)

Template Methodパターン(テンプレート・メソッド・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。「振る舞いに関するパターン」に属する。Template Methodパターンの目的は、ある処理のおおまかなアルゴリズムをあらかじめ決めておいて、そのアルゴリズムの具体的な設計をサブクラスに任せることである。そのため、システムのフレームワークを構築するための手段としてよく活用される。

UML class diagram

templatemethod.png
(以上、ウィキペディア(Wikipedia)より引用)

■ "Template Method"のサンプルプログラム

実際に、Template Methodパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

  • 文字"H"を、5回連続で表示する。さらに前後では、"<<", ">>"で囲って表示する
  • 文字列"Hello, World!"を5回連続で表示する。さらに、枠線で囲って表示する
$ go run Main.go 
<<HHHHH>>

+-------------+
|Hello, World!|
|Hello, World!|
|Hello, World!|
|Hello, World!|
|Hello, World!|
+-------------+

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

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

  • ディレクトリ構成
.
├── Main.go
└── templatemethod
    └── display.go

(1) AbstractClass(抽象クラス)の役

AbstractClass役は、テンプレートメソッドを実装します。また、テンプレートメソッドで使っている抽象メソッドを宣言します。この抽象メソッドは、サブクラスであるConcreteClass役によって実装されます。
サンプルプログラムでは、PrinterインタフェースとAbstractDisplay構造体が、この役を努めます。

templatemethod/display.go
package templateMethod

import "fmt"

// Printer is interface
type Printer interface {
    open()
    print()
    close()
}

// AbstractDisplay is struct
type AbstractDisplay struct {
    printer Printer
}

// Display for printout result
func (a *AbstractDisplay) Display() {
    a.printer.open()
    for i := 0; i < 5; i++ {
        a.printer.print()
    }
    a.printer.close()
}

(2) ConcreteClass(具象クラス)の役

AbstractClass役で定義された抽象クラスを具体的に実装します。ここで実装したメソッドは、AbstractClass役のテンプレートメソッドから呼び出されます。
サンプルプログラムでは、CharDisplay構造体と、StringDisplay構造体が、この役を努めます。

templatemethod/display.go
// CharDisplay is struct
type CharDisplay struct {
    *AbstractDisplay
    ch byte
}

// NewCharDisplay func for initializing CharDisplay
func NewCharDisplay(ch byte) *CharDisplay {
    charDisplay := &CharDisplay{
        AbstractDisplay: &AbstractDisplay{},
        ch:              ch,
    }
    charDisplay.printer = charDisplay
    return charDisplay
}

func (c *CharDisplay) open() {
    fmt.Print("<<")
}
func (c *CharDisplay) print() {
    fmt.Print(string(c.ch))
}
func (c *CharDisplay) close() {
    fmt.Println(">>")
}
templatemethod/display.go
// StringDisplay is struct
type StringDisplay struct {
    *AbstractDisplay
    str   string
    width int
}

// NewStringDisplay func for initalizing StringDisplay
func NewStringDisplay(str string) *StringDisplay {
    stringDisplay := &StringDisplay{
        AbstractDisplay: &AbstractDisplay{},
        str:             str,
        width:           len(str),
    }
    stringDisplay.printer = stringDisplay
    return stringDisplay
}

func (s *StringDisplay) open() {
    s.printLine()
}
func (s *StringDisplay) print() {
    fmt.Printf("%s%s%s\n", "|", s.str, "|")
}
func (s *StringDisplay) close() {
    s.printLine()
}

func (s *StringDisplay) printLine() {
    fmt.Print("+")
    for i := 0; i < s.width; i++ {
        fmt.Print("-")
    }
    fmt.Println("+")
}

(3) Client(利用者)の役

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

Main.go
package main

import (
    "fmt"

    templateMethod "./templatemethod"
)

func startMain() {
    d1 := templateMethod.NewCharDisplay('H')
    d2 := templateMethod.NewStringDisplay("Hello, World!")
    d1.Display()
    fmt.Println("")
    d2.Display()
}

func main() {
    startMain()
}

■ 参考URL

5
4
0

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
  3. You can use dark theme
What you can do with signing up
5
4