なぜ抽象ファクトリデザインパターンが必要なのですか?


124

定義のほとんどは言う:

抽象ファクトリーは、具体的なクラスを指定せずに関連オブジェクトのファミリーを作成するためのインターフェースを提供します

具象クラス自体のオブジェクトを作成することでタスクを達成できるため、抽象ファクトリパターンの使用方法は何ですか。Concreteクラスのオブジェクトを作成するファクトリメソッドがあるのはなぜですか?

abstractFactoryパターンを実装する必要がある実際の例を教えてください。

回答:


219

抽象ファクトリは、依存性注入(DI)の非常に中心的な設計パターンです。以下は、抽象ファクトリの適用がソリューションとして受け入れられたスタックオーバーフローの質問のリストです。

私の理解の及ぶ限り、これらの質問は人々が抱えていた実際の懸念や問題を表しています。そのため、実際の例から始めることができます。


6
これらの例はすべて、単一の製品インターフェースを返すため、ファクトリーメソッドパターンについて説明しています。これらはどれも、関連する製品インターフェースのファミリーを生成しないため、抽象ファクトリパターンではありません。
jaco0646

32
完全な開示のために、この回答の作成者は、リンクされた回答すべての作成者でもあることを明確にすべきでした。したがって、このリストはSOコミュニティの代表的なサンプルではありません
jaco0646 2016

1
@ jaco0646 IIRC、ファクトリメソッドパターンは、継承に依存するテンプレートメソッドパターンの特殊化です。しかし、私は現在旅行中で、GoFブックを持っていないので、誤解されているかもしれません。「それらのどれも関連製品インターフェースのファミリを生成しない」とはどういう意味ですか?
Mark Seemann

1
ファクトリが抽象ファクトリパターンに準拠していないことを示す最も簡単な手がかりは、ファクトリが生成する抽象製品を数えることです(「抽象」という言葉の使いすぎを避けるために、「抽象製品」の代わりに「製品インターフェイス」という用語を使用しました)。 。単一の抽象プロダクトを生成するファクトリは、抽象ファクトリになることはできません。これは、抽象ファクトリが定義により関連プロダクトのファミリーを生成するためです。このファミリが1つのインターフェースの異なる実装を指すのではなく、関連する異なるインターフェースを備えた製品を指すことに注意することが重要です。
jaco0646 2016

1
以下はoodesign.com/abstract-factory-pattern.htmlの例です。これが、抽象ファクトリパターンが最初に作成された目的です。
user2802557 2016

23

Abstract Factoryパターンの実際の使用例は、2つの異なるデータソースへのデータアクセスを提供することです。アプリケーションがさまざまなデータストアをサポートしているとします。(例:SQLデータベースとXMLファイル)。2つの異なるデータアクセスインターフェイスがあります。たとえば、IReadableStoreおよびIWritableStoreは、使用するデータソースのタイプに関係なく、アプリケーションが期待する一般的なメソッドを定義します。

使用するデータソースのタイプによって、クライアントコードがデータアクセスクラスを取得する方法が変わることはありません。あなたは、AbstractDataAccessFactoryデータソースの種類が設定されているかを知っているし、提供し、具体的な工場をクライアントコードのために、すなわちSqlDataAccessFactoryXmlDataAccessFactory。これらの具体的な工場は、例えば、具体的な実装を作成することができますSqlReadableStoreし、SqlWriteableStore

.NET Framework のDbProviderFactoryは、このパターンの例です。


18
この回答は、ファクトリーメソッドパターンまたは静的ファクトリーパターンを正確に表すことができますが、抽象ファクトリーパターンを正確に表すことはできません。
jaco0646 2016

5

私があなたを正しく理解しているなら-問題は、なぜFactoryメソッドと抽象的なファクトリパターンの両方があるのか​​ということです。異なるポリモーフィッククラスのインスタンス化手順が異なる場合は、抽象ファクトリが必要です。また、オブジェクトの初期化の詳細を知らなくても、いくつかのモジュールでインスタンスを作成して使用したいとします。たとえば、いくつかの計算を行うJavaオブジェクトを作成するとします。ただし、一部はアプリケーションの一部ですが、他のバイトコードはDBから読み取る必要があります。一方、なぜファクトリメソッドが必要なのですか?同意します、その抽象的なファクトリはそれに重なります。しかし、場合によっては、コードの記述がはるかに少なく、クラスやインターフェースが少ないため、システムを理解しやすくなります。


3

具象クラス自体のオブジェクトを作成することでタスクを達成できるため、抽象ファクトリパターンの使用方法は何ですか。Concreteクラスのオブジェクトを作成するファクトリメソッドがあるのはなぜですか?

Abstract Factoryがない場合、クライアントは具象クラスの詳細を知る必要があります。この密結合はAbstract Factoryで削除されました。

これで、ファクトリメソッドは、クライアントが使用する必要があるコントラクトを公開します。Factoryメソッドによって公開されるインターフェースを実装する新製品を追加することにより、より多くの製品を工場に追加できます。

理解を深めるには、関連するSEの質問を参照してください。

ファクトリパターンと抽象ファクトリパターンの基本的な違いは何ですか?

意図:

具体的クラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリを作成するためのインターフェースを提供します。

これから、抽象ファクトリパターンの意図、構造、チェックリスト、および経験則を理解できます。ソース作成記事ます。

チェックリスト:

  1. 決定する プラットフォームの独立性と作成サービスが現在の痛みの原因であるます
  2. プラットフォームのマトリックスを計画する製品の
  3. を定義します 製品ごとのファクトリメソッドで構成されるファクトリインタフェースます
  4. を定義します 新しいオペレーターへのすべての参照をカプセル化する各プラットフォームのファクトリー派生クラスをます。
  5. クライアントは、新しいへのすべての参照を引退し、使用する必要がありますファクトリメソッドを作成するために、製品のオブジェクトを。

2

抽象ファクトリは、コードベースを統一したまま、複数のプラットフォームをサポートするのに最適です。Windows、Linux、OSXで実行したい大きなQtまたはGTK +または.NET / Monoプログラムがあるとします。しかし、各プラットフォームで異なる方法で実装されている機能があります(おそらく、kernel32 APIまたはPOSIX機能を介して)。

public abstract class Feature
{
    public abstract int PlatformSpecificValue { get; }

    public static Feature PlatformFeature
    {
        get
        {
            string platform;
            // do platform detection here
            if (platform == "Win32")
                return new Win32Feature();
            if (platform == "POSIX")
                return new POSIXFeature();
        }
    }

    // platform overrides omitted
}

この抽象ファクトリーを使用すると、UIは現在のプラットフォームについて何も知る必要がありません。

Feature feature = Feature.PlatformFeature;
Console.WriteLine(feature.PlatformSpecificValue);

1
わかりません。クライアントが実装の詳細を知らないように、抽象メソッドを返すためにファクトリメソッドを使用するのとどう違うのですか?
kiwicomb123 2018年

1

設計パターンを見ると、それらのほとんどすべてを冗長にすることができます。しかし、どのようなパターンが、同様のタイプの問題を解決するために一般的に使用されるアプローチを意味します。設計パターンは、一連の類似したタイプの設計問題に対する設計レベルのアプローチまたはソリューションを提供します。デザインパターンを使用すると、問題を解決して、より迅速に提供できます。



1

Abstract Factoryパターンが過大評価されていることに気づきました。

まず第一に、起こらないことをあなたが設定していることが多い相互にインスタンス化するタイプを。

第二に、インターフェースによって提供される間接性(抽象化)のレベル通常、依存関係注入を処理するときに十分です。

WindowsButton、MacButton、WindowsScrollBar、MacScrollbarなどがあるWindowsGui vs MacGui vs ...の典型的な例は、ビジターやインタープリターパターンを使用して具体的なボタンやスクロールバーなどを定義することで実装が簡単になることがよくあります。実際の行動。


それには特定の目的があります。依存性注入を使用すると、コンポジットルートからさらに下のサービスロケータが不要になります。代わりに、注入された抽象ファクトリを使用します。
Adam Tuliper-MSFT、2011年

2
まあ... DIでサービスロケータを使用することは反パターンです。Abstract Factoryは、ランタイム値からDEPENDENCIESを作成する必要がある場合の汎用ソリューションです。
TheMentor 2013年

1

インスタンス化が非常に複雑で、1つのファクトリでは複雑で見苦しく、UIで理解するには複雑すぎる場所には、単純なファクトリパターンではなく、抽象的なファクトリパターンの場所があると思います。

これが単一クラスではなくTYPE_Aブランドであるとします。たとえば、Type-Aの100種類の類似クラスのファミリーがあり、それらから1つのオブジェクトをインスタンス化する必要があるとします。多くの類似したタイプのオブジェクトのブランドから正しいオブジェクトを作成するために必要な詳細で洗練された情報があり、このオブジェクトエンティティでは、調整するパラメーターとそれらを調整する方法を正確に知る必要があると想像してください。

このブランドの特別な工場では、それらを区別し、インスタンス化する正確なオブジェクトと、インスタンス化の方法を取得します。ネットからの入力(オンラインストアで利用できる色としましょう)や、バックグラウンドで実行されている他のアプリケーションやサービスからの入力(UIでは認識されないパラメーター)がわかります。

そして、たぶん明日は、インスタンス化するtype_Bとtype_Cのような別のファミリがあるでしょう。したがって、UIには「if else」があり、ユーザーが「type_A」、「type_B」、または「type_C」を必要とするかどうかがわかります。ただし、ファクトリークラスは、タイプ(ファミリー)からビルドするクラスを正確に決定します。チューニング方法–パラメータに設定する値、または請負業者に送信する値。このすべて-UIが認識していない多くのパラメーターによる。これらすべては、単一のファクトリークラスには多すぎます。


0

あなたの質問に直接答えるために、おそらくそのようなデザインパターンを使わずに逃げることができます。

ただし、現実世界のほとんどのプロジェクトは進化しているため、プロジェクトを将来を見据えたものにするために、ある種の拡張性を提供したいことに注意してください。

私自身の経験から、ほとんどの場合、ファクトリーが実装され、プロジェクトが成長するにつれて、抽象ファクトリーなどのより複雑な設計パターンに変更されます。


0

依存関係がすべてです。密結合と依存関係を気にしない場合は、抽象ファクトリは必要ありません。ただし、メンテナンスが必要なアプリケーションを作成するとすぐに問題になります。


0

あなたが.jarを作成し、他の誰かがそのjarを使用していて、コードで新しい具象オブジェクトを使用したいとします。抽象ファクトリを使用していない場合、彼女はあなたのコードを変更するか、コードを上書きする必要があります。しかし、抽象ファクトリを使用している場合は、ファクトリを提供してコードに渡すことができ、すべてが問題ありません。

洗練されたバージョン:以下のシナリオを検討してください:誰かがフレームワークを書きました。フレームワークは、抽象ファクトリといくつかの具体的なファクトリを使用して、実行時に多くのオブジェクトを作成します。したがって、独自のファクトリを既存のフレームワークに簡単に登録し、独自のオブジェクトを作成できます。フレームワークは変更に近づいていますが、抽象的なファクトリパターンにより、拡張は簡単です。


0

このパターンは、クライアントが作成するタイプを正確に知らない場合に特に役立ちます。例として、携帯電話を独占的に販売しているショールームがサムスン製のスマートフォンのクエリを取得するとします。ここでは、作成されるオブジェクトの正確なタイプがわかりません(電話のすべての情報が具体的なオブジェクトの形式でラップされていると想定しています)。しかし、私たちはサムスン製のスマートフォンを探していることを知っています。この情報は、設計に抽象ファクトリ実装がある場合に実際に利用できます。

C#での抽象ファクトリパターンの理解と実装


0

実際の例は、DbConnection、DbCommand、およびDbDataAdapterを含む抽象基本クラスを持ち、System.Data.SqlClientやSystem.Data.OracleClientなどの.NET Frameworkデータプロバイダーによって共有されるSystem.Data.Common名前空間で提供されます。開発者が特定のデータプロバイダーに依存しない一般的なデータアクセスコードを記述できるようにします。

DbProviderFactoriesクラスは、DbProviderFactoryインスタンスを作成するための静的メソッドを提供します。次に、インスタンスは、プロバイダー情報と実行時に提供される接続文字列に基づいて、厳密に型指定された正しいオブジェクトを返します。

例:

 DataTable allProvidersTable = DbProviderFactories.GetFactoryClasses();

ここに画像の説明を入力してください

        /* Getting SqlClient family members */
        DbProviderFactory dbProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
        DbCommand dbCommand = dbProviderFactory.CreateCommand();
        DbConnection dbConnection = dbProviderFactory.CreateConnection();
        DbDataAdapter dbDataAdapter = dbProviderFactory.CreateDataAdapter();

        SqlClientFactory sqlClientFactory = (SqlClientFactory)dbProviderFactory;
        SqlConnection sqlConnection = (SqlConnection)dbConnection;
        SqlCommand sqlCommand = (SqlCommand) dbCommand;
        SqlDataAdapter sqlDataAdapter = (SqlDataAdapter) dbDataAdapter;

        /* Getting OracleClient family members*/
        dbProviderFactory = DbProviderFactories.GetFactory("System.Data.OracleClient");
        dbCommand = dbProviderFactory.CreateCommand();
        dbConnection = dbProviderFactory.CreateConnection();
        dbDataAdapter = dbProviderFactory.CreateDataAdapter();

        OracleClientFactory oracleClientFactory = (OracleClientFactory)dbProviderFactory;
        OracleConnection oracleConnection = (OracleConnection)dbConnection;
        OracleCommand oracleCommand = (OracleCommand)dbCommand;
        OracleDataAdapter oracleDataAdapter = (OracleDataAdapter)dbDataAdapter;

例-2 ここに画像の説明を入力してください

コードソリューションアーキテクチャ ここに画像の説明を入力してください

コンクリートファクトリインスタンスは、以下のように静的ファクトリメソッドを使用して提供されます

public  class FurnitureProviderFactory
{
    public static IFurnitureFactory GetFactory(string furnitureType)
    {
        if (furnitureType == "Wood")
        {
            return new WoodenFurnitureFactory();
        }
        if (furnitureType == "Plastic")
        {
            return new PlasticFurnitureFactory();
        }
        throw new Exception("Undefined Furniture");
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.