プログラミング

ファクトリメソッドとは?オブジェクト生成を委譲する柔軟なデザインパターン

ファクトリメソッドは、オブジェクトの生成処理をサブクラスに委ねるデザインパターンです。

これにより、生成する対象のクラスに直接依存せず、柔軟な設計や拡張性を実現できます。

たとえば、共通のProductインターフェースを複数の具体的製品で実装することで、クライアントは生成方法を意識せずに利用でき、保守性の高いシステム構築が可能となります。

ファクトリメソッドの基本

オブジェクト生成処理の委譲について

オブジェクトの生成処理を、クラスそのものが直接行うのではなくサブクラスに任せる仕組みです。

この方式を取り入れると、クライアント側でどの具体的なクラスのインスタンスを作るか気にする必要がなくなります。

その結果、インスタンスの生成方法に柔軟性が生まれ、後から変更や拡張がしやすくなります。

柔軟性と拡張性の向上理由

オブジェクト生成の処理が一箇所にまとまることで、変更が必要になった際の影響を小さくできます。

また、機能追加や新たな製品クラスを導入する際に、既存のコードへ大きな手直しをする必要がなくなるため、保守性が良くなります。

  • クラス同士の結合度が低くなる
  • 新たな製品が追加しやすくなる
  • 生成処理の集中管理が実現できる

設計パターンとしての特徴と構成要素

各役割の概要

Productの役割

生成されるオブジェクトを表す共通のインターフェースです。

この仕組みによって、クライアント側は具体的な実装に依存する必要がなくなります。

Creatorの役割

ファクトリメソッドとしての役割を担う抽象クラスです。

ここに定義されたメソッドが、実際のオブジェクト生成処理を委譲するための窓口となります。

具体的実装クラスの関係

Concrete Productの役割

Productインターフェースを実装した具象クラスです。

実際に動作するオブジェクトを提供する役割を持ちます。

Concrete Creatorの役割

Creatorクラスのサブクラスとして具象的に実装したクラスです。

ファクトリメソッド内でどのConcrete Productを返すか決定し、インスタンスを作成して返します。

実装例による適用事例

Javaでのサンプルコード解説

コード構造と動作の流れ

Javaでの実装例では、以下のような構成となっています。

  • Productインターフェース

クライアントと連携する共通メソッドを定義している

  • ConcreteProductAConcreteProductB

実際の処理を実装する具象クラス

  • Creator抽象クラス

factoryMethodというメソッドを定義

  • ConcreteCreatorAConcreteCreatorB

それぞれ異なるConcrete Productを生成する処理を記述

具体的には、下記のコードがその流れを示しています。

// Productインターフェース
public interface Product {
    void use();
}
// ConcreteProductAクラス
public class ConcreteProductA implements Product {
    public void use() {
        System.out.println("ConcreteProductAのuseメソッドが呼ばれました。");
    }
}
// ConcreteProductBクラス
public class ConcreteProductB implements Product {
    public void use() {
        System.out.println("ConcreteProductBのuseメソッドが呼ばれました。");
    }
}
// Creator抽象クラス
public abstract class Creator {
    public abstract Product factoryMethod();
}
// ConcreteCreatorAクラス
public class ConcreteCreatorA extends Creator {
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}
// ConcreteCreatorBクラス
public class ConcreteCreatorB extends Creator {
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}
// 実行クラス
public class FactoryMethodDemo {
    public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.factoryMethod();
        productA.use();
        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.factoryMethod();
        productB.use();
    }
}

このコードの流れは、まずCreatorの具体的な実装によって作成されるProductインターフェースの実装クラスを決定し、クライアント側では生成したオブジェクトの操作だけに集中する仕組みとなっています。

他言語での応用例

Java以外の言語でも同じ考え方が適用できます。

例えば、PythonやC++では以下の点に注意しながら実装することが可能です。

  • インターフェースや抽象クラスの概念を用いる
  • サブクラスにオブジェクト生成処理を任せる
  • シンプルなファクトリメソッドを実装する

以下はPythonでの簡単な例です。

from abc import ABC, abstractmethod

# Productの共通インターフェース

class Product(ABC):
    @abstractmethod
    def use(self):
        pass

# ConcreteProductAクラス

class ConcreteProductA(Product):
    def use(self):
        print("ConcreteProductAのuseメソッドが呼ばれました。")

# ConcreteProductBクラス

class ConcreteProductB(Product):
    def use(self):
        print("ConcreteProductBのuseメソッドが呼ばれました。")

# Creator抽象クラス

class Creator(ABC):
    @abstractmethod
    def factory_method(self):
        pass

# ConcreteCreatorAクラス

class ConcreteCreatorA(Creator):
    def factory_method(self):
        return ConcreteProductA()

# ConcreteCreatorBクラス

class ConcreteCreatorB(Creator):
    def factory_method(self):
        return ConcreteProductB()

# 実行例

if __name__ == "__main__":
    creator = ConcreteCreatorA()
    product = creator.factory_method()
    product.use()

この例でも、各クラスの役割が明確に分けられています。

クライアントは具体的な実装に依存せずに、オブジェクト生成の結果として得た共通のインターフェースをもとに処理を行うことができます。

利用時のポイント

メリットの詳細

ファクトリメソッドパターンを利用するメリットは多く、以下の点が挙げられます。

  • オブジェクト生成方法の変更が、クラス全体に波及しにくい
  • 新たな製品クラスの追加時に、既存のコード修正が少なく済む
  • インターフェースを通じたやりとりにより、コードの保守や拡張が楽になる

利用上の注意点と留意事項

実装する際には、いくつか注意する点が存在します。

  • 過度にパターンを使うと、設計が複雑になりすぎる可能性がある
  • 開発チーム全体で、パターンの利点や実装意図を十分に共有しておく必要がある
  • 使用する言語やフレームワークに合わせた適切な実装方法を検討する必要がある

まとめ

ファクトリメソッドパターンは、オブジェクト生成の責任をサブクラスに委ねる仕組みで、クライアント側の負担を軽減しながら柔軟な設計を実現できます。

設計の見通しが良くなるため、機能拡張や保守がしやすいメリットがあります。

その一方で、過度な利用は設計の複雑さを招く可能性もあるため、プロジェクト全体の構成や目的に合わせて適切に選択することが重要です。

関連記事

Back to top button