0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

全30回:静的と動的でどう違うのか、JavaとPythonで学ぶデザインパターン - Day 6 Abstract Factoryパターン:関連するオブジェクトファミリーをまとめて生成する

Posted at

はじめに

皆さん、こんにちは!「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つの主要コンポーネントで構成されます:

  1. 抽象ファクトリー(AbstractFactory):関連するオブジェクトファミリーを生成するメソッドを定義するインターフェース
  2. 具象ファクトリー(ConcreteFactory):特定のファミリーに対応する具体的なファクトリー実装
  3. 抽象プロダクト(AbstractProduct):生成される各製品カテゴリの共通インターフェース
  4. 具象プロダクト(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モジュールによるインターフェース定義(必要に応じて)
  • クラスオブジェクトの第一級市民としての扱い
  • 辞書やファクトリー関数による柔軟な設計
  • より簡潔で実用的なコード

実際の適用例と注意点

実用的な適用場面

  1. マルチプラットフォームアプリ:OS固有のファイルシステムアクセスオブジェクト群
  2. データベース抽象化:データベース固有のDAO(Data Access Object)群
  3. テスト環境:本番用/テスト用のサービスオブジェクト群
  4. プラグインアーキテクチャ:プラグイン固有の機能コンポーネント群

注意点

  • 過度な抽象化を避ける:関連性の薄いオブジェクトを無理にファミリー化しない
  • Factory Methodとの使い分け:単一オブジェクトならFactory Methodで十分
  • 設定管理:どのファクトリーを使用するかの判断ロジックを明確にする

まとめ:両言語での実装比較

特性 Java Python
実装アプローチ インターフェース中心の階層設計 クラスオブジェクトや辞書を活用
型安全性 コンパイル時チェックで高い安全性 型ヒントで改善可能、実行時チェック
コードの複雑さ 明示的だが冗長になりがち 簡潔で柔軟、可読性に優れる
拡張性 インターフェース変更時の影響が明確 動的な拡張が容易
適用場面 大規模システム、厳密な仕様が必要 プロトタイピング、小〜中規模システム

Abstract Factoryパターンは、両言語で実装スタイルは大きく異なりますが、**「関連するオブジェクトファミリーの生成をカプセル化し、クライアントコードから具象クラスを分離する」**という本質的な目的は共通です。

Javaは厳密な型システムで安全性と保守性を重視し、Pythonは言語の柔軟性を活かして実用性と簡潔性を追求します。プロジェクトの要件と特性に応じて、適切なアプローチを選択することが重要です。

次回は、既存のオブジェクトをテンプレートとして新しいオブジェクトを効率的に作成するPrototypeパターンについて解説します。お楽しみに!

次回予告:「Day 7 Prototypeパターン:既存のオブジェクトから新しいオブジェクトを作る」

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?