設計モードノート(5)---工場方法モード(作成型)


Gof定義


オブジェクトを作成するための言い訳を定義し、サブクラスにどのクラスをインスタンス化するかを決定させ、Factory Methodはクラスのインスタンス化をサブクラスに遅延させる.

動機


ソフトウェアシステムでは、常に「オブジェクト」の作成作業に直面しています.需要の変化によって、このオブジェクトは常に激しい変化に直面しているが、比較的安定した口実を持っている.
このようなシーンがあると仮定すると、自動車クラスと自動車テストフレームクラスがあり、自動車テストフレームワークは自動車をテストする責任を負い、通常は以下のように書きたいと思います.
public class Car
{
    public void Startup() { }

    public void Run(){ }

    public void Turn(Direction direction) { }

    public void Stop() { }
}

フレームクラスのテスト
class CarTestFramework
{
    public void BuildTestContext()
    {
        Car car = new Car();
        //do something
    }

    public void DoTest()
    {
        Car car = new Car();
        //do something
    }

    public void GetTestData()
    {
        Car car = new Car();
        //do something
    }
}

上記のテストフレームワーククラスCarTestFrameworkの各メソッドは、Carクラスをインスタンス化する可能性があります.上のコードでは直接インスタンス化されたCarクラスです.このようなテストフレーム類と自動車の間には強い依存関係がある.実際、私たちのテストフレームクラスは1つのタイプの自動車だけをテストすることはできません.だから、テストされた車のタイプが変化したとき、テストフレームクラスも変化しなければなりません.これはもちろん私たちが望んでいるものではありません.今すぐ自動車類を抽象化します.
public abstract class AbstractCar
{
    public abstract void Startup();

    public abstract void Run();

    public abstract void Turn(Direction direction);

    public abstract void Stop();
}

抽象的な自動車クラスが作成されると、テストフレームワーククラスも相応の変化が発生し、AbstraactCartをすぐにCarに交換する可能性があります.以下のようにします.
class CarTestFramework
{
    public void BuildTestContext()
    {
        AbstractCar car = new AbstractCar();
        //do something
    }

    //....
}

上のコードは実際には間違っており,抽象クラスはインスタンス化できないことが容易に分かる.このとき,抽象クラスを用いてサブクラスをインスタンス化する別の方法も考えられる.
class CarTestFramework
{
    public void BuildTestContext()
    {
        AbstractCar car=new Car();
        //do something
    }
    //......
}

しかし、このようにするのは問題があり、インスタンス化する際に具体的なクラスCarを使用し、Carに依存している.オブジェクトを作成するには、ファクトリクラスが必要です.次に、工場クラスCarFactoryを作成します.
public class CarFactory
{
    public AbstractCar CreateCar()
    {
        return new Car();
    }
}

テストフレームワーククラスのコードは次のとおりです.
class CarTestFramework
{
    public void BuildTestContext(CarFactory carFactory)
    {
        AbstractCar car1 = carFactory.CreateCar();
        AbstractCar car2 = carFactory.CreateCar();
        AbstractCar car3 = carFactory.CreateCar();
        //... 
    }
    //........
}

クライアント・プログラムで次のように呼び出されます.
public class App
{
    public void Main()
    {
        CarTestFramework carTestFramework = new CarTestFramework();
        carTestFramework.BuildTestContext(new CarFactory());
        //....
    }
}

上記のコードから,テストフレームワーククラスに伝達されるメソッドのパラメータはファクトリクラスのオブジェクトであり,ファクトリクラスのメソッドCreateCarでは直接返されるCarクラスであり,このような結合関係はファクトリクラスのCreateCarに移動し,強い依存性を生じることが分かる.HongqiCar<テストが必要な場合は、次のようにCreateCarメソッドを変更する必要があります.
public class HongqiCar:AbstractCar
{
    public override void Startup() { }

    public override void Run() { }

    public override void Turn(Direction direction) { }

    public override void Stop() { }
}

変更後のファクトリクラス
public class CarFactory
{
    public AbstractCar CreateCar()
    {
        return new HongqiCar();
    }
}

このようなデザインも明らかによくない.強い依存が工場クラスで発生する以上,工場クラスも抽象化できる.
public abstract class AbstractCarFactory
{
    public abstract AbstractCar CreateCar();
}

HongqiCarがテストされる必要があるといえば、抽象的なファクトリクラスを継承するHongqiCarを生成するファクトリクラスを作成します.
public class HongqiCarFactory : AbstractCarFactory
{
    public override AbstractCar CreateCar()
    {
        return new HongqiCar();
    }
}

お客様のプログラムをこのように変更できます.
public class App
{
    public void Main()
    {
        CarTestFramework carTestFramework = new CarTestFramework();
        carTestFramework.BuildTestContext(new HongqiCarFactory());
        //....
    }
}

AutiCarを追加してテストするなど、新しいニーズがある場合は、次の手順に従います.
1 AutiCarクラス継承AbstractCarクラスの追加
public class AudiCar : AbstractCar
{
    public override void Startup() { }

    public override void Run() { }

    public override void Turn(Direction direction) { }

    public override void Stop() { }
}

2 AudiCarのファクトリクラスを追加AbstractCarFactoryクラスを継承
public class AudiCarFactory : AbstractCarFactory
{
    public override AbstractCar CreateCar()
    {
        return new AudiCar();
    }
}

3お客様のプログラムは少し変更すればよい
public class App
{
    public void Main()
    {
        CarTestFramework carTestFramework = new CarTestFramework();
        carTestFramework.BuildTestContext(new HongqiCarFactory());
        // 
        carTestFramework.BuildTestContext(new AudiCarFactory());
        //....
    }
}

完全なコード


AbstractCar.cs
/// <summary>
///  
/// </summary>
public abstract class AbstractCar
{
    public abstract void Startup();
    public abstract void Run();
    public abstract void Turn(Direction direction);
    public abstract void Stop();
}

AbstractCarFactory.cs
/// <summary>
///  
/// </summary>
public abstract class AbstractCarFactory
{
    public abstract AbstractCar CreateCar();
}

CarTestFramework.cs
/// <summary>
///  
/// </summary>
public class CarTestFramework
{
    public void BuildTestContext(AbstractCarFactory abstractCarFactory)
    {
        AbstractCar car = abstractCarFactory.CreateCar();
    }

    public void DoTest(AbstractCarFactory abstractCarFactory)
    {
        //do something
    }

    public void GetTestData(AbstractCarFactory abstractCarFactory)
    {
        //do something
    }
}

App.cs
/// <summary>
///  
/// </summary>
public class App
{
    public void Main()
    {
        CarTestFramework carTestFramework = new CarTestFramework();
        carTestFramework.BuildTestContext(new HongqiCarFactory());
        //....
    }
}

上の大きいのは基本的なコードで、もしどんなタイプの車がテストされる必要があるならば、2つの具体的なクラスを追加して抽象的な自動車クラスと抽象的な工場クラスを継承するだけで、それから取引先のプログラムで少し修正することができて、しかも取引先のプログラムは反射してプロファイルの方式をプラスすることを通じていかなる修正をする必要はありません.これでOCPの原則をよく満たし,需要変動は新しいクラスを拡張すればよい.

Factory Method設計モードのいくつかのポイント

  • Factory Methodモードは、主にクラスオブジェクトの使用者と特定のタイプとの結合関係を分離するために使用される.常に変化する特定のタイプに直面して、緊密な結合関係はソフトウェアの脆弱性をもたらします.
  • Factory Methodモードでは、作成する特定のオブジェクトの動作をサブクラスに遅延させることで、変更ではなく拡張(変更)ポリシーを実現し、このような緊密な結合関係を解決します.
  • Factory Methodモードは「単一オブジェクト」の変化を解決し、Abstract Factoryモードは「シリーズオブジェクト」の需要変化を解決し、Builderモードは「オブジェクト部分」の需要変化を解決した.