web-dev-qa-db-ja.com

クライアントから隠されたオブジェクトを作成するためのデザインパターン

次のシナリオに最適なデザインパターンを理解できません。アプリケーションはデータベースを使用します。そのデータベースは、本番バージョン([〜#〜] sql [〜#〜])またはテストバージョン(テスト)。これが私の現在のデザインです:

enter image description here

このデザインの問題は、アプリケーションがTestを作成する必要があることです(これは純粋にテストのためです)。ただし、Applicationは、使用するデータベースへの参照を何らかの方法で取得する必要があります。[〜#〜] sql [ 〜#〜]またはTest

アプリケーションをだましてデータベースのTestバージョンを作成するためにどのような設計パターンを実装できますか?このソリューションはUMLでどのように見えますか?

すでに十分な調査を行って、選択を抽象ファクトリー、ビルダー、またはファクトリーメソッド(またはそれらの組み合わせ)に絞り込みました

2
Corey P

プログラムの一部で特定のサブクラスをインスタンス化したいが、インスタンス化の動作を他のクライアントコード(Applicationクラス)から隠したい場合は、おそらくファクトリメソッドDPを使用する必要があります。

それはインスタンス化の部分に関係しています。サブクラス間の実際の動作の違いに関しては、@ Paulによって述べられているように、戦略DPが良い解決策になる可能性があります。

4

あなたの質問の要点に入る前に、あなたが提供した構造に特定の問題があることを指摘させてください(あなたは気づいていますが、少し詳しく説明しましょう):

enter image description here

この設計では、依存関係の逆転を使用して、クライアント(アプリケーション)と具象サービス(DBゲートウェイ-SQL、テスト)を互いに独立させ、それぞれをIDatabaseインターフェイスに依存させます。ただし、アプリケーションが具象サービスを作成するという事実により、具象タイプに依存するようになり、ある程度まで全体のセットアップが台無しになります。これが実際に問題である度合いは、クライアントコードの動作、開発者の規律(アプリケーションクラスの明確に定義された部分への結合の分離に関する)、使用される手法(たとえば、画像が少し変化する場合、具体的なサービスはリフレクションを通じてインスタンス化されます)、コードをライブラリに編成する方法など。

伝統的なOOテクニックだけに依存して、クライアントコードがIDatabaseを実装する具象タイプを認識していないことを確認する方法は、これらのタイプすべてを知っている3番目のコードを導入することです。 、したがってそれらを接続することができます(これにより、これらの型を分離したままにすることができます)。依存関係注入を使用するアプローチでは、この3番目のコードは通常構成になりますroot(多かれ少なかれ、アプリケーションのエントリポイント)。

この構造に従うように、投稿した元のクラス図を変更する1つの方法は、Applicationクラスを2つの部分に分割することです。 1つのパーツはコンポジションルート(他のパーツの作成と接続、実行、または実行を可能にするting)になります。もう1つの部分は、IDatabaseを使用するクライアントコードです。

これにより、コンストラクターインジェクションを介してテストコードに具象データベースインスタンスを提供することで、この他の部分(クライアントコードに含まれる実際のビジネスロジック)を分離して(たとえば、ユニットテスト)テストできます。

これまで説明したように、具体的なインスタンスを作成する場合、コンポジションルートは基本的に「設定としてのコード」を使用しますが、必要に応じて(または必要に応じて)、設定を読み取らせることで、本当に設定可能にすることができます。ファイルし、そのデータに基づいてその決定を行うか、DIコンテナーに依存して、構成、依存関係の注入、およびオブジェクトのライフサイクルを管理できます。

4

したがって、実行時に実装を選択することに興味があるので、あなたの質問に対する簡単な答えは、あなたが見つけようとしているパターンはおそらく Strategy Pattern であるということです。特に、コードの実装が非常に異なる場合(たとえば、実際のDBはRDBMSですが、テストDBはテストデータのフラットファイルにすぎません)

しかし、そうは言っても、必要な設計パターンに関してソフトウェアを考えて考えるのは間違いだと思います。ほとんどの場合、可能な限り単純なアプローチから始め、単純なアプローチでは不十分な場合はパターンにリファクタリングする必要があります。

どういう意味?

さて、あなたのケースを取ってみましょう。最も単純なバージョンでは、環境(製品またはテスト)に基づいてDB接続文字列を変更するだけでよいと主張します。したがって、接続情報を環境変数に入れて、それで完了です。

さらに変更が必要な場合は、別のシードデータが必要だとします。次に、テストロジックの前に実行する関数をテストに追加して、コンテンツを更新するか、別の環境変数に基づいて選択するシードデータファイルを用意します。

さらに多くの違いが必要な場合は、設計で問題が発生する可能性が高くなります。なぜなら、本番環境で行っていることとは異なるものをテストするのに、なぜ多くの時間を費やすのでしょうか。

OK、私はあなたをだましました。この問題を解決するために戦略パターンを使用する理由は実際にはほとんどありません。しかし、それはですあなたが質問をした方法であなたが「探していた」ものです。どういう意味?

3
Paul

設計では、アプリケーション作成データベースです。ただし、アプリケーションにはデータベースを選択するために必要な情報がありません。代わりに、データベースをアプリケーションに渡します。

たとえば、アプリケーションのスタートアップコードでは次のようになります。

データベースdb =新規 MySQLConnection(...); 
 Application app = new Application(db); 
 app.run(); 

しかし、あなたのテストコードでは:

データベースdb =新規 TestDBConnection(...); 
 Application app = new Application(db); 
 app.run(); 

つまり、Databaseインターフェースを使用して、アプリケーションに依存関係を挿入します。

コンストラクター引数または関数引数を介して依存関係を手動で注入する代わりに、依存関係注入フレームワークを使用することもできます。次に、アプリケーションはDIフレームワークからDatabaseオブジェクトを要求し、特定のクラスを使用してDatabaseインターフェースを実装するようにDIフレームワークを以前に構成していました。ただし、フレームワークの使用は必須ではなく、コンストラクター注入はほとんどのプロジェクトで問題なく機能します。

3
amon

他の回答を補足するUMLは次のとおりです。

工場

Factoryパターンには少なくとも3つのバリアントがあります。たとえば、Static、Concrete、Factory Method、およびAbstract Factoryです。 Staticはインターフェイスでは実際には機能しないため、アプリケーションに作成の詳細を知らせない場合は、Concreteで十分です。 Factory MethodとAbstract Factoryは、あなたが質問で与えた状況に適切ではないようです。

アプリケーションがファクトリを使用するとき、データベースがインスタンス化される方法の詳細が非表示であっても、アプリケーションはまだデータベースがインスタンス化されるを決定します工場で。

Application use Factory to create the instance

依存性注入

アプリケーションはインスタンス化されないデータベースを決定しません。 Deciderなどの他のクラスは、そうします。 Deciderは、ファクトリを使用して、インスタンス化の詳細から隔離します(上記のように)。

Another class decides which implementation and passes it to Application

1
Fuhrmanator