C#でデザインパターン~Factoryパターン編~
2019-11-06
azblob://2022/11/11/eyecatch/2019-11-06-csharp-gof-factorymethodpattern-000.jpg

Factoryパターンとは

今回も、C#での実際に動くシンプルな例を用いながら、デザインパターンについて解説していきたいと思います。

今回ご紹介するのはFactoryパターンなのですが、このFactoryパターンはさらにFactory MethodパターンとAbstract Factoryパターンに分かれます。

まるで関西風か関東風かというような分かれ方をしましたが、まずはそもそもFactoryパターンとは何かということから解説していきます。

Factoryパターンとは、何らかのクラスのインスタンスが必要となる際に、そのクラスをインスタンス化する部分を集約するために利用されるデザインパターンです。

例えば、クラスのインスタンス化をそれぞれ必要となる箇所で行っていると、そのインスタンス化の対象となるクラスに変更が起きた場合、それによる影響はそれぞれをインスタンス化を行っている箇所に波及します。

その影響をなるべく小さい箇所に抑えられることがFactoryパターンの利点です。

その中で、 Abstract Factoryパターンは特定の条件の下で複数のクラスをインスタンス化する必要がある場合に役立ちます。

例えば、ある状況ではクラスA,B,Cが欲しいが別の状況ではD,E,Fが欲しいというような場合、Factoryを二つ用意して片方ではA、B、C、もう片方ではD、E、Fを作成させるようにしておくことにより、それぞれで使うものを実際に欲しいクラスではなく、ファクトリーで区別することができます。

一方、Factory Methodパターンはもう少しシンプルです。

作成するインスタンスは一つの流れにつき一つで、最初に解説したような「クラスのインスタンス化を行う部分を集約する」という意味合いが強くなっています。

実際にはFactoryクラスをstaticにすることでFactory自体をインスタンス化することなく使用する例も多く見受けられます。

今回は、このFactory Methodパターンを例に見ていきます。

Factory Methodパターンを実装

いつものようにコンソールアプリケーションとしてプロジェクトを立ち上げたら、Factoryで作成されるオブジェクトの共通の振る舞いを規定していきます。

interface IProductInstance
{
    public string Description { get; set; }
}

次に、その振舞いを継承して実際に生成されるクラスを作成していきます。

中身は、単純にインスタンス化時にそのクラスの説明文を受け付けて代入しておき、求められたらそれを応える程度のものです。

class ProductInstanceA : IProductInstance
{
    public string Description { get; set; }
    public ProductInstanceA(string description)
    {
        Description = description;
    }
}

同じようなクラスとしてもう一つクラスを作成します。

キャラ付けのため、こちらは語尾をですます調にしてもらいます。

class ProductInstanceB : IProductInstance
{
    public string Description { get; set; }
    public ProductInstanceB(string description)
    {
        Description = description + "ですよー!";
    }
}

次に、このパターンの本体であるFactoryクラスを作成していきます。

メソッドを呼び分けることでほしいクラスのインスタンスを入手することができます。

staticクラスとして作成することで、Factoryをインスタンス化せずに機能を利用することができます。

static class Factory
{
    static public IProductInstance CreateProductA(string description)
    {
        return new ProductInstanceA(description);
    }
    static public IProductInstance CreateProductB(string description)
    {
        return new ProductInstanceB(description);
    }
}

最後に、このパターンを利用してクラスのインスタンスを作成してみます。

ここでMainのコードの中にnewで始まるコードがないことが肝です。

Factoryクラスのメソッドを利用することによって、クラスのインスタンス化をこの場で行う必要がなくなり、依存関係をFactoryクラスに集約することができました。

static void Main(string[] args)
{
    var productInstanceA = Factory.CreateProductA("productの説明");
    var productInstanceB = Factory.CreateProductB("productの説明");
    Console.WriteLine(productInstanceA.Description);
    Console.WriteLine(productInstanceB.Description);
}

ここまでのコーディングが上手くいっていればコードを走らせた結果以下のような文章が出力されるはずです。

productの説明
productの説明ですよー!

おわりに

今回はFactoryパターンについて概説し、その中のFactory Methodパターンを実装しました。

クラスのインスタンス化を行う部分を集約することで、それに伴う手順を集約化したり、クラスの仕様の変更に対する影響範囲を絞ったりすることができます。

似たようなインスタンス化の手順が繰り返され始めたり同じインスタンスを作成するコードをコピー&ペーストで作成し始めようとしたときに、このパターンを使うことを検討してみてはどうでしょうか。