技術(非IT系)

ファクトリパターンの実装方法は?手順と書き方も解説!(抽象ファクトリ:コンクリートファクトリ:インターフェース:クラス設計など)

当サイトでは記事内に広告を含みます

ファクトリパターンは概念として理解できても、実際の実装手順となると迷う方も多いのではないでしょうか。

どのようにインターフェースを設計し、どのようにコンクリートファクトリを書き、クライアントコードとどう連携させるか、という具体的な実装の流れを把握することが実践への第一歩です。

本記事では、ファクトリパターンの実装方法を段階的・体系的に解説し、Simple Factory・Factory Method・Abstract Factoryのそれぞれについて具体的なコードと手順を示します。

クラス設計の考え方からコードの書き方まで、実装に必要な知識をまとめていますので、ぜひ参考にしてください。

ファクトリパターン実装の全体的な手順と設計の流れ

それではまず、ファクトリパターンを実装するための全体的な手順と設計の流れについて解説していきます。

実装に入る前に、設計全体の構造を把握しておくことが、品質の高いコードを書く上で非常に重要です。

ファクトリパターンの実装は大きく「インターフェース設計」「具体クラスの実装」「ファクトリの実装」「クライアントコードの作成」の4ステップで進めます。

ステップ1:Productインターフェースの設計

ファクトリパターン実装の最初のステップは、Productインターフェース(または抽象クラス)の設計です。

このインターフェースが、クライアントコードと具体クラスをつなぐ「共通の契約」となります。

// Java:Productインターフェースの例

public interface Vehicle {

 String getType();

 int getSpeed();

 void drive();

}

// C#:Productインターフェースの例

public interface IVehicle {

 string GetType();

 int GetSpeed();

 void Drive();

}

インターフェース設計では、クライアントが利用する操作のみを定義し、具体的な実装詳細は含めないことが重要です。

メソッド名は操作の意図を明確に表す命名にし、将来的な拡張を考慮した設計を心がけましょう。

抽象クラスを使用するケースは、複数の具体クラスで共通する基本実装を提供したい場合に有効です。

ステップ2:ConcreteProduct(具体製品クラス)の実装

次のステップは、Productインターフェースを実装した具体クラス(ConcreteProduct)の作成です。

// Java:具体クラスの実装例

public class Car implements Vehicle {

 public String getType() { return “Car”; }

 public int getSpeed() { return 120; }

 public void drive() {

  System.out.println(“車で走行中:” + getSpeed() + “km/h”);

 }

}

public class Motorcycle implements Vehicle {

 public String getType() { return “Motorcycle”; }

 public int getSpeed() { return 180; }

 public void drive() {

  System.out.println(“バイクで走行中:” + getSpeed() + “km/h”);

 }

}

各ConcreteProductクラスはProductインターフェースのすべてのメソッドを実装し、それぞれ固有の振る舞いを定義します。

ConcreteProductクラスはファクトリを通じてのみ生成される前提で設計するため、コンストラクタのアクセス修飾子をpackage-private(Javaの場合)にする設計パターンもあります。

ステップ3:Creatorとファクトリメソッドの実装

ファクトリパターンの中核となるCreatorクラスとファクトリメソッドを実装します。

// Factory Methodパターン:Creatorの実装(Java)

public abstract class VehicleCreator {

 // ファクトリメソッド(サブクラスで実装)

 public abstract Vehicle createVehicle();

 // テンプレートメソッド(共通処理)

 public void deliverVehicle() {

  Vehicle v = createVehicle();

  System.out.println(v.getType() + “を配送します”);

  v.drive();

 }

}

// ConcreteCreatorの実装

public class CarCreator extends VehicleCreator {

 public Vehicle createVehicle() { return new Car(); }

}

public class MotorcycleCreator extends VehicleCreator {

 public Vehicle createVehicle() { return new Motorcycle(); }

}

CreatorクラスのファクトリメソッドはProductインターフェースを戻り値型として宣言し、具体クラスの型は返さないことが設計原則です。

テンプレートメソッドパターンと組み合わせることで、生成後の共通処理をCreatorに集約することもできます。

Abstract Factoryパターンの実装方法

続いては、より高度なAbstract Factory(抽象ファクトリ)パターンの実装方法を確認していきます。

Abstract Factoryは、関連する複数のオブジェクト群を一貫して生成するための強力なパターンです。

UIコンポーネントやデータベース接続など、複数の関連クラスをセットで管理したい場合に特に有効です。

Abstract Factoryの基本構造と実装手順

Abstract Factoryパターンは、関連する複数の製品群をまとめて生成するインターフェースを提供します。

// Abstract Factory インターフェース(Java)

public interface UIFactory {

 Button createButton();

 TextField createTextField();

 Dialog createDialog();

}

// Windows用の具体的なFactory

public class WindowsUIFactory implements UIFactory {

 public Button createButton() { return new WindowsButton(); }

 public TextField createTextField() { return new WindowsTextField(); }

 public Dialog createDialog() { return new WindowsDialog(); }

}

// Mac用の具体的なFactory

public class MacUIFactory implements UIFactory {

 public Button createButton() { return new MacButton(); }

 public TextField createTextField() { return new MacTextField(); }

 public Dialog createDialog() { return new MacDialog(); }

}

Abstract Factoryでは、一つのファクトリが複数種類のオブジェクトを生成するため、製品間の一貫性が自然に保たれます。

たとえばWindowsUIFactoryを使用している限り、生成されるすべてのUIコンポーネントがWindows風の見た目に統一されます。

クライアントコードの実装と注入方法

Abstract Factoryパターンにおけるクライアントコードの実装では、ファクトリの具体型ではなく抽象型(インターフェース)で受け取ることが重要です。

// クライアントコード(Java)

public class Application {

 private UIFactory factory;

 // ファクトリをコンストラクタインジェクションで受け取る

 public Application(UIFactory factory) {

  this.factory = factory;

 }

 public void buildUI() {

  Button btn = factory.createButton();

  TextField tf = factory.createTextField();

  btn.render();

  tf.render();

 }

}

// エントリポイント

UIFactory factory = new WindowsUIFactory(); // 環境に応じて切り替え

Application app = new Application(factory);

app.buildUI();

クライアントコードはUIFactoryインターフェースのみに依存しているため、WindowsUIFactoryをMacUIFactoryに差し替えるだけで全UIコンポーネントのプラットフォームを切り替えることができます。

このファクトリの差し替えは、設定ファイルや環境変数に応じてエントリポイントで行うのが一般的な実装パターンです。

ファクトリの登録と管理:ファクトリレジストリパターン

多くの種類のファクトリを管理する場面では、ファクトリレジストリパターンが有効です。

// ファクトリレジストリパターン(Java)

public class VehicleRegistry {

 private static Map<String, Supplier<Vehicle>> registry = new HashMap<>();

 public static void register(String type, Supplier<Vehicle> factory) {

  registry.put(type, factory);

 }

 public static Vehicle create(String type) {

  Supplier<Vehicle> factory = registry.get(type);

  if (factory == null) throw new IllegalArgumentException(“Unknown: ” + type);

  return factory.get();

 }

}

// 登録

VehicleRegistry.register(“car”, Car::new);

VehicleRegistry.register(“motorcycle”, Motorcycle::new);

// 使用

Vehicle v = VehicleRegistry.create(“car”);

ファクトリレジストリパターンでは、新しい製品クラスの追加時に既存のファクトリコードを修正することなく、レジストリへの登録だけで拡張が可能です。

これはOCP(オープン・クローズド原則)を高いレベルで実現する実装テクニックです。

実装における命名規則とコーディング規約

続いては、ファクトリパターン実装における命名規則とコーディング規約について確認していきます。

適切な命名規則に従うことで、コードの可読性・保守性が向上し、チーム内での共通認識も形成しやすくなります。

業界で広く採用されているベストプラクティスを把握しておきましょう。

Java・C#における標準的な命名規則

ファクトリパターンの実装では、役割が一目でわかる命名規則を採用することが重要です。

役割 Java命名例 C#命名例
Productインターフェース Vehicle、Animal、Logger IVehicle、IAnimal、ILogger
ConcreteProduct Car、Dog、FileLogger Car、Dog、FileLogger
AbstractCreator VehicleFactory、AnimalFactory VehicleFactory、AnimalFactory
ConcreteCreator CarFactory、DogFactory CarFactory、DogFactory
ファクトリメソッド create()、createVehicle() Create()、CreateVehicle()

C#では慣習としてインターフェース名の先頭に「I」を付ける命名規則が標準的です。

JavaではインターフェースにIプレフィックスを付けない場合が多く、クラス設計の意図をクラス名で表現することが重視されます。

ファクトリクラス名には「Factory」サフィックスを付けることで、一目でファクトリの役割であることが明確になります。

パッケージ・名前空間の構成方針

ファクトリパターンを含む設計のパッケージ構成も、コードの整理・管理において重要な要素です。

一般的なパッケージ構成として、製品インターフェースと具体クラスをそれぞれ別パッケージに分離する方法が推奨されます。

推奨パッケージ構成例(Java):

com.example.vehicle

├── Vehicle.java(インターフェース)

├── VehicleFactory.java(抽象Creator)

├── impl

│ ├── Car.java

│ ├── Motorcycle.java

│ ├── CarFactory.java

│ └── MotorcycleFactory.java

└── registry

 └── VehicleRegistry.java

インターフェースと実装クラスを分離することで、依存関係の方向が明確になり、コードの整理がしやすくなります。

チームのコーディング規約に基づき、パッケージ構成を統一することがプロジェクト全体のコード品質維持につながります。

実装時のよくある誤りと回避策

ファクトリパターンの実装でよく見られる誤りと、その回避策を確認しておきましょう。

最も多い誤りは、ファクトリメソッドの戻り値型を具体クラス型にしてしまうことです。これではインターフェースへの依存という恩恵が得られません。

次に、ファクトリクラス内でビジネスロジックを実装してしまうケースも問題です。ファクトリの責務はオブジェクト生成に限定するべきです。

また、過度なインターフェース化も避けるべき誤りです。必要のない抽象化はコードを複雑にするだけであり、YAGNI原則に反します。

実装前に「このファクトリパターンは本当に必要か?」という問いを常に持ち、シンプルな実装で済むケースでは無理にパターンを適用しないことが重要です。

まとめ

本記事では、ファクトリパターンの実装方法を、Simple Factory・Factory Method・Abstract Factoryの各パターンについて具体的なコードと共に解説しました。

実装の基本手順は「Productインターフェース設計→ConcreteProduct実装→Creator・ファクトリメソッド実装→クライアントコード作成」という流れです。

Abstract Factoryは関連する複数の製品群を一貫して管理する強力なパターンであり、UIコンポーネントやデータベース接続など実務でよく登場する場面で活躍します。

命名規則・パッケージ構成・よくある誤りを把握した上で実装に臨むことで、保守性・可読性の高いコードが実現できます。

本記事のコード例を参考に、実際のプロジェクトでファクトリパターンを活用してみてください。