おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

C#でFactory Methodパターンを体験してみた。

はじめに

GoFデザインパターンの1つであるFactory Methodパターンを体験してみました。

Factory Methodパターンとは

Factory Methodパターンについての説明は以下の記事がとても分かりやすかったです。

nprogram.hatenablog.com

FactoryMethodの説明と構成要素は上記記事より引用させていただいています。

インスタンス化したいオブジェクト(製品)を実行時の条件によって決めたい場合に利用します。 Factory Methodパターンはオブジェクト(製品)を生成する側と利用する側に分けて定義する必要があります。 分けておくことで、将来システムに起こりえる変更をあらかじめ分離でき保守性を保つことができます。

構成要素は以下です。

Product : 生成されるオブジェクト(製品)のAPIを定義する抽象クラス。製品の具象クラスが抽象クラスで使用できるようにする
ConcreteProduct : 生成される具象製品クラス
Creator : ファクトリメソッドを除く製品を操作するAPI抽象クラスを定義する抽象クラス
ConcreteCreator : ファクトリメソッドを実装し、実際に製品を作成するクラス

サンプルコード

以下にサンプルコードを記載します。 構成要素は以下になります。

Product: Productクラス

ConcreteProduct: SQLServerクラス, PostgreSQLクラス

Creator: Creatorクラス

ConcreteCreator: DBCreatorクラス

public class Program
{
    static void Main(string[] args)
    {
        Creator DBCreator = new DBCreator("SQLServer");
        var sqlServer = DBCreator.Create();
        sqlServer.Connect();
    }
}

public abstract class Product
{
    public abstract void Connect();
}

//ConcreteProduct
public class SQLServer : Product
{
    public override void Connect()
    {
        Console.WriteLine("SQLServerに繋ぎます~!");
    }
}

//ConcreteProduct
public class PostgreSQL : Product
{
    public override void Connect()
    {
        Console.WriteLine("PostgreSQLに繋ぎます~!");
    }
}

public abstract class Creator
{
    public abstract Product Create();
}

//ConcreteCreator
public class DBCreator : Creator
{
    private readonly string _name;
    public DBCreator(string name) => _name = name;

    public override Product Create()
    {
        switch (_name)
        {
            case "SQLServer":
                return new SQLServer();
            case "PostgreSQL":
                return new PostgreSQL();
            default:
                throw new Exception();
        }
    }
}
SQLServerに繋ぎます~!

おわりに

最初に引用させていただいた説明にて、「インスタンス化したいオブジェクト(製品)を実行時の条件によって決めたい場合に利用します。」とありましたが、確かに上記のサンプルコードだとDBCreatorのコンストラクタ引数によってインスタンス化したいクラスが簡単に切り替えることが可能ですね。

インスタンス化の切り替えについてはMainメソッド内で条件分岐したら同じことができるんじゃない?とも思いましたが、こちらの方が機能を分離できていますし、あちらこちらでnewしてConcreteProductを生成する必要性もなさそうなので良さそうです。 あちらこちらでnewして作っていた場合、仮に元のクラスでコンストラクタ引数を渡すような変更が起きた時にnewしている全部の箇所に引数を追加しないといけませんが、Factory Methodパターンを使っていたらConcreteCreator(上記のサンプルならDBCreatorクラス)だけ直せば良さそうというところも気に入りました。