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

Abstract Factoryとは

以前のブログにおいて、Factory Methodパターンについて解説しました。
Factoryパターンはさらに2つに分かれ、中でもFactory Methodの特徴は、クラスのインスタンス化を行う部分を同じFactoryクラスに集約することで、クライアント(そのクラスのインスタンスを必要としている部分)がクラスに直接依存せずに済むという点でした。
今回は、そのもう一つのパターンの、Abstract Factoryパターンについて解説します。
Abstract Factoryパターンの特徴は、Factoryの抽象クラス(ここがabstract)を作り、クラスのインスタンスを生成するという振る舞いを登録しておくことで、どのFactoryクラスでも同じようにクラスのインスタンスを生成できることです。
特に、場面によって異なる組み合わせのクラスインスタンスが欲しい場合において、Factoryを複数作っておいてそれぞれで生成するクラスを別にしておくことで、それに対応することができます。
とにかく実装例を見てみましょう。

実装例ですよ!

いつものように.Net Coreのコンソールアプリを作成したら、まずはFactoryで作成されるProductの振る舞いを規定します。
Product1は車か何かのように「走る」ことができ、Product2は対話ロボットのように「話す」ことができます。

interface IProduct1
{
    public void Run();
}
interface IProduct2
{
    public void Speak();
}

次に、Product群を作成していきます。
IProduct1とIProduct2を継承したクラスがTypeA,Bに1つずつあり、同じIProduct1でもTypeAとTypeBで別の文字列を出力するようにしています。

class Product1TypeA : IProduct1
{
    public void Run()
    {
        Console.WriteLine("TypeAが走ります");
    }
}
class Product2TypeA : IProduct2
{
    public void Speak()
    {
        Console.WriteLine("TypeAが発話します");
    }
}
class Product1TypeB : IProduct1
{
    public void Run()
    {
        Console.WriteLine("TypeBが走ります");
    }
}
class Product2TypeB : IProduct2
{
    public void Speak()
    {
        Console.WriteLine("TypeBが発話します");
    }
}

次に、Factoryの抽象部分を作成していきます。
それぞれ、IProduct1と2を継承したクラスを作成して返す振る舞いを規定しておきます。

abstract class AbstractFactory
{
    public abstract IProduct1 CreateProduct1();
    public abstract IProduct2 CreateProduct2();
}

次に、Factoryクラスを実装していきます。
FactoryTypeAではTypeAと名前の付いたProductを生成し、FactoryTypeBではTypeBと名前の付いたProductを生成します。
こうすることによって、FactoryTypeAを使ってインスタンスを生成している限り間違ってTypeBのProductを生成してしまうことがなくなるというのがAbstractMethodの肝になります。

class FactoryTypeA : AbstractFactory
{
    public override IProduct1 CreateProduct1()
    {
        return new Product1TypeA();
    }
    public override IProduct2 CreateProduct2()
    {
        return new Product2TypeA();
    }
}
class FactoryTypeB : AbstractFactory
{
    public override IProduct1 CreateProduct1()
    {
        return new Product1TypeB();
    }
    public override IProduct2 CreateProduct2()
    {
        return new Product2TypeB();
    }
}

最後に、クライアント側のコードを作成していきます。
Factoryクラスを生成してからProductクラスを生成し、それぞれの機能を実行させます。

class Program
{
    static void Main(string[] args)
    {
        var factoryA = new FactoryTypeA();
        var product1a = factoryA.CreateProduct1();
        var product2a = factoryA.CreateProduct2();
        product1a.Run();
        product2a.Speak();
        var factoryB = new FactoryTypeB();
        var product1b = factoryB.CreateProduct1();
        var product2b = factoryB.CreateProduct2();
        product1b.Run();
        product2b.Speak();
    }
}

ここまでうまくコードが作成できていれば、結果として以下のような出力がなされるはずです。
Productによって出力する文字列が違うようにしていたのですが、factoryAから生成されたProductはちゃんとTypeAのものとして作られ、factoryBから生成されたProductもちゃんとTypeBの者が生成されていることが分かります。

TypeAが走ります
TypeAが発話します
TypeBが走ります
TypeBが発話します

さいごに

今回はFactoryパターンの概念のもう一つのパターン、Abstract Factoryパターンについて解説しました。
Factory Methodパターンが主にインスタンスの生成個所を絞ることが主な目的だったのに対し、Abstract MethodパターンではFactory毎に作成するインスタンスを規定するということが主な目的になっています。
そのために、Factoryクラスについてもさらに抽象クラスを作成し、返すProductの継承するinterfaceだけを規定した抽象メソッドを規定しています。
このような意味で、Abstract FactoryパターンはFactory Methodパターンを内包しているとも言えます。