Factory Methodパターン実装研究2025|柔軟なオブジェクト生成の仕組み

Factory Methodパターン実装研究2025|柔軟なオブジェクト生成の仕組み

更新日:2025年10月15日

オブジェクト指向プログラミングにおいて、具体的なクラスを指定せずにオブジェクトを生成したい場合があります。 例えば、ドキュメント作成アプリケーションで、PDFドキュメントやWordドキュメントなど、 異なる形式のドキュメントを統一的に扱いたいケースです。 Factory Methodパターンは、オブジェクト生成の責任をサブクラスに委譲することで、 柔軟で拡張性の高い設計を実現します。 個人的な関心から調査・考察してみましたので、参考になれば幸いです。

Factory Methodパターンが解決する問題

アプリケーション開発において、生成すべきオブジェクトの具体的な型が実行時まで決まらない、 または頻繁に変更される可能性がある場合があります。 このような状況で具体的なクラスを直接インスタンス化すると、 コードの柔軟性が失われ、保守が困難になります。

具体的なクラスに依存する問題

// 問題のあるコード:具体的なクラスに直接依存
public class DocumentProcessor {
    
    public void processDocument(String type) {
        Document doc;
        
        // 条件分岐で具体的なクラスを生成
        if (type.equals("PDF")) {
            doc = new PdfDocument();
        } else if (type.equals("WORD")) {
            doc = new WordDocument();
        } else if (type.equals("EXCEL")) {
            doc = new ExcelDocument();
        } else {
            throw new IllegalArgumentException("未対応の形式");
        }
        
        doc.open();
        doc.process();
        doc.save();
    }
}

// 新しい形式を追加する度に、このメソッドを修正する必要がある
// → Open-Closed原則の違反
重要なポイント
上記の実装では、新しいドキュメント形式を追加する度に既存のコードを修正する必要があります。 これは「拡張に開いているが、修正に閉じている」というOpen-Closed原則に違反しており、 バグの混入リスクが高まります。

Factory Methodパターンによる解決

Factory Methodパターンは、オブジェクト生成のインターフェースを定義しながら、 実際にどのクラスをインスタンス化するかはサブクラスに決定させます。 これにより、既存のコードを変更せずに新しい型を追加できるようになります。

「Factory Methodパターンは、オブジェクト生成のためのインターフェースを定義するが、 どのクラスをインスタンス化するかはサブクラスに決定させる」 - GoF(Gang of Four)デザインパターン

パターンの構造とUML図解

基本的な構造

Factory Methodパターンは以下の4つの役割から構成されます:

構成要素の役割

  • Product(製品):生成されるオブジェクトの共通インターフェース
  • ConcreteProduct(具体的な製品):実際に生成される具体的なクラス
  • Creator(作成者):Factory Methodを宣言する抽象クラス
  • ConcreteCreator(具体的な作成者):Factory Methodを実装し、ConcreteProductを生成

動作の流れ

クライアントはCreatorのメソッドを呼び出し、CreatorはFactory Methodを使用してProductを生成します。 具体的にどのConcreteProductが生成されるかは、どのConcreteCreatorが使用されるかによって決まります。

Java実装パターンと実践例

1. 基本的な実装パターン

Product(製品)インターフェース

// 生成されるオブジェクトの共通インターフェース
public interface Document {
    void open();
    void save();
    void close();
    String getFormat();
}

ConcreteProduct(具体的な製品)

// PDFドキュメント
public class PdfDocument implements Document {
    
    @Override
    public void open() {
        System.out.println("PDFドキュメントを開きます");
    }
    
    @Override
    public void save() {
        System.out.println("PDFドキュメントを保存します");
    }
    
    @Override
    public void close() {
        System.out.println("PDFドキュメントを閉じます");
    }
    
    @Override
    public String getFormat() {
        return "PDF";
    }
}

// Wordドキュメント
public class WordDocument implements Document {
    
    @Override
    public void open() {
        System.out.println("Wordドキュメントを開きます");
    }
    
    @Override
    public void save() {
        System.out.println("Wordドキュメントを保存します");
    }
    
    @Override
    public void close() {
        System.out.println("Wordドキュメントを閉じます");
    }
    
    @Override
    public String getFormat() {
        return "WORD";
    }
}

// Excelドキュメント
public class ExcelDocument implements Document {
    
    @Override
    public void open() {
        System.out.println("Excelドキュメントを開きます");
    }
    
    @Override
    public void save() {
        System.out.println("Excelドキュメントを保存します");
    }
    
    @Override
    public void close() {
        System.out.println("Excelドキュメントを閉じます");
    }
    
    @Override
    public String getFormat() {
        return "EXCEL";
    }
}

Creator(作成者)抽象クラス

// Factory Methodを定義する抽象クラス
public abstract class DocumentCreator {
    
    // Factory Method(抽象メソッド)
    protected abstract Document createDocument();
    
    // テンプレートメソッド:Factory Methodを使用
    public void processDocument() {
        Document doc = createDocument();
        
        doc.open();
        System.out.println("ドキュメント形式: " + doc.getFormat());
        
        // 共通の処理
        performCommonOperations(doc);
        
        doc.save();
        doc.close();
    }
    
    // 共通処理
    private void performCommonOperations(Document doc) {
        System.out.println("共通処理を実行中...");
        // 実際の処理
    }
}

ConcreteCreator(具体的な作成者)

// PDFドキュメントを生成するCreator
public class PdfDocumentCreator extends DocumentCreator {
    
    @Override
    protected Document createDocument() {
        return new PdfDocument();
    }
}

// Wordドキュメントを生成するCreator
public class WordDocumentCreator extends DocumentCreator {
    
    @Override
    protected Document createDocument() {
        return new WordDocument();
    }
}

// Excelドキュメントを生成するCreator
public class ExcelDocumentCreator extends DocumentCreator {
    
    @Override
    protected Document createDocument() {
        return new ExcelDocument();
    }
}

2. 使用例

public class FactoryMethodDemo {
    
    public static void main(String[] args) {
        // PDFドキュメントの処理
        DocumentCreator pdfCreator = new PdfDocumentCreator();
        pdfCreator.processDocument();
        
        System.out.println("\n---\n");
        
        // Wordドキュメントの処理
        DocumentCreator wordCreator = new WordDocumentCreator();
        wordCreator.processDocument();
        
        System.out.println("\n---\n");
        
        // Excelドキュメントの処理
        DocumentCreator excelCreator = new ExcelDocumentCreator();
        excelCreator.processDocument();
    }
}

/* 実行結果:
PDFドキュメントを開きます
ドキュメント形式: PDF
共通処理を実行中...
PDFドキュメントを保存します
PDFドキュメントを閉じます

---

Wordドキュメントを開きます
ドキュメント形式: WORD
共通処理を実行中...
Wordドキュメントを保存します
Wordドキュメントを閉じます

---

Excelドキュメントを開きます
ドキュメント形式: EXCEL
共通処理を実行中...
Excelドキュメントを保存します
Excelドキュメントを閉じます
*/

3. 実践的な例:通知システム

// 通知インターフェース
public interface Notification {
    void send(String message, String recipient);
}

// Email通知
public class EmailNotification implements Notification {
    
    @Override
    public void send(String message, String recipient) {
        System.out.println("Emailを送信: " + recipient);
        System.out.println("内容: " + message);
    }
}

// SMS通知
public class SmsNotification implements Notification {
    
    @Override
    public void send(String message, String recipient) {
        System.out.println("SMSを送信: " + recipient);
        System.out.println("内容: " + message);
    }
}

// Push通知
public class PushNotification implements Notification {
    
    @Override
    public void send(String message, String recipient) {
        System.out.println("Push通知を送信: " + recipient);
        System.out.println("内容: " + message);
    }
}

// 通知作成者(抽象クラス)
public abstract class NotificationCreator {
    
    protected abstract Notification createNotification();
    
    public void notifyUser(String message, String recipient) {
        Notification notification = createNotification();
        
        // 送信前の共通処理
        logNotification(message, recipient);
        
        notification.send(message, recipient);
        
        // 送信後の共通処理
        updateStatistics();
    }
    
    private void logNotification(String message, String recipient) {
        System.out.println("通知ログ: " + recipient + " へ送信準備");
    }
    
    private void updateStatistics() {
        System.out.println("統計情報を更新");
    }
}

// 具体的な作成者
public class EmailNotificationCreator extends NotificationCreator {
    
    @Override
    protected Notification createNotification() {
        return new EmailNotification();
    }
}

public class SmsNotificationCreator extends NotificationCreator {
    
    @Override
    protected Notification createNotification() {
        return new SmsNotification();
    }
}

public class PushNotificationCreator extends NotificationCreator {
    
    @Override
    protected Notification createNotification() {
        return new PushNotification();
    }
}

// 使用例
public class NotificationSystem {
    
    public static void main(String[] args) {
        String message = "重要なお知らせがあります";
        
        // Emailで通知
        NotificationCreator emailCreator = new EmailNotificationCreator();
        emailCreator.notifyUser(message, "user@example.com");
        
        System.out.println("\n---\n");
        
        // SMSで通知
        NotificationCreator smsCreator = new SmsNotificationCreator();
        smsCreator.notifyUser(message, "090-1234-5678");
        
        System.out.println("\n---\n");
        
        // Push通知
        NotificationCreator pushCreator = new PushNotificationCreator();
        pushCreator.notifyUser(message, "device-token-12345");
    }
}

4. パラメータ化されたFactory Method

// パラメータに基づいてProductを選択する実装
public class ParameterizedDocumentCreator extends DocumentCreator {
    
    private String documentType;
    
    public ParameterizedDocumentCreator(String documentType) {
        this.documentType = documentType;
    }
    
    @Override
    protected Document createDocument() {
        switch (documentType.toUpperCase()) {
            case "PDF":
                return new PdfDocument();
            case "WORD":
                return new WordDocument();
            case "EXCEL":
                return new ExcelDocument();
            default:
                throw new IllegalArgumentException("未対応の形式: " + documentType);
        }
    }
}

// 使用例
public class ParameterizedDemo {
    
    public static void main(String[] args) {
        // 設定ファイルやユーザー入力から形式を取得
        String format = "PDF";
        
        DocumentCreator creator = new ParameterizedDocumentCreator(format);
        creator.processDocument();
    }
}

使用シーンとベストプラクティス

適切な使用シーン
• ドキュメント処理システム(PDF、Word、Excelなど)
• 通知システム(Email、SMS、Pushなど)
• データベース接続(MySQL、PostgreSQL、Oracleなど)
• UI コンポーネント生成(Windows、Mac、Linuxなど)
• ロギングシステム(ファイル、データベース、クラウドなど)

メリットとデメリット

観点 メリット デメリット
柔軟性 新しい型の追加が容易 クラス数の増加
保守性 Open-Closed原則に準拠 小規模プロジェクトでは過剰設計
テスタビリティ モックオブジェクトの注入が容易 初期学習コストが高い
結合度 具体的なクラスへの依存を排除 抽象化レイヤーの追加

ベストプラクティス

効果的な実装のポイント

  • インターフェースの設計:Productインターフェースはシンプルで明確に
  • 命名規則:XxxCreator、XxxFactoryなど一貫した命名を使用
  • 共通処理の集約:Creator の抽象クラスに共通処理を実装
  • 拡張性の考慮:新しい型の追加を見越した設計

他のパターンとの関連

関連するデザインパターン
Abstract Factory: Factory Methodを複数組み合わせて、関連するオブジェクト群を生成
Template Method: Factory MethodはTemplate Methodパターンの特殊ケース
Prototype: 複製によるオブジェクト生成の代替手段
Strategy: 生成戦略を動的に切り替える場合に組み合わせて使用

アンチパターンと注意点

避けるべき実装
• Factory Methodにビジネスロジックを含める
• 過度に複雑な条件分岐をFactory Method内に記述
• Creator階層が深くなりすぎる設計
• 単純なオブジェクト生成にFactory Methodを適用

モダンJavaでの実装

Java 8以降では、ラムダ式やメソッド参照を活用することで、 よりシンプルな実装が可能になります。

// 関数型インターフェースを使用した実装
import java.util.function.Supplier;
import java.util.Map;
import java.util.HashMap;

public class FunctionalDocumentFactory {
    
    private static final Map> factories = new HashMap<>();
    
    static {
        // メソッド参照で登録
        factories.put("PDF", PdfDocument::new);
        factories.put("WORD", WordDocument::new);
        factories.put("EXCEL", ExcelDocument::new);
    }
    
    public static Document createDocument(String type) {
        Supplier factory = factories.get(type.toUpperCase());
        if (factory == null) {
            throw new IllegalArgumentException("未対応の形式: " + type);
        }
        return factory.get();
    }
    
    public static void main(String[] args) {
        Document pdf = createDocument("PDF");
        pdf.open();
        
        Document word = createDocument("WORD");
        word.open();
    }
}
参考・免責事項
本記事は2025年10月15日時点の情報に基づいて作成されています。 Java 8以降のバージョンを想定しており、実装例は教育目的で提供されています。 記事内容は個人的な考察に基づくものであり、 実際のプロジェクトでの使用にあたっては、要件に応じた適切な判断をお願いします。 重要な設計決定については、複数の情報源を参考にし、チームで検討することを推奨します。