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
■ "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
構造体が、この役を努めます。
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
構造体が、この役を努めます。
// 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(">>")
}
// 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
関数が、この役を努めます。
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()
}