Abstract Factoryパターンとは
生成に関するデザインパターンの一つ。関連したオブジェクトの集まりを、具象クラスを指定することなく生成することを可能にするパターン。
例えばPC, モバイル端末のそれぞれに特化したGUIを生成する場合を考える。GUIを生成する際に必要な部品, 様式として下記のように考えることができる。
- 部品: Button, CheckBox etc…
- 様式: PC用,IPhone用, IPad用 etc…
仮にPC用のGUIを生成しようとした場合、すべての部品(PC用Button, PC用CheckBox…)を生成する必要がある。このような時に具象クラスを呼び出すことなくそれぞれの様式に沿ったオブジェクトを生成することを可能にしたのがAbstract Factoryパターンになる。
各部品は様式(PC, IPhone,…)ごとに用意されたファクトリーで生成される。
クライアント側はファクトリー・部品ともに抽象インターフェースを利用して実装される。そのためファクトリーや部品の型・実装を変更したとしても問題なく動作することができる。
Abstract Factoryパターンを利用することで下記のようなメリットを得ることができる。
- ファクトリから得られる部品同士は互換であることが保証される。
- 具象とクライアント側処理の密結合を防ぐことができる。
- 部品作成コードが1箇所にまとめられる。(単一責任の原則)
- 新しい変種(ファクトリー)を追加してもクライアント側コードは問題なく動作する。(開放閉鎖の原則)
クラス図

- AbstractProduct
AbstractFactoryによって生成される抽象的な部品や製品のインターフェースを宣言する。 - ConcreteProduct
AbstractFactoryに宣言されたインターフェースを実装する。個々のAbstractProductはすべての変種(ProductA1,A2…)について実装されている必要がある。 - AbstractFactory
AbstractProductのそれぞれを生成するためのインターフェースを宣言する。 - ConcreteFactory
AbstractFactoryで宣言されたAbstractProductを生成するためのインターフェースを実装する。
個々のConcreteFactoryは特定の種類(ConcreteProductA1,B1..)にのみ対応している。
実装
AbstractProduct
package abstract_factory
type ICheckBox interface {
SetHeight(height int)
SetWidth(width int)
GetHeight() int
GetWidth() int
}
type CheckBox struct {
Height int
Width int
}
func (c *CheckBox) SetHeight(height int) {
c.Height = height
}
func (c *CheckBox) SetWidth(width int) {
c.Width = width
}
func (c *CheckBox) GetHeight() int {
return c.Height
}
func (c *CheckBox) GetWidth() int {
return c.Width
}
package abstract_factory
type IButton interface {
SetColor(color string)
SetText(text string)
GetColor() string
GetText() string
}
type Button struct {
Color string
Text string
}
func (b *Button) SetColor(color string) {
b.Color = color
}
func (b *Button) SetText(text string) {
b.Text = text
}
func (b *Button) GetColor() string {
return b.Color
}
func (b *Button) GetText() string {
return b.Text
}
ConcreteProduct
package pcfactory
import (
. "abstract_factory/abstract_factory"
)
type pcCheckBox struct {
CheckBox
}
package pcfactory
import (
. "abstract_factory/abstract_factory"
)
type pcButton struct {
Button
}
package mobilefactory
import (
. "abstract_factory/abstract_factory"
)
type mobileCheckBox struct {
CheckBox
}
package mobilefactory
import (
. "abstract_factory/abstract_factory"
)
type mobileButton struct {
Button
}
AbstractFactory
package abstract_factory
type IGuiFactory interface {
createCheckBox() ICheckBox // 戻り値は抽象部品
createButton() IButton // 戻り値は抽象部品
}
ConcreteFactory
package pcfactory
import (
. "abstract_factory/abstract_factory"
)
// PC用GUI部品を生成するファクトリー
type pcGuiFactory struct {}
func NewGetPcGuiFactory() *pcGuiFactory {
return &pcGuiFactory{}
}
func (f *pcGuiFactory) CreateCheckBox() ICheckBox { // 戻り値は抽象部品
return &pcCheckBox{
CheckBox: CheckBox{Height: 20, Width: 20},
}
}
func (f *pcGuiFactory) CreateButton() IButton { // 戻り値は抽象部品
return &pcButton{
Button: Button{Color: "red", Text: "PC Button"},
}
}
package mobilefactory
import (
. "abstract_factory/abstract_factory"
)
// モバイル用GUI部品を生成するファクトリー
type mobileGuiFactory struct {}
func NewGetMobileGuiFactory() *mobileGuiFactory {
return &mobileGuiFactory{}
}
func (f *mobileGuiFactory) CreateCheckBox() ICheckBox { // 戻り値は抽象部品
return &mobileCheckBox{
CheckBox: CheckBox{Height: 10, Width: 10},
}
}
func (f *mobileGuiFactory) CreateButton() IButton { // 戻り値は抽象部品
return &mobileButton{
Button: Button{Color: "blue", Text: "Mobile Button"},
}
}
動作確認
package main
import (
"fmt"
. "abstract_factory/abstract_factory"
. "abstract_factory/pcfactory"
. "abstract_factory/mobilefactory"
)
func getFactory(factoryType string) IGuiFactory {
if factoryType == "pc" {
return NewGetPcGuiFactory()
}
if factoryType == "mobile" {
return NewGetMobileGuiFactory()
}
return nil
}
type app struct {
factory IGuiFactory
}
func (a *app) createGUI() {
c := a.factory.CreateCheckBox()
b := a.factory.CreateButton()
fmt.Printf("CheckBox={height: %d, width: %d}\n", c.GetHeight(), c.GetWidth())
fmt.Printf("Button={color: %s, text: %s}\n", b.GetColor(), b.GetText())
}
func main() {
types := []string{"pc", "mobile"}
// クライアントからは抽象型(IGuiFactory, ICheckBox, IButton)を通して処理を行う。
// これにより仮に新たな型・ファクトリが追加されてもクライアント側は問題なく動作する。
for _, t := range types {
fmt.Println(t)
f := getFactory(t)
a := app{factory: f}
a.createGUI()
}
}
>> go run .
pc
CheckBox={height: 20, width: 20}
Button={color: red, text: PC Button}
mobile
CheckBox={height: 10, width: 10}
Button={color: blue, text: Mobile Button}
補足
- 新しいファクトリーを追加することは容易。しかし新しい部品を追加する場合は、各ファクトリの修正やクライアント側の修正が必要になる可能性がある。
コメント