はじめに
皆さん、こんにちは!「JavaとPythonで比べるデザインパターン」シリーズの第6回目です。
今回は、関連するオブジェクトのファミリーを、クライアントが具象クラスを指定することなく生成するためのAbstract Factory(抽象ファクトリー)パターンについて解説します。
Abstract Factoryパターンとは?
Abstract Factoryパターンは、互いに関連し合う一連のオブジェクトファミリーを、クライアントコードから分離して生成するためのデザインパターンです。前回解説したFactory Methodパターンが単一のオブジェクトを生成するのに対し、Abstract Factoryパターンは複数の種類のオブジェクトをセットとして生成します。
このパターンが解決する問題
以下のような状況でAbstract Factoryパターンが威力を発揮します:
- プラットフォーム固有のコンポーネント:Windows用/Mac用のUIコンポーネント群
- テーマやスタイル:ライトテーマ/ダークテーマのUI要素一式
- データベース接続:MySQL用/PostgreSQL用の接続オブジェクト群
- ゲーム要素:中世/SF/ファンタジーの兵器・防具セット
パターンの構成要素
Abstract Factoryパターンは4つの主要コンポーネントで構成されます:
- 抽象ファクトリー(AbstractFactory):関連するオブジェクトファミリーを生成するメソッドを定義するインターフェース
- 具象ファクトリー(ConcreteFactory):特定のファミリーに対応する具体的なファクトリー実装
- 抽象プロダクト(AbstractProduct):生成される各製品カテゴリの共通インターフェース
- 具象プロダクト(ConcreteProduct):実際に生成される具体的な製品クラス
Javaでの実装:型安全性を重視した階層設計
Javaの強力な型システムとインターフェースを活用して、堅牢なAbstract Factoryを実装してみましょう。今回は、異なるテーマのGUIコンポーネントを生成する例を使用します。
// JavaでのAbstract Factoryパターンの実装例
// 1. 抽象プロダクト:各UIコンポーネントの共通インターフェース
interface Button {
void render();
void onClick();
}
interface Checkbox {
void render();
void toggle();
}
// 2. 具象プロダクト:ライトテーマ実装
class LightThemeButton implements Button {
@Override
public void render() {
System.out.println("Rendering Light Theme Button with white background");
}
@Override
public void onClick() {
System.out.println("Light button clicked with subtle animation");
}
}
class LightThemeCheckbox implements Checkbox {
private boolean checked = false;
@Override
public void render() {
System.out.println("Rendering Light Theme Checkbox with gray border");
}
@Override
public void toggle() {
checked = !checked;
System.out.println("Light checkbox " + (checked ? "checked" : "unchecked"));
}
}
// 具象プロダクト:ダークテーマ実装
class DarkThemeButton implements Button {
@Override
public void render() {
System.out.println("Rendering Dark Theme Button with dark background");
}
@Override
public void onClick() {
System.out.println("Dark button clicked with glow effect");
}
}
class DarkThemeCheckbox implements Checkbox {
private boolean checked = false;
@Override
public void render() {
System.out.println("Rendering Dark Theme Checkbox with bright border");
}
@Override
public void toggle() {
checked = !checked;
System.out.println("Dark checkbox " + (checked ? "checked" : "unchecked"));
}
}
// 3. 抽象ファクトリー:テーマ固有のUIコンポーネントファミリーを生成
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 4. 具象ファクトリー:各テーマに対応
class LightThemeFactory implements GUIFactory {
@Override
public Button createButton() {
return new LightThemeButton();
}
@Override
public Checkbox createCheckbox() {
return new LightThemeCheckbox();
}
}
class DarkThemeFactory implements GUIFactory {
@Override
public Button createButton() {
return new DarkThemeButton();
}
@Override
public Checkbox createCheckbox() {
return new DarkThemeCheckbox();
}
}
// クライアントコード:テーマを切り替えても同じように動作
class Application {
private GUIFactory factory;
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
this.factory = factory;
}
public void createUI() {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void renderUI() {
button.render();
checkbox.render();
}
}
// 使用例
public class Main {
public static void main(String[] args) {
// 設定やユーザー選択に基づいてファクトリーを選択
String userTheme = "dark"; // 実際にはユーザー設定から取得
GUIFactory factory = switch (userTheme) {
case "light" -> new LightThemeFactory();
case "dark" -> new DarkThemeFactory();
default -> new LightThemeFactory();
};
Application app = new Application(factory);
app.createUI();
app.renderUI();
}
}
Javaの特徴:
- インターフェースによる厳密な契約定義
- コンパイル時の型チェックによる安全性
- 明確なクラス階層による保守性の向上
Pythonでの実装:柔軟性を活かしたアプローチ
Pythonでは、言語の柔軟性を活かして、よりシンプルで実用的なAbstract Factoryを実装できます。
アプローチ1:クラスベースの実装
# PythonでのAbstract Factoryパターンの実装例
from abc import ABC, abstractmethod
# 抽象プロダクト(型ヒントとして使用)
class Button(ABC):
@abstractmethod
def render(self):
pass
@abstractmethod
def on_click(self):
pass
class Checkbox(ABC):
@abstractmethod
def render(self):
pass
@abstractmethod
def toggle(self):
pass
# 具象プロダクト:ライトテーマ
class LightThemeButton(Button):
def render(self):
print("Rendering Light Theme Button with white background")
def on_click(self):
print("Light button clicked with subtle animation")
class LightThemeCheckbox(Checkbox):
def __init__(self):
self.checked = False
def render(self):
print("Rendering Light Theme Checkbox with gray border")
def toggle(self):
self.checked = not self.checked
print(f"Light checkbox {'checked' if self.checked else 'unchecked'}")
# 具象プロダクト:ダークテーマ
class DarkThemeButton(Button):
def render(self):
print("Rendering Dark Theme Button with dark background")
def on_click(self):
print("Dark button clicked with glow effect")
class DarkThemeCheckbox(Checkbox):
def __init__(self):
self.checked = False
def render(self):
print("Rendering Dark Theme Checkbox with bright border")
def toggle(self):
self.checked = not self.checked
print(f"Dark checkbox {'checked' if self.checked else 'unchecked'}")
# 抽象ファクトリー
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
# 具象ファクトリー
class LightThemeFactory(GUIFactory):
def create_button(self) -> Button:
return LightThemeButton()
def create_checkbox(self) -> Checkbox:
return LightThemeCheckbox()
class DarkThemeFactory(GUIFactory):
def create_button(self) -> Button:
return DarkThemeButton()
def create_checkbox(self) -> Checkbox:
return DarkThemeCheckbox()
アプローチ2:よりPythonicな実装
# よりPythonicなアプローチ:ファクトリー関数とクラスオブジェクト
# 製品クラス群
class LightThemeButton:
def render(self):
print("Rendering Light Theme Button with white background")
def on_click(self):
print("Light button clicked with subtle animation")
class LightThemeCheckbox:
def __init__(self):
self.checked = False
def render(self):
print("Rendering Light Theme Checkbox with gray border")
def toggle(self):
self.checked = not self.checked
print(f"Light checkbox {'checked' if self.checked else 'unchecked'}")
class DarkThemeButton:
def render(self):
print("Rendering Dark Theme Button with dark background")
def on_click(self):
print("Dark button clicked with glow effect")
class DarkThemeCheckbox:
def __init__(self):
self.checked = False
def render(self):
print("Rendering Dark Theme Checkbox with bright border")
def toggle(self):
self.checked = not self.checked
print(f"Dark checkbox {'checked' if self.checked else 'unchecked'}")
# Pythonicなファクトリー実装
class ThemeFactory:
"""クラスオブジェクトを使った柔軟なファクトリー"""
def __init__(self, button_class, checkbox_class):
self.button_class = button_class
self.checkbox_class = checkbox_class
def create_button(self):
return self.button_class()
def create_checkbox(self):
return self.checkbox_class()
# さらにPythonicな辞書ベースの実装
THEME_COMPONENTS = {
'light': {
'button': LightThemeButton,
'checkbox': LightThemeCheckbox
},
'dark': {
'button': DarkThemeButton,
'checkbox': DarkThemeCheckbox
}
}
def create_theme_factory(theme_name):
"""ファクトリー関数:設定に基づいてファクトリーを生成"""
components = THEME_COMPONENTS.get(theme_name, THEME_COMPONENTS['light'])
return ThemeFactory(components['button'], components['checkbox'])
# 使用例
def main():
# ユーザー設定に基づいてテーマを選択
user_theme = "dark" # 実際にはユーザー設定から取得
# ファクトリーを作成
factory = create_theme_factory(user_theme)
# UIコンポーネントを生成
button = factory.create_button()
checkbox = factory.create_checkbox()
# 描画
button.render()
checkbox.render()
# インタラクション
button.on_click()
checkbox.toggle()
if __name__ == "__main__":
main()
Pythonの特徴:
-
abcモジュールによるインターフェース定義(必要に応じて) - クラスオブジェクトの第一級市民としての扱い
- 辞書やファクトリー関数による柔軟な設計
- より簡潔で実用的なコード
実際の適用例と注意点
実用的な適用場面
- マルチプラットフォームアプリ:OS固有のファイルシステムアクセスオブジェクト群
- データベース抽象化:データベース固有のDAO(Data Access Object)群
- テスト環境:本番用/テスト用のサービスオブジェクト群
- プラグインアーキテクチャ:プラグイン固有の機能コンポーネント群
注意点
- 過度な抽象化を避ける:関連性の薄いオブジェクトを無理にファミリー化しない
- Factory Methodとの使い分け:単一オブジェクトならFactory Methodで十分
- 設定管理:どのファクトリーを使用するかの判断ロジックを明確にする
まとめ:両言語での実装比較
| 特性 | Java | Python |
|---|---|---|
| 実装アプローチ | インターフェース中心の階層設計 | クラスオブジェクトや辞書を活用 |
| 型安全性 | コンパイル時チェックで高い安全性 | 型ヒントで改善可能、実行時チェック |
| コードの複雑さ | 明示的だが冗長になりがち | 簡潔で柔軟、可読性に優れる |
| 拡張性 | インターフェース変更時の影響が明確 | 動的な拡張が容易 |
| 適用場面 | 大規模システム、厳密な仕様が必要 | プロトタイピング、小〜中規模システム |
Abstract Factoryパターンは、両言語で実装スタイルは大きく異なりますが、**「関連するオブジェクトファミリーの生成をカプセル化し、クライアントコードから具象クラスを分離する」**という本質的な目的は共通です。
Javaは厳密な型システムで安全性と保守性を重視し、Pythonは言語の柔軟性を活かして実用性と簡潔性を追求します。プロジェクトの要件と特性に応じて、適切なアプローチを選択することが重要です。
次回は、既存のオブジェクトをテンプレートとして新しいオブジェクトを効率的に作成するPrototypeパターンについて解説します。お楽しみに!
次回予告:「Day 7 Prototypeパターン:既存のオブジェクトから新しいオブジェクトを作る」