Abstract Factory パターン
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 88/107。
Abstract Factoryパターンは、具体的な型を指定することなく、関連するオブジェクトのファミリを作成することで、Factoryパターンを拡張します。単一のファクトリ関数の代わりに、複数の具体的なファクトリが実装するファクトリインターフェースを定義し、それぞれが一貫した関連オブジェクトのセットを生成します。
ボタンやチェックボックスを作成する必要があるUIツールキットを考えてみましょう。オペレーティングシステムによって実装は異なりますが、コンポーネントは一貫して連携して動作する必要があります:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// 抽象ファクトリインターフェース
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}各具象ファクトリは、関連するコンポーネントのファミリーを生成します:
// Windows ファミリー
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Mac ファミリー
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }クライアントコードはファクトリインターフェースを介して動作するため、具体的な実装に依存しません:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}システムが関連する製品の複数のファミリーを扱う必要があり、同じファミリーのコンポーネントが確実に一緒に使用されるようにしたい場合に、Abstract Factoryを使用します。これは単純なFactoryよりも複雑であるため、製品ファミリーを管理する必要が本当にある場合にのみ検討してください。
チャレンジ
簡単Abstract Factory パターンを使用して家具店システムを構築しましょう!関連する家具アイテムのファミリーを生成するファクトリを作成します。各ファクトリは一貫したスタイル(Modern または Vintage)を生成し、同じファクトリから作られた椅子とテーブルのデザインが調和するようにします。
コードは以下の3つのファイルに分けて構成します:
furniture.go: 製品のインターフェースと具体的な家具の型を定義します。2つの製品インターフェースを作成してください:
SitOn() stringメソッドを持つChairPlaceItems() stringメソッドを持つTable
2つの家具ファミリーを実装してください:
- Modern ファミリー:
ModernChairはSitting on a sleek modern chairを返し、ModernTableはPlacing items on a glass modern tableを返します。 - Vintage ファミリー:
VintageChairはSitting on an ornate vintage chairを返し、VintageTableはPlacing items on a wooden vintage tableを返します。
factory.go: 抽象ファクトリインターフェースと具体的なファクトリを定義します。2つのメソッドを持つ
FurnitureFactoryインターフェースを作成してください:CreateChair() ChairCreateTable() Table
2つの具体的なファクトリを実装してください:
ModernChairとModernTableを生成するModernFactoryVintageChairとVintageTableを生成するVintageFactory
また、スタイル(
modernまたはvintage)に基づいて適切なファクトリを返す関数GetFactory(style string) FurnitureFactoryを作成してください。認識されないスタイルの場合は、デフォルトでModernFactoryを返します。main.go: ファクトリを使用して部屋に家具を配置します。ファクトリを使用して椅子とテーブルを作成し、両方のアクションを別々の行にフォーマットした文字列として返す関数
FurnishRoom(factory FurnitureFactory) stringを作成してください。スタイルの入力を読み取り、適切なファクトリを取得し、部屋に家具を配置して、その結果を出力します。
以下の入力が提供されます:
- 1行目:家具のスタイル(
modern、vintage、またはその他)
例えば、次のように入力された場合:
modern出力は以下のようになります:
Sitting on a sleek modern chair
Placing items on a glass modern tableまた、次のように入力された場合:
vintage出力は以下のようになります:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableまた、次のように入力された場合:
rustic出力は以下のようになります:
Sitting on a sleek modern chair
Placing items on a glass modern tableFurnishRoom 関数がインターフェースを通じてどのファクトリとも連携できることに注目してください。この関数は、モダンな家具を作っているのかヴィンテージな家具を作っているのかを知る必要がありません。これにより、部屋のすべての家具が同じファミリーから提供され、一貫したスタイルが維持されることが保証されます!
チートシート
Abstract Factory パターンは、具体的な型を指定することなく、関連するオブジェクトのファミリーを作成します。これは、複数の具体的なファクトリが実装するファクトリインターフェースを定義し、各ファクトリが一貫した関連オブジェクトのセットを生成します。
構造
各オブジェクトタイプに対してプロダクトインターフェースを定義します:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}各プロダクトタイプを作成するためのメソッドを宣言する抽象ファクトリインターフェースを作成します:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}具体的な実装
各ファミリーが一貫したスタイリングや動作を持つ、具体的なプロダクトファミリーを実装します:
// Windows ファミリー
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Mac ファミリー
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }クライアントコード
クライアントコードはファクトリインターフェースを介して動作し、具体的な実装からは独立したままになります:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}いつ使用するか
システムが関連するプロダクトの複数のファミリーを扱う必要があり、同じファミリーのコンポーネントが一緒に使用されることを保証したい場合に Abstract Factory を使用します。これは単純な Factory よりも複雑であるため、プロダクトファミリーを管理する必要が本当にある場合にのみ使用してください。
自分で試してみよう
package main
import "fmt"
// FurnishRoom はファクトリを使用して家具を作成し、その結果を返します
// TODO: この関数を実装してください
// - ファクトリを使用して椅子とテーブルを作成します
// - 1行目に `SitOn()` の結果、2行目に `PlaceItems()` の結果を含む文字列を返します
func FurnishRoom(factory FurnitureFactory) string {
// TODO: ファクトリを使用して椅子とテーブルを作成してください
// TODO: 両方のアクションを別々の行に含めた、フォーマット済み文字列を返してください
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: スタイルに基づいて適切なファクトリを取得してください
// TODO: ファクトリを使用して部屋に家具を配置してください
// TODO: 結果を出力してください
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー