はじめに
- Decoraterパターンは、オブジェクトの振る舞いを追加 / 変更するための非常に強力なデザインパターンです
- Goには直接的なサポートはありませんが、インタフェースと関数を使うことで、このパターンを実装することができます
- 本記事では、GoでDecoratorパターンを実装する手法の非常にシンプルな例として、メソッドの出力前後に固定のメッセージを追加するデコレータを紹介します
対象読者
- Goの関数、メソッド、インタフェースの基礎を理解されている方
Decoratorパターンとは
- GoFによって定義されたデザインパターンの一つです
- 既存のメソッドを変更せずに、オブジェクトの振る舞いを追加 / 変更することが可能です
ステップ1:インタフェース定義
以下のようなPrinterインタフェースと、それを実装するMyPrinter型を定義します。
main.go
package main
import "fmt"
// Printerインタフェースの定義
type Printer interface {
Print()
}
// プリンターの実装
type MyPrinter struct{}
func (mp MyPrinter) Print() {
fmt.Println("hogehoge")
}
func main() {
myPrinter := MyPrinter{}
myPrinter.Print()
}
実行結果
hogehoge
ステップ2:Decorator用の構造体を定義する
Printerを属性として持つ構造体「DecoratedPrinter」を定義します。DecoratedPrinterのPrintメソッドは、引数で受け取ったPrinter.Printメソッド出力前後に固定文字「start」と「end」を出力します。
// Printerを属性として持つ構造体
type DecoratedPrinter struct {
wrapped Printer
}
func (dp DecoratedPrinter) Print() {
fmt.Println("start")
dp.wrapped.Print()
fmt.Println("end")
}
ステップ3:Decoratorの定義
今回の目玉であるインタフェースをラップする関数「Decorate」の定義です。シグネチャはPrinterを受け取り、Printerを返すというシンプルなものですが、実際にreturnしているのはPrinterではなく、DecoratedPrinterです。
Printerはインタフェース、そして、DecoratedPrinterはPrintメソッドを持っている為、Printerインタフェースを実装しています。Goではこの場合、DecoratedPrinterをPrinterに代入可能です。
// Printerインタフェースをラップする
func Decorate(p Printer) Printer {
return DecoratedPrinter{wrapped: p}
}
ステップ4:Decoratorを適用する
元のMyPrinterをデコレートします。
func main() {
printer := Decorate(MyPrinter{})
printer.Print()
}
完成したソースコード
MyPrinterがデコレートされていることが確認できました。めでたしめでたし。
main.go
package main
import "fmt"
// Printerインタフェースの定義
type Printer interface {
Print()
}
// プリンターの実装
type MyPrinter struct{}
func (mp MyPrinter) Print() {
fmt.Println("hogehoge")
}
// Printerを属性として持つ構造体
type DecoratedPrinter struct {
wrapped Printer
}
func (dp DecoratedPrinter) Print() {
fmt.Println("start")
dp.wrapped.Print()
fmt.Println("end")
}
// Printerインタフェースをラップする
func Decorate(p Printer) Printer {
return DecoratedPrinter{wrapped: p}
}
func main() {
printer := Decorate(MyPrinter{})
printer.Print()
}
実行結果
start
hogehoge
end