GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Factory Method」を学ぶ"
今回は、Pythonで実装した” Factory Method”のサンプルアプリをGolangで実装し直してみました。
■ Factory Method(ファクトリメソッド)
Factory Method パターン(ファクトリメソッド・パターン)とは、GoF (Gang of Four; 四人組)によって定義されたデザインパターンの1つである。 Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。
Virtual Constructor パターンとも呼ばれる。
UML class diagram
□ 備忘録
振る舞いに関するTemplate Method
パターンは、スーパークラスで処理の大枠を作って、サブクラスで具体的処理を決定するというもの。このTemplate Method
パターンを、インスタンスをつくる場面に適用したものが、Factory Method
パターンらしい。
■ "Factory Method"のサンプルプログラム
Factory Methodパターンは、以下の特徴があるようです。
- インスタンスの作り方をスーパークラス側で定める
- 具体的なクラス名までは定めない
- 具体的な肉付けは、全てサブクラス側で行う
これによって、インスタンス作成のための枠組み(フレームワーク)と実際のインスタンス作成のクラスを分けて考えることができるようになるそうです。
実際に、Factory Methodパターンを使って、「身分証明書カード(IDカード)を作る工場」を題材としたサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
-
Hiroshi Yuki"
,Tomura
,Hanako Sato
の3名の身分証明書カードを作成する -
Hiroshi Yuki"
,Tomura
,Hanako Sato
の3名の身分証明書カードを使用する
$ go run Main.go
I'll create Hiroshi Yuki's card
I'll create Tomura's card
I'll create Hanako Sato's card
I'll use Hiroshi Yuki's card
I'll use Tomura's card
I'll use Hanako Sato's card
サンプルプログラムを動かしただけだと、いまいち、何がしたいのかよく分かりませんね
つづいて、サンプルプログラムの詳細を確認していきます。
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/FactoryMethod
- ディレクトリ構成
.
├── Main.go
└── framework
├── factory.go
└── id_card_factory.go
(1) Product(製品)の役
Product
役では、インスタンスが持つべきインタフェースを定めます。
サンプルプログラムでは、Product
インタフェースが、この役を努めます。
package factoryMethod
... (snip)
// Product is interface
type Product interface {
Use()
getOwner() string
}
(2) Creator(作成者)の役
Creator
役では、Product
を生成する役割を担います。実際に生成するConcreteProduct
役については何も知りません。
さらに、Creator
役には、インスタンスの各部分を作るためのメソッドが用意されます。
サンプルプログラムでは、Factory
構造体が、この役を努めます。
package factoryMethod
// FactoryInterface is interface
type FactoryInterface interface {
createProduct(owner string) Product
registerProduct(Product)
Create(owner string) Product
}
// Factory is struct
type Factory struct {
factory FactoryInterface
}
// Create func for creating product
func (f *Factory) Create(owner string) Product {
p := f.factory.createProduct(owner)
f.factory.registerProduct(p)
return p
}
... (snip)
(3) ConcreteProduct(具体的製品)の役
ConcreteProduct
役は、Product
役のインタフェースを実装しているクラスです。実際のインスタンス作成後に呼び出されるメソッドが、ここで定義されます。
サンプルプログラムでは、iDCardProduct
構造体が、この役を努めます。
package factoryMethod
... (snip)
type iDCardProduct struct {
owner string
}
func newIDCardProduct(owner string) *iDCardProduct {
fmt.Printf("I'll create %s's card\n", owner)
return &iDCardProduct{owner}
}
// Use func for using card
func (i *iDCardProduct) Use() {
fmt.Printf("I'll use %s's card\n", i.owner)
}
func (i *iDCardProduct) getOwner() string {
return i.owner
}
(4) ConcreteCreator(具体的作成者)の役
ConcreteCreator
役は、Creator
役のインタフェースを実装しているクラスです。具体的な製品を作るクラスを定めます。
サンプルプログラムでは、IDCardFactory
構造体が、この役を努めます。
package factoryMethod
import "fmt"
// IDCardFactory is struct
type IDCardFactory struct {
*Factory
owners []string
}
// NewIDCardFactory func for initializing IDCardFactory
func NewIDCardFactory() *IDCardFactory {
idCardFactory := &IDCardFactory{
Factory: &Factory{},
}
idCardFactory.factory = idCardFactory
return idCardFactory
}
func (i *IDCardFactory) createProduct(owner string) Product {
return newIDCardProduct(owner)
}
func (i *IDCardFactory) registerProduct(product Product) {
i.owners = append(i.owners, product.getOwner())
}
... (snip)
(5) Client(依頼人)の役
サンプルプログラムでは、startMain
関数が、この役を努めます。
package main
import (
factoryMethod "./framework"
)
func startMain(factoryObject factoryMethod.FactoryInterface) {
card1 := factoryObject.Create("Hiroshi Yuki")
card2 := factoryObject.Create("Tomura")
card3 := factoryObject.Create("Hanako Sato")
card1.Use()
card2.Use()
card3.Use()
}
func main() {
startMain(factoryMethod.NewIDCardFactory())
}