web-dev-qa-db-ja.com

抽象的な工場パターン

  1. C#の抽象ファクトリパターンの良い例ですか?
  2. C#の抽象ファクトリパターンの利点は何ですか?
  3. Abstract factoryパターンでC#ジェネリックを使用する方法は?
  4. 抽象ファクトリーパターンを使用した単体テストの方法
47

まず、Abstract Factoryパターンについて読むことをお勧めします。たとえば、 here です。次に、このパターンを使用する理由を説明します。

通常、Factoryパターンを使用する場合、Factoryでオブジェクトを作成します。問題は、特定のクラスの複数の実装がある場合に発生します。現在、これらの複数の実装はグループ化されています。ファクトリがある場合はAbstract Factory patternを使用しますが、グループごとにオブジェクトの作成をグループ化します。

さて、上記の説明は完全に明確ではないかもしれませんので、例を挙げます。

データエージェントを備えたクラスライブラリがあるとします。データエージェントは、さまざまなデータにアクセスして保存する方法を提供します。もちろん、データを保存する方法は複数あります。例:データベース、XMLファイル、サービス経由。これらの可能な方法のそれぞれについて、データエージェントが必要です。問題は、誰かがXMLファイル用のDataAgentAをデータベース用のDataAgentBとともに使用することを望まないことです(エンティティAとBがあると仮定しましょう)。ユーザーは1つのストレージエンジンのみを使用する必要があります。

Abstract Factoryパターンを紹介します。

ユーザーがData Agentを直接インスタンス化できないようにしますが、これらのData Agentを工場から取り出す必要があります。 (追加の利点は、たとえばデータベース(EF)を使用する場合、内部接続を実行して、Data Agentが同じコンテキストを使用することなどを確認できることです。)これをどのように実現しますか?データエージェントのコンストラクターを「内部」に設定します。それとは別に、ストレージエンジンごとに異なるファクトリを作成します。さて、これらの工場はすべて同じことをしているので、これらのインターフェースもあります(データエージェントと同じように、それらはすべて同じことをしなければならないからです!?)。

以下にインターフェースがあります。基本的にこれはファクトリーパターンですが、今だけではclassesの代わりに、interfacesについて話します。

public interface IAgentA 
{
    // Add some methods here!
}

public interface IAgentB
{
    // Add some methods here!
}

public interface IAgentFactory
{
    IAgentA CreateAgentA();
    IAgentB CreateAgentB();
}

これで、2つのエージェントに対して、2つの可能な実装があります。1つはXML用で、もう1つはデータベースストレージ用です(これも一例です。必要な数の実装タイプを使用できます)。これらの実装は次のようになります(以下を参照)。コンストラクタをinternalにしたことに注意してください!これは、このコードブロックの後に来る部分に必要です。

public class AgentA_Xml : IAgentA
{
    internal AgentA_Xml()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Xml : IAgentB
{
    internal AgentB_Xml()
    { /* Construction here */}

    // IAgentB method implementations
}


public class AgentA_Database : IAgentA
{
    internal AgentA_Database()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Database : IAgentB
{
    internal AgentB_Database()
    { /* Construction here */}

    // IAgentB method implementations
}

コンストラクタは内部にあるため。これにより、アセンブリの外部でこれらのクラスをインスタンス化できなくなります。これは通常、このような場合に行うことです。次に、工場を作成する必要があります。

public class XMLAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Xml();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Xml();
    }
}


public class DatabaseAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Database();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Database();
    }
}

両方の工場がIAgentFactoryインターフェースを実装しているため、ユーザーは他のコードを変更せずにAgentFactory実装を簡単に変更できます(この場合、別のストレージエンジンを使用したい場合)。 (明らかに)インターフェイスに対してプログラムした限り、(エージェントに対して)書きました。

上記の説明があなたの質問(1)と(2)に答えることを願っています。

  1. C#の抽象ファクトリパターンの良い例ですか?
  2. c#の抽象ファクトリパターンの利点は何ですか?

質問に答える(3)。

  1. Abstract factoryパターンでC#ジェネリックを使用する方法

ジェネリックを引き続き使用できますが、Abstract Factoryパターンを使用してもこれは少しも変わりません。もちろん、ジェネリックファクトリメソッド(createメソッド)を作成する必要がありますが、それは問題になりません。

質問に答えます(4)。

  1. 抽象ファクトリーパターンを使用してユニットテストを行う方法

他のクラスを単体テストするのと同じです。 1つだけ異なることがあります。

クラスのコンストラクタ(および他の内部メソッド)もテストする必要があるため、ユニットテストプロジェクトで内部コンストラクタ(メソッド)を表示する必要があります(internalpublic)。これは、プロジェクト(ファクトリーとクラスがあるプロジェクト)のAssemblyInfo.csファイルに次の行を追加することで簡単に実行できます。

[Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("My.UnitTest.Namespace")]

[〜#〜] msdn [〜#〜] のInternalsVisibleTo属性に関する詳細情報(および注釈)を見つけることができます。 。

この種の質問があなたの質問に答えることを願っています。

140
Styxxy