GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Adapter」を学ぶ"
今回は、Pythonで実装した”Adapter”のサンプルアプリをGolangで実装し直してみました。
■ Adapter(アダプター・パターン)
Adapterパターン(アダプター・パターン)とは、GoF (Gang of Four; 4人のギャングたち) によって定義されたデザインパターンの1つである。Adapterパターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。Adapterパターンを実現するための手法として"継承を利用した手法"と"委譲を利用した手法"が存在する。
UML class diagram
1. 継承を利用したAdapter
継承を利用したAdapterは、利用したいクラスのサブクラスを作成し、そのサブクラスに対して必要なインタフェースを実装することで実現される。
2. 委譲を利用したAdapter
委譲を利用したAdapterは、利用したいクラスのインスタンスを生成し、そのインスタンスを他クラスから利用することで実現される。
(以上、ウィキペディア(Wikipedia)より引用)
■ Golangで、Pythonクラスを実装し直す際の備忘録
Golang では、Python等でお馴染みの、Inheritance(継承)ではなく、Composition(合成)のみが使われます。
具体的、以下の2点を気をつけて、サンプルアプリの再実装に取り組みました。
- golangには、オブジェクトのクラス継承の概念が存在しないので、再利用性(Embedded)と多相性(Interface)の活用で対応する必要がある。
- Pythonの場合だと、動的型付けのおかげて、引数/戻り値の型を意識することなくポリモーフィズムを実践できていたが、Golangの場合では、引数/戻り値の型を柔軟に扱えるようインタフェースを活用する必要がある。
□ 大変、お世話になったQiita記事
■ "Adapter"のサンプルプログラム
実際に、Adapterパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
- 文字列を、カッコでくくって表示する
- 文字列の前後に、*印をつけて表示する
なお、サンプルプログラムは、再利用性(Embedded)を活用したものと多相性(Interface)を活用したもの、それぞれ用意しました。サンプルプログラム動作の結果は、全く同じになります。
□ 再利用性(Embedded)を利用したサンプルプログラム
$ go run Adapter_1_Embedding/Main.go
(Hello)
*Hello*
□ 多相性(Interface)を利用したサンプルプログラム
$ go run Adapter_2_Interface/Main.go
(Hello)
*Hello*
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Adapter
- ディレクトリ構成
.
├── Adapter_1_Embedding
│ ├── Main.go
│ └── adapter
│ ├── banner.go
│ └── print_banner.go
└── Adapter_2_Interface
├── Main.go
└── adapter
├── banner.go
└── print_banner.go
□ 再利用性(Embedded)を利用したサンプルプログラム
(1) Target(対象)の役
Target
役は、インスタンスの振る舞いに関わるインタフェースを定めます。
再利用性(Embedded)を利用したサンプルプログラムでは、この役は存在しません。
(2) Client(依頼者)の役
Target
役のメソッドを使って、仕事をする役です。
サンプルプログラムでは、startMain
メソッドが、この役を努めます。
package main
import (
"./adapter"
)
func startMain() {
p := adapter.NewPrintBanner("Hello")
p.PrintWeak()
p.PrintString()
}
func main() {
startMain()
}
(3) Adaptee(適合される側)の役
Adapter
役のなかで実際に動作するメソッドを、ここで実装します。
サンプルプログラムでは、banner
構造体が、この役を努めます。
package adapter
import "fmt"
type banner struct {
str string
}
func (b *banner) showWithParen() {
fmt.Printf("(%s)\n", b.str)
}
func (b *banner) showWithAster() {
fmt.Printf("*%s*\n", b.str)
}
(4) Adapter(適合する側)の役
Adapter
役は、Target
役のインタフェースを実装しているクラスです。
サンプルプログラムでは、PrintBanner
構造体が、この役を努めます。
package adapter
// PrintBanner is struct
type PrintBanner struct {
*banner
}
// NewPrintBanner func for initializing PrintBanner
func NewPrintBanner(str string) *PrintBanner {
return &PrintBanner{
banner: &banner{str: str},
}
}
// PrintWeak func for formatting with paren
func (p *PrintBanner) PrintWeak() {
p.showWithParen()
}
// PrintString func for formatting with aster
func (p *PrintBanner) PrintString() {
p.showWithAster()
}
□ 多相性(Interface)を利用したサンプルプログラム
(1) Target(対象)の役
Target
役は、インスタンスの振る舞いに関わるインタフェースを定めます。
多相性(Interface)を利用したサンプルプログラムでも、この役は存在しません。
(2) Client(依頼者)の役
Target
役のメソッドを使って、仕事をする役です。
サンプルプログラムでは、startMain
関数が、この役を努めます。
package main
import (
"./adapter"
)
func startMain() {
p := adapter.NewPrintBanner("Hello")
p.PrintWeak()
p.PrintString()
}
func main() {
startMain()
}
(3) Adaptee(適合される側)の役
Adapter
役のなかで実際に動作するメソッドを、ここで実装します。
サンプルプログラムでは、banner
インタフェースに、Compositionを適用して、この役を努めます。
package adapter
import "fmt"
type banner interface {
showWithParen()
showWithAster()
}
func (p *PrintBanner) showWithParen() {
fmt.Printf("(%s)\n", p.str)
}
func (p *PrintBanner) showWithAster() {
fmt.Printf("*%s*\n", p.str)
}
(4) Adapter(適合する側)の役
Adapter
役は、Target
役のインタフェースを実装しているクラスです。
サンプルプログラムでは、PrintBanner
構造体が、この役を努めます。
package adapter
// PrintBanner is struct
type PrintBanner struct {
str string
banner banner
}
// NewPrintBanner func for initializing PrintBanner
func NewPrintBanner(str string) *PrintBanner {
printBanner := &PrintBanner{
str: str,
}
printBanner.banner = printBanner
return printBanner
}
// PrintWeak func for formatting with paren
func (p *PrintBanner) PrintWeak() {
p.banner.showWithParen()
}
// PrintString func for formatting with aster
func (p *PrintBanner) PrintString() {
p.banner.showWithAster()
}