[Design pattern] 1-3. ファクトリーメソッドパターン(Factory method pattern)


Study / Design pattern    作成日付 : 2021/06/23 19:45:37   修正日付 : 2021/06/23 19:46:01

こんにちは。明月です。


この投稿はデザインパターンのファクトリーメソッドパターン(Factory method pattern)に関する説明です。


以前の投稿でビルダーパターンに関して説明しました。

link - [Design pattern] 1-2. ビルダーパターン(Builder pattern)

ビルダーパターンは簡単に説明するとBuilderクラスとDirectorクラスの組み合いで一つのインスタンスを生成する生成パターンです。このファクトリーメソッドパターンもビルダーパターンと同じ生成パターンの一つで、インスタンスを生成するパターンです。

このファクトリーメソッドパターンはメソッドのパラメータにより生成されるインスタンスを変わる形のパターンです。


Reference - https://en.wikipedia.org/wiki/Factory_method_pattern
#pragma once
#include <stdio.h>
#include <iostream>
using namespace std;
// 抽象クラス
class INode {
public:
  // 抽象メソッド
  virtual void print() = 0;
};
// Node1クラス、INodeクラスを継承する。
class Node1 : public INode {
public:
  // 出力関数
  virtual void print() {
    // コンソール出力
    cout << "Node1 Class " << endl;
  }
};
// Node2クラス、INodeクラスを継承する。
class Node2 : public INode {
public:
  // 出力関数
  virtual void print() {
    // コンソール出力
    cout << "Node2 Class " << endl;
  }
};
// ファクトリーメソッドパターンの関数
INode* factory(int type) {
  // パラメータのtypeの値が0の場合
  if (type == 0) {
    // Node1インスタンスを生成
    return new Node1();
  }
  // パラメータのtypeの値が1の場合
  else if (type == 1) {
    // Node2インスタンスを生成
    return new Node2();
  }
  // nullリターン
  return nullptr;
}
// 実行関数
int main() {
  // ファクトリーメソッドのパラメータで0を入れたら
  INode* node = factory(0);
  // Node1クラスのprint関数が実行
  node->print();
  // メモリ解除
  delete node;
  // ファクトリーメソッドのパラメータで1を入れたら
  node = factory(1);
  // Node2クラスのprint関数が実行
  node->print();
  // メモリ解除
  delete node;
  return 0;
}


上の例をみればfactory関数のパラメータの値によりNode1クラスのインスタンスを生成するかNode2クラスのインスタンスを生成することが決めます。


ビルダーパターンと比べたらすごく簡単な構造です。

// 実行関数があるクラス
public class Program {
  // ファクトリーメソッドパターンの関数
  public static INode factory(String type) {
    // パラメータのtypeが大小文字に関係せず、NODE1の場合
    if ("NODE1".equalsIgnoreCase(type)) {
      // Node1インスタンスを生成
      return new Node1();
    // パラメータのtypeが大小文字に関係せず、NODE2の場合
    } else if ("NODE2".equalsIgnoreCase(type)) {
      // Node2インスタンスを生成
      return new Node2();
    }
    // 上の条件に合うことがなければnullをリターン
    return null;
  }
  // 実行関数
  public static void main(String... args) {
    // ファクトリーメソッドにnode1の値を入れたら
    INode node = factory("node1");
    // Node1インスタンスが生成され、コンソール出力
    node.print();
    // ファクトリーメソッドにnode2の値を入れたら
    node = factory("node2");
    // Node2インスタンスが生成され、コンソール出力
    node.print();
  }
}
// インターフェース
interface INode {
  // コンソール出力の抽象メソッド
  void print();
}
// Node1クラス、INodeインスタンスを継承する。
class Node1 implements INode {
  // 再定義
  @Override
  public void print() {
    // コンソール出力
    System.out.println("Node 1 class");
  }
}
// Node2クラス、INodeインスタンスを継承する。
class Node2 implements INode {
  // 再定義
  @Override
  public void print() {
    // コンソール出力
    System.out.println("Node 2 class");
  }
}


Javaの例ではパラメータの値をStringで受けます。つまり、Stringデータによりインスタンスを生成することができます。

こんなことになると実際のプロジェクトにはデータベースやユーザから受ける値により生成するインスタンスを変わって実行するロジックを選択することができます。


そしてファクトリーメソッドにはリターンするタイプを一つ種類のタイプに統一しなければならないのでinterfaceを使いました。

using System;

namespace Example
{
  // インターフェース
  interface INode
  {
    // コンソール出力の抽象メソッド
    void Print();
  }
  // Node1クラス、INodeインスタンスを継承する。
  class Node1 : INode
  {
    // 再定義関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Node1 Class");
    }
  }
  // Node2クラス、INodeインスタンスを継承する。
  class Node2 : INode
  {
    // 再定義関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Node2 Class");
    }
  }
  // 列挙型
  enum NodeType
  {
    Node1,
    Node2
  }
  // 実行関数があるクラス
  class Program
  {
    // ファクトリーメソッドパターンの関数
    static INode Factory(NodeType type)
    {
      // typeの値がNodeType.Node1なら
      if (type == NodeType.Node1)
      {
        // Node1クラスのインスタンスを生成
        return new Node1();
      }
      // typeの値がNodeType.Node2なら
      else if (type == NodeType.Node2)
      {
        // Node2クラスのインスタンスを生成
        return new Node2();
      }
      // 上の条件に合うことがなければnullをリターン
      return null;
    }
    // 実行関数
    static void Main(string[] args)
    {
      // ファクトリーメソッドにNodeType.Node1の値を入れたら
      INode node = Factory(NodeType.Node1);
      // Node1インスタンスが生成され、コンソール出力
      node.Print();
      // ファクトリーメソッドにNodeType.Node2の値を入れたら
      node = Factory(NodeType.Node2);
      // Node1インスタンスが生成され、コンソール出力
      node.Print();

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}


上の例はStringタイプではなく、列挙型の値によりインスタンスを生成しました。

Stringタイプを使うことはもし、String値にタイプミスがある場合、デバッグ段階でエラーをチェックしないので、バグが発生する可能性があります。でも、列挙型でパラメータを設定すればバグが発生する可能性は少しなくすことができます。


ファクトリーメソッドパターンは普通はシングルトンパターンと組み合いしてよく使います。なので、EntityタイプのデータクラスよりControllerみたいに処理クラスによく使います。

using System;

namespace Example
{
  // インターフェース
  interface INode
  {
    // コンソール出力の抽象メソッド
    void Print();
  }
  // Node1クラス、INodeインスタンスを継承する。
  class Node1 : INode
  {
    // シングルトンパターンのための変数
    private static Node1 singleton = null;
    // コンストラクタはprivate
    private Node1() { }
    // インスタンスを取得関数
    public static Node1 GetInstance()
    {
      // インスタンスがない場合
      if(singleton == null)
      {
        // インスタンス生成
        singleton = new Node1();
      }
      // インスタンスリターン
      return singleton;
    }
    // 再定義関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Node1 Class");
    }
  }
  // Node2クラス、INodeインスタンスを継承する。
  class Node2 : INode
  {
    // シングルトンパターンのための変数
    private static Node2 singleton = null;
    // コンストラクタはprivate
    private Node2() { }
    // インスタンスを取得関数
    public static Node2 GetInstance()
    {
      // インスタンスがない場合
      if (singleton == null)
      {
        // インスタンス生成
        singleton = new Node2();
      }
      // インスタンスリターン
      return singleton;
    }
    // 再定義関数
    public void Print()
    {
      // コンソール出力
      Console.WriteLine("Node2 Class");
    }
  }
  // 列挙型
  enum NodeType
  {
    Node1,
    Node2
  }
  // 実行関数があるクラス
  class Program
  {
    // ファクトリーメソッドパターンの関数
    static INode Factory(NodeType type)
    {
      // typeの値がNodeType.Node1なら
      if (type == NodeType.Node1)
      {
        // Node1クラスのインスタンスを生成
        return new Node1();
      }
      // typeの値がNodeType.Node2なら
      else if (type == NodeType.Node2)
      {
        // Node2クラスのインスタンスを生成
        return new Node2();
      }
      // 上の条件に合うことがなければnullをリターン
      return null;
    }
    // 実行関数
    static void Main(string[] args)
    {
      // ファクトリーメソッドにNodeType.Node1の値を入れたら
      INode node = Factory(NodeType.Node1);
      // Node1インスタンスが生成され、コンソール出力
      node.Print();
      // ファクトリーメソッドにNodeType.Node2の値を入れたら
      node = Factory(NodeType.Node2);
      // Node1インスタンスが生成され、コンソール出力
      node.Print();

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}


上の例はNode1クラスとNode2クラスにあるコンストラクタをprivateに設定してGetInstance関数でインスタンスを取得する形のシングルトンパターンを作成しました。

ファクトリーメソッドにはパラメータにより各クラスのGetInstance関数を呼び出し、インスタンスをリターンします。


この構造を何処でよく見えるかと思えばウェブのMVCの形のControllerクラスでウェブのURL要請により呼び出すクラスインスタンスが変わることと同じ構造だと思います。(Routeクラス)


ここまでデザインパターンのファクトリーメソッドパターン(Factory method pattern)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

Factory method pattern
最新投稿