第13章: Abstract Factory パターン — 6言語統合ガイド
1. はじめに
Abstract Factory パターンは、関連するオブジェクトのファミリーを一貫した方法で生成する GoF パターンです。OOP ではファクトリインターフェースと具象ファクトリクラスを用いますが、関数型プログラミングではファクトリ関数のレコード / マップ / モジュールとして表現されます。スタイル付き図形、プラットフォーム別 UI、データベースアダプターなどの生成を統一的に管理します。
2. 共通の本質
Abstract Factory の構造
Factory = { createA: params → A, createB: params → B }
- 製品ファミリー: 一貫したスタイル/テーマの製品群を生成
- ファクトリの差し替え: 実行時にファクトリを切り替えて製品群を変更
- 一貫性の保証: 同じファクトリから生成された製品は互いに整合
典型的なユースケース
- 図形ファクトリ: Standard / Outlined / Filled スタイルの図形生成
- UI ファクトリ: Windows / Mac / Linux 向けのコンポーネント生成
- データベースファクトリ: MySQL / PostgreSQL / SQLite の接続生成
3. 言語別実装比較
3.1 ファクトリの表現方法
| 言語 |
ファクトリ表現 |
製品の表現 |
| Clojure |
関数のマップ / マルチメソッド |
マップ |
| Scala |
trait + object |
case class |
| Elixir |
プロトコル + 構造体 |
構造体 |
| F# |
判別共用体 / 関数レコード |
レコード |
| Haskell |
型クラス / 関数レコード |
ADT |
| Rust |
trait + struct |
struct |
3.2 図形ファクトリ
Scala: trait + object
trait ShapeFactory:
def createCircle(center: Point, radius: Double): Shape
def createSquare(topLeft: Point, side: Double): Shape
def createRectangle(topLeft: Point, width: Double, height: Double): Shape
object StandardShapeFactory extends ShapeFactory:
def createCircle(center: Point, radius: Double): Shape =
Circle(center, radius)
def createSquare(topLeft: Point, side: Double): Shape =
Square(topLeft, side)
def createRectangle(topLeft: Point, w: Double, h: Double): Shape =
Rectangle(topLeft, w, h)
object OutlinedShapeFactory extends ShapeFactory:
def createCircle(center: Point, radius: Double): Shape =
StyledShape(Circle(center, radius), Style(outline = true, color = "black"))
// ...
Haskell: 型クラス
class ShapeFactory f where
createCircle :: f -> Point -> Double -> Shape
createSquare :: f -> Point -> Double -> Shape
createRectangle :: f -> Point -> Double -> Double -> Shape
data StandardFactory = StandardFactory
data OutlinedFactory = OutlinedFactory String Float
instance ShapeFactory StandardFactory where
createCircle _ center radius = Circle center radius
createSquare _ topLeft side = Square topLeft side
createRectangle _ tl w h = Rectangle tl w h
instance ShapeFactory OutlinedFactory where
createCircle (OutlinedFactory color width) center radius =
StyledShape (Circle center radius) (Style color width)
-- ...
F#: 判別共用体によるファクトリ選択
type ShapeFactory =
| Standard
| Outlined of color: string * width: float
| Filled of color: string
module ShapeFactory =
let createCircle (factory: ShapeFactory) (center: Point) (radius: float) =
let baseShape = Circle(center, radius)
match factory with
| Standard -> baseShape
| Outlined(color, width) ->
StyledShape(baseShape, { OutlineColor = color; OutlineWidth = width })
| Filled(color) ->
StyledShape(baseShape, { FillColor = color })
let createSquare (factory: ShapeFactory) (topLeft: Point) (side: float) =
let baseShape = Square(topLeft, side)
match factory with
| Standard -> baseShape
| Outlined(color, width) ->
StyledShape(baseShape, { OutlineColor = color; OutlineWidth = width })
| Filled(color) ->
StyledShape(baseShape, { FillColor = color })
Rust: trait + struct
pub trait ShapeFactory {
fn create_circle(&self, center: Point, radius: f64) -> Shape;
fn create_square(&self, top_left: Point, side: f64) -> Shape;
}
pub struct StandardFactory;
impl ShapeFactory for StandardFactory {
fn create_circle(&self, center: Point, radius: f64) -> Shape {
Shape::Circle { center, radius }
}
fn create_square(&self, top_left: Point, side: f64) -> Shape {
Shape::Square { top_left, side }
}
}
pub struct OutlinedFactory {
pub color: String,
pub width: f64,
}
impl ShapeFactory for OutlinedFactory {
fn create_circle(&self, center: Point, radius: f64) -> Shape {
Shape::Styled {
shape: Box::new(Shape::Circle { center, radius }),
style: Style { outline_color: self.color.clone(), outline_width: self.width },
}
}
// ...
}
Clojure: 関数のマップ
(def standard-factory
{:create-circle (fn [center radius]
{:type :circle :center center :radius radius})
:create-square (fn [top-left side]
{:type :square :top-left top-left :side side})})
(def outlined-factory
{:create-circle (fn [center radius]
{:type :styled-shape
:shape {:type :circle :center center :radius radius}
:style {:outline true :color "black"}})
:create-square (fn [top-left side]
{:type :styled-shape
:shape {:type :square :top-left top-left :side side}
:style {:outline true :color "black"}})})
;; ファクトリの利用
(defn draw-scene [factory]
(let [circle ((:create-circle factory) {:x 0 :y 0} 5)
square ((:create-square factory) {:x 10 :y 10} 3)]
[circle square]))
Elixir: プロトコル + 構造体
defprotocol ShapeFactory do
def create_circle(factory, center, radius)
def create_square(factory, top_left, side)
end
defmodule StandardShapeFactory do
defstruct []
end
defimpl ShapeFactory, for: StandardShapeFactory do
def create_circle(_factory, center, radius) do
%Circle{center: center, radius: radius}
end
def create_square(_factory, top_left, side) do
%Square{top_left: top_left, side: side}
end
end
defmodule OutlinedShapeFactory do
defstruct [:outline_color, :outline_width]
end
defimpl ShapeFactory, for: OutlinedShapeFactory do
def create_circle(factory, center, radius) do
%StyledShape{
shape: %Circle{center: center, radius: radius},
style: %{color: factory.outline_color, width: factory.outline_width}
}
end
# ...
end
3.3 UI ファクトリ(プラットフォーム別)
プラットフォーム別 UI 生成の比較
// Rust
pub trait GUIFactory {
fn create_button(&self, label: &str) -> Box<dyn Button>;
fn create_checkbox(&self, label: &str, checked: bool) -> Box<dyn Checkbox>;
}
pub struct WindowsFactory;
pub struct MacFactory;
impl GUIFactory for WindowsFactory {
fn create_button(&self, label: &str) -> Box<dyn Button> {
Box::new(WindowsButton { label: label.to_string() })
}
// ...
}
// Scala
trait GUIFactory:
def createButton(label: String): Button
def createCheckbox(label: String, checked: Boolean): Checkbox
object WindowsFactory extends GUIFactory:
def createButton(label: String) = WindowsButton(label)
def createCheckbox(label: String, checked: Boolean) = WindowsCheckbox(label, checked)
object MacFactory extends GUIFactory:
def createButton(label: String) = MacButton(label)
def createCheckbox(label: String, checked: Boolean) = MacCheckbox(label, checked)
4. 比較分析
4.1 ファクトリの抽象化レベル
| 言語 |
抽象化方法 |
切り替えの容易さ |
| Clojure |
マップの差し替え |
最も柔軟(動的) |
| Scala |
trait の実装切り替え |
型安全に切り替え |
| Elixir |
プロトコル実装の差し替え |
プロトコルベースで柔軟 |
| F# |
判別共用体のパターンマッチ |
コンパイル時に網羅性保証 |
| Haskell |
型クラスインスタンスの選択 |
型推論で自動選択 |
| Rust |
trait object の差し替え |
Box<dyn Factory> で動的 |
4.2 OOP Abstract Factory vs 関数型
| 観点 |
OOP |
関数型 |
| ファクトリ |
インターフェース + 実装クラス |
関数のレコード / 型クラス / trait |
| 製品 |
具象クラス |
ADT / enum / マップ |
| 拡張 |
新クラス追加 |
新しいインスタンス / 判別共用体追加 |
| 依存性注入 |
コンストラクタ |
関数引数 |
4.3 Elixir 版のボリューム
Elixir 版は 604 行と最も長い記事です。これは以下の理由によります:
- 3-4 種類のファクトリ(Standard, Outlined, Filled, Complex)の完全実装
- UI ファクトリ(Windows, Mac, Linux)の詳細例
- データベースファクトリの追加例
- 各ファクトリのテストコードと使用例
5. 実践的な選択指針
| 要件 |
推奨言語 |
理由 |
| 動的なファクトリ切り替え |
Clojure |
マップベースで最も柔軟 |
| 型安全なファクトリ |
Haskell |
型クラスで自動推論 |
| プラットフォーム別 UI |
Rust, Scala |
trait ベースの具象ファクトリ |
| コンパイル時の網羅性保証 |
F# |
判別共用体でファクトリ種別を管理 |
| プロトコルベースの拡張 |
Elixir |
既存型に後からファクトリ実装を追加 |
6. まとめ
Abstract Factory パターンは、関数型プログラミングで以下のように表現されます:
- ファクトリ = 関数の集合: 生成関数をレコード / マップ / 型クラスでまとめる
- 製品ファミリーの一貫性: 同じファクトリから生成された製品は整合する
- 差し替え可能性: ファクトリを引数として渡すことで実行時に切り替え
言語別個別記事