🫥

初心者のためのFactory Methodパターン入門

に公開

はじめに

プログラミングを学ぶ過程で、コードの再利用性や保守性を高めるための「デザインパターン」という概念に出会うことでしょう。その中でも今回は、オブジェクト指向プログラミングにおける基本的かつ重要なデザインパターンである「Factory Method(ファクトリーメソッド)パターン」について解説します。
このパターンは初めて学ぶ人にとっては少し難解に感じるかもしれませんが、理解すると多くの場面で活用できる便利なツールとなります。この記事では、Factory Methodパターンの基本概念から実装例、活用シーンまでをわかりやすく説明していきます。

Factory Methodパターンとは?

Factory Methodは、生成に関するデザインパターンの一つで、スーパークラスでオブジェクトを作成するためのインターフェースが決まっています。しかし、サブクラスでは作成されるオブジェクトの型を変更することができます。もう少しわかりやすく言うと、「コンストラクタの代わりにインスタンスの工場となるメソッドを作る」ことがこのパターンの核心です。オブジェクトの生成方法をサブクラスに任せることで、柔軟性と拡張性を持ったコードを実現します。

なぜFactory Methodパターンが必要なのか?

プログラムを開発していると、「新しい種類のオブジェクトを追加したい」という要求がよく発生します。このとき単純に新しいクラスを追加すると、そのクラスを利用するコード全体に変更を加える必要が出てきます。
Factory Methodパターンを使用すると、オブジェクトの生成処理を親クラスから切り離し、サブクラスに任せることができます。これにより、新しい種類のオブジェクトを追加する際の変更箇所が最小限に抑えられます。

Factory Methodパターンの構造

Factory Methodパターンのクラス図では、抽象クラスCreatorは抽象クラスProductを生成するメソッドを持ちます。クラスConcreteCreatorは具象クラスであり、ConcreteProductを生成するメソッドを持ちます。ConcreteProductはProductの具象クラスです。
一般的に以下の4つの要素から構成されます:

  1. Creator(作成者):抽象クラスまたはインターフェースで、Factoryメソッドを宣言します
  2. ConcreteCreator(具体的作成者):Creatorを継承し、Factoryメソッドを実装してConcreteProductのインスタンスを返します
  3. Product(製品):生成されるオブジェクトのインターフェースを定義します
  4. ConcreteProduct(具体的製品):Productを実装した具体的なクラスです
//Product: 保存処理を行うインターフェース
interface FileSaver {
    save(data: string): void;
}

//ConcreteProduct: CSVファイルの保存を行うクラス
class CSVFileSaver implements FileSaver {
    save(data: string): void {
        console.log(`データをCSV形式で保存しました: ${data}`);
        // CSV保存処理
    }
}

//ConcreteProduct: HTMLファイルの保存を行うクラス
class HTMLFileSaver implements FileSaver {
    save(data: string): void {
        console.log(`データをHTML形式で保存しました: ${data}`)
        //HTML保存処理
    }
}

// Creator: ファクトリーメソッドを定義する抽象クラス
abstract class FileExporter {
    protected abstract createFileSaver(): FileSaver;

    //テンプレートメソッド
    public exportData(data: string): void {
        const saver = this.createFileSaver();
        saver.save(data);
    }
}

//ConcreteCreator: CSVエクスポート用のクラス
class CSVExporter extends FileExporter {
    protected override createFileSaver(): FileSaver {
        return new CSVFileSaver();
    }
}

// ConcreteCreator: HTMLエクスポート用のクラス
class HTMLExporter extends FileExporter {
    protected override createFileSaver(): FileSaver {
        return new HTMLFileSaver();
    }
}

//クライアントコード
function main(): void {
    const data = 'サンプルデータ';

    const csvExporter = new CSVExporter();
    csvExporter.exportData(data);  // データをCSV形式で保存
    
    const htmlExporter = new HTMLExporter();
    htmlExporter.exportData(data);  // データをHTML形式で保存
}

//実行
main();

この例では、FileExporterクラスがインスタンス生成の枠組みを提供し、具体的なインスタンスの生成はCSVExporterHTMLExporterが担当しています。
「Factory Method(ファクトリメソッド)」パターンは、インスタンスの生成に「Template Method(テンプレートメソッド)」を利用したパターンであり、インスタンス生成の処理の流れは「親クラス」で決定し、継承先の子クラスで具体的なインスタンスの生成処理などを実装していきます。

Factory Methodパターンのメリット

  1. クラス間の結合度を下げる:Factory Methodパターンは、具体的なオブジェクトの生成をサブクラスに任せることで、親クラスとサブクラスの間の疎結合を実現します。疎結合とは、オブジェクト間の依存度を下げることで、コードの修正や保守がしやすくなることを指します。
  2. 拡張性の向上:新しい種類のオブジェクトを追加する場合、既存のコードを変更せずにサブクラスを追加するだけで対応できます。
  3. テストの容易さ:モックオブジェクトを使ったテストが容易になります。

Factory Methodパターンの活用シーン

以下のような状況でFactory Methodパターンが特に役立ちます:

  1. 将来的に新しい種類のオブジェクトが追加される可能性があるとき
  2. オブジェクト生成のロジックを一箇所に集中させたいとき
  3. オブジェクト生成の詳細を隠蔽したいとき
  4. クラス間の依存関係を減らしたいとき

設計時には「デザインパターンを適用しよう」という意識よりも、オブジェクト指向設計におけるクラスの責務分割の一環としてクラス設計し、デザインパターンは参考程度にとどめるという視点で捉えることも有効です。

Factory Methodパターンと他のパターンとの違い

Factoryパターンでは、生成するオブジェクトの種類の増加や生成処理手順が複雑化に伴って、ファクトリ内の処理が冗長で複雑化してしまいます。一方、Factory Methodパターンでは、サブクラスで生成処理を行うため生成処理を簡潔にすることができますが、生成するオブジェクトの種類を変更する場合、ファクトリクラスを切り替える必要があります。
また、Factory MethodパターンとAbstract Factoryパターンはよく混同されますが、大きな違いがあります。Factory Methodパターンは単一の製品を作成するのに対し、Abstract Factoryパターンは関連する製品のファミリーを作成するためのものです。

まとめ

Factory Methodパターンは、オブジェクトの生成方法を柔軟にし、コードの再利用性や保守性を高めるための強力なツールです。このパターンを理解し活用することで、より柔軟で拡張性の高いコードを書くことができるようになります。
特に新しいクラスの追加が頻繁に発生するようなプロジェクトでは、Factory Methodパターンを導入することで、変更の影響範囲を最小限に抑えることができます。
このパターンは初心者にとっては少し難しく感じるかもしれませんが、オブジェクト指向プログラミングの理解を深める上で重要な概念です。ぜひ実際のプロジェクトで試してみてください。

Discussion