🤪

デザインパターン入門Factory Methodパターンについて

2023/03/10に公開
  1. Factory Methodパターンとは何か?
    • ファクトリーメソッドとは何か?
    • オブジェクトの生成をサブクラスに委譲するパターン
    • オブジェクトの生成処理を一元化することで柔軟性を向上させる
  2. Factory Methodパターンの実装
    • 抽象クラスの作成
    • ファクトリーメソッドの実装
    • クラスの継承と具象クラスの作成
  3. Factory Methodパターンの具体例
    • ボタンオブジェクトの生成例
    • ファイル入出力の具象クラスの作成
  4. Factory Methodパターンの利用例
    • GUIフレームワークのSwing
    • Spring FrameworkのBeanFactory
  5. Factory Methodパターンのまとめ
    • ファクトリーメソッドパターンのメリット
    • ファクトリーメソッドパターンの欠点
    • ファクトリーメソッドパターンとAbstract Factoryパターンの違い

1. Factory Methodパターンとは何か?

Factory Methodパターンは、オブジェクトを生成する方法を規定するデザインパターンの一つです。このパターンは、具体的なオブジェクトの生成をサブクラスに任せることで、親クラスとサブクラスの間の疎結合を実現します。

疎結合とは、オブジェクト間の依存度を下げることで、コードの修正や保守がしやすくなることを指します。Factory Methodパターンでは、親クラスはオブジェクトを生成するためのインタフェースを定義し、具体的なオブジェクト生成はサブクラスに委ねます。

具体的には、親クラスでインタフェースを定義し、サブクラスでそのインタフェースを実装することで、オブジェクトの生成方法を変更できます。これにより、コードの再利用性や保守性を高めることができます。

2. Factory Methodパターンの実装

Factory Methodパターンを実装するには、まずFactoryインタフェースを定義する必要があります。このインタフェースは、製品を作成するメソッドを定義します。そして、具体的な製品を作成するための具体的なファクトリクラスを作成します。ファクトリクラスは、Factoryインタフェースを実装する必要があります。

以下に、Factory Methodパターンの実装例を示します。ここでは、商品を作成するためのFactoryインタフェースと、具体的なファクトリクラスとして、CarFactoryとBikeFactoryを作成します。CarFactoryとBikeFactoryは、Factoryインタフェースを実装しており、getProductメソッドを実装しています。

// Factoryインタフェース
public interface Factory {
    Product getProduct();
}

// 製品
public interface Product {
    void print();
}

// 具体的な製品
public class Car implements Product {
    @Override
    public void print() {
        System.out.println("This is a car.");
    }
}

public class Bike implements Product {
    @Override
    public void print() {
        System.out.println("This is a bike.");
    }
}

// 具体的なファクトリクラス
public class CarFactory implements Factory {
    @Override
    public Product getProduct() {
        return new Car();
    }
}

public class BikeFactory implements Factory {
    @Override
    public Product getProduct() {
        return new Bike();
    }
}

これで、Factory Methodパターンを使って、CarFactoryやBikeFactoryからCarやBikeを作成することができます。

Factory carFactory = new CarFactory();
Product car = carFactory.getProduct();
car.print(); // This is a car.

Factory bikeFactory = new BikeFactory();
Product bike = bikeFactory.getProduct();
bike.print(); // This is a bike.

このように、Factory Methodパターンは、製品を作成するための共通インタフェースを提供し、具体的な製品の作成をファクトリクラスに委譲することで、柔軟で拡張性のあるコードを実現します。

3. Factory Methodパターンの具体例

Factory MethodパターンはGUIフレームワークなどでよく使われます。例えば、ボタンのようなGUIコンポーネントを生成する場合、通常は以下のようにしてオブジェクトを生成します。

Button button = new Button();

しかし、Factory Methodパターンを使うことで、以下のようにボタンオブジェクトを生成することができます。

Button button = ButtonFactory.createButton();

ButtonFactoryクラスは、Buttonオブジェクトを生成するためのファクトリメソッドを提供します。

public class ButtonFactory {
    public static Button createButton() {
        return new Button();
    }
}

こうすることで、Buttonオブジェクトの生成に必要な処理をカプセル化し、コードの保守性や可読性を高めることができます。

また、ButtonFactoryクラスには、Buttonオブジェクトを生成するだけでなく、必要な初期化処理を行うメソッドを定義することもできます。例えば、以下のようにcreateSubmitButton()メソッドを定義することで、送信ボタンを生成するための初期化処理を追加することができます。

public class ButtonFactory {
    public static Button createButton() {
        return new Button();
    }
    
    public static Button createSubmitButton() {
        Button button = new Button();
        button.setText("Submit");
        button.setButtonType(ButtonType.SUBMIT);
        return button;
    }
}

このように、Factory Methodパターンを使うことで、オブジェクト生成処理に必要な処理をカプセル化し、コードの保守性や可読性を高めることができます。

ファイル入出力の具象クラスを作成する際には、Factory Methodパターンを適用することが有用です。

例えば、ファイルを読み書きするプログラムでは、データを読み込むクラスとデータを書き込むクラスを実装する必要があります。しかし、ファイルの種類や場所によっては、読み込みや書き込みを行う方法が異なる場合があります。例えば、ファイルの種類がテキストファイルかバイナリファイルかによって、読み込みや書き込みを行うためのクラスが異なる場合があります。

このような場合にFactory Methodパターンを使用すると、抽象的なI/O処理を行うインターフェースを定義し、その具象クラスをファクトリーメソッドによって生成することができます。

たとえば、以下のような抽象的なI/O処理のインターフェースを定義することができます。

public interface FileReaderWriter {
    public String readFile(String path);
    public void writeFile(String path, String data);
}

そして、このインターフェースを実装する具象クラスをファクトリーメソッドによって生成することができます。以下は、ファクトリーメソッドを用いて具象クラスを生成する例です。

public class FileReaderWriterFactory {
    public static FileReaderWriter createFileReaderWriter(String path) {
        if (path.endsWith(".txt")) {
            return new TextFileReaderWriter();
        } else if (path.endsWith(".bin")) {
            return new BinaryFileReaderWriter();
        } else {
            throw new RuntimeException("Unknown file type");
        }
    }
}

このように、Factory Methodパターンを適用することで、抽象的なI/O処理を実装するインターフェースと、具象クラスの生成を切り離すことができます。

4. Factory Methodパターンの利用例

Factory Methodパターンは、多くのフレームワークやライブラリに利用されています。ここでは、代表的な2つの利用例であるGUIフレームワークのSwingとSpring FrameworkのBeanFactoryについて説明します。

まず、GUIフレームワークのSwingにおいては、ウィンドウやボタンなどのGUIコンポーネントを生成するためにFactory Methodパターンが利用されています。Swingにおいては、JFrameやJButtonなどのGUIコンポーネントがありますが、これらのコンポーネントはFactory Methodパターンを用いて生成されます。具体的には、JFrameを生成するJFrameFactoryやJButtonを生成するJButtonFactoryなどのFactoryクラスが存在し、これらのFactoryクラスが具体的なJFrameやJButtonを生成します。

次に、Spring FrameworkのBeanFactoryについて説明します。Spring Frameworkは、DI(Dependency Injection)やIoC(Inversion of Control)といった概念を用いた軽量なフレームワークです。BeanFactoryは、Spring FrameworkにおけるDIの実現において重要な役割を担っています。BeanFactoryは、Spring Frameworkが管理するオブジェクト(Bean)を生成するためのFactory Methodパターンを利用しています。具体的には、BeanFactoryはBeanDefinitionという概念を用いて、Beanの生成方法を定義します。そして、BeanFactoryは定義されたBeanDefinitionに基づいて、必要なタイミングでBeanを生成します。

以上のように、Factory Methodパターンは、多くのフレームワークやライブラリにおいて利用され、オブジェクトの生成において柔軟性を提供しています。

5. Factory Methodパターンのまとめ

Factory Methodパターンについてまとめてみましょう。

まず、Factory Methodパターンのメリットは、オブジェクトの生成処理を抽象化し、実装を切り替えることが容易になる点です。具体的には、インスタンス生成処理を担当するファクトリーメソッドを定義し、このメソッドを実装することで、生成するオブジェクトの種類や生成方法を柔軟に変更することができます。また、オブジェクト生成処理を外部化することで、コードの保守性や再利用性を高めることができます。

一方、Factory Methodパターンの欠点は、新しいオブジェクトを生成するために新たなクラスを作成する必要があることです。つまり、オブジェクト生成処理を追加するためには、新しいファクトリーメソッドを実装する必要があります。また、Factory Methodパターンを適用する場合、インスタンスの生成が重要な処理である必要があります。そうでなければ、Factory Methodパターンの恩恵を受けることができません。

Factory MethodパターンとAbstract Factoryパターンの違いは、抽象ファクトリーと具象ファクトリーの違いにあります。Abstract Factoryパターンは、複数の関連するオブジェクトを生成する場合に適しており、抽象ファクトリーで複数のFactory Methodを定義して、一括してオブジェクト生成処理を行います。一方、Factory Methodパターンは、単一のオブジェクトを生成する場合に適しており、1つのファクトリーメソッドを定義してオブジェクト生成処理を行います。

以上がFactory Methodパターンについてのまとめです。オブジェクト生成処理が重要である場合には、Factory Methodパターンを使用して柔軟性の高いコードを作成することができます。

Discussion