質問は少し奇妙に聞こえるかもしれませんが、私はそれがそうだと思います。いくつかのデザインパターンを閲覧しているときに、その質問に思いつきました。私は悪名高い州/戦略パターンに来て、このコードを思いつきました:
public class Account
{
public double Balance { get; private set; }
private IAccountState state;
public Account()
{
state = new ActiveAccountState();
}
public void Deposit(double number)
{
state = state.Deposit(() => Balance += number);
}
public void Withdraw(double number)
{
state = state.Withdraw(() => Balance -= number);
}
public void Freeze()
{
state = state.Freeze();
}
public void Close()
{
state = state.Close();
}
}
もちろん、それは単純化されており、プロダクションの準備はできていませんが、それがそうなるように努力しているわけではありません。簡単な例です。このコードは実際に私が抱えている問題をよく表しています。基本的に私は状態/戦略を表すIAccountStateインターフェイスを持っています。これは依存関係と見なすことができ、AccountクラスがActiveAccountStateを作成する方法を知ることができるようにすることで、Accountクラスに複数の責任を持たせることができます。 AccountStateFactoryを作成して、アカウントクラスに依存させることができます。これにより、Accountクラスのコンストラクターを使用して、この新しく作成されたFactory依存関係を簡単に挿入できます。しかし、このようにして、実装の詳細を公開します。これは、おそらくAccountクラスのみに関係し、他の関係者には関係ありません。それはユニットテストを少し簡単にするでしょう。
私の質問は、何かが依存関係であるかどうかを知る方法ですか?時々、それは完全に単純です(つまり、サービス、ファクトリー、プロバイダーなど)が、混乱することがあります(この状態/戦略の例のように)。前もって感謝します!
依存関係があるかどうかを知る方法は?
あなたが何かをする必要があるが、ここでそれをしたくないときはいつでも。
参照渡し(または、派手であることを主張する場合は依存性注入)について私が理解してほしい大きなことは、既知の適切なデフォルトを使用しても問題ないということです。新品の使用を禁じられていません。 newが指摘するものをオーバーライドする方法を提供せずにnewを使用することは禁止されています。
これは、名前付きパラメーターを持たない言語(Javaなど)ではあまり理解されていません。 C#にはそれらがあるので、言い訳はありません。 Josh BlochはJava users his builder pattern を提供してこれを修正したので、あなたがそれを知っていれば、言い訳もありません。
名前付きパラメーターを使用すると、オプションの引数を簡単に設定できます。それがあれば、デフォルト値を簡単に設定できます。状態はオーバーライド可能なデフォルト値である必要があります。これにより、自分で構築できない無力なクラスに悩まされることなく、DIを使用できます。
このDIへの異なるアプローチの背後にある哲学は convention over configuration と呼ばれます。それを試してみてください。きっとこれらのアイデアは理論的ではなく、より有用なものになるでしょう。
あなたが尋ねなかった他の問題があり、これはそうではないため Code Review 私はそれらすべてに入るわけではありませんが、double Balance
を無視することはできません。ドルやセントを表すものではないことを教えてください。 Doubleは 離散インチ (基数2)および非離散/連続測定(基数なし)では有効ですが、基数10の通貨では有効ではありません。 IEEE浮動小数点と会計士 めったにうまくいかない 。
状態/戦略の分離を提供していないか、Accountの責任を軽減していません。 Accountの呼び出し元には、AccountとActiveAccountStateの間に分離はありません。 Accountのメソッドを呼び出すと、ActiveAccountStateのメソッドも呼び出されます。したがって、期待される結果は、AccountおよびActiveAccountStateクラスの両方に依存します。 Accountの単体テストは、ActiveAccountStateの変更に敏感になります。
IAccountStateインスタンスをAccountに注入することで実際にそれらを分離する場合、AccountIAccountStateのモック実装を使用します。これらのテストは、ActiveAccountStateへの変更の影響を受けなくなります。これは、Accountインスタンスの作成を複雑にします。ファクトリクラスまたは依存関係注入コンテナが役立つ場合があります。
知る方法?コードをテストファーストで記述してみてください。複雑なテストを書くのは面白くないので、テストを容易にするためにクラス設計を最適化し始めます。テストが容易なコードは保守が容易です。
ところで、なぜBalanceはAccountのメンバーですか?確かにBalanceはアカウントの状態によって所有されるべきです。
ActiveAccountStateは明らかに依存関係ですが、問題はinjectこの依存関係を作成するか、アカウント内で作成するかです。この場合、注射によるメリットはありません。
アカウントはActiveAccountStateインスタンスを作成しますが、これはnotがアカウントを「ActiveAccountStateの製造方法を知っている」ことを意味します。この知識は、おそらくActiveAccountStateのコンストラクタ内にカプセル化されています。アカウントは、パブリックインターフェイスを使用して状態遷移を実行するのと同じように、パブリックインターフェイスを使用しています。
一般に、内部をできるだけ公開しないようにします。私には、ActiveAccountStateは純粋にアカウント内部のメカニズムであり、アカウントのクライアントに公開されるべきではないようです。依存関係を注入することを要求するということは、この依存関係をクライアントに公開することを意味します。
私自身もこの問題に単一責任パターンを適用します。
ビジネス動作を提供するクラスは、その依存関係をインスタンス化する責任を負いません。
したがって、疑わしい場合はDIを支持してください。
私の質問は、何かが依存関係であるかどうかを知る方法ですか?
確かなOOクラス設計から始めて、依存関係は自明になる傾向があります。
IAccountState
は、他の方法では一貫性のあるクラスの動作と状態を不適切に分離することにより、依存関係を生み出しています。しかし、あなたは事実上、頭の上の釘を打ちました:「私は、実装の詳細を公開します。これは、おそらくAccountクラスのみに興味があり、他には興味がないでしょう」
この場合の抽象クラスは、戦略パターンを主張しながらクラスフラクチャを解決し、人為的な依存関係の問題を排除できます。そして、具体的、抽象的、それ以外のコンストラクタやメソッドがあるところはどこでも、依存性注入のための継ぎ目があります。
「継承チェーンを介してロジックを焼くとき、DIの機会は限られている」ので、継承から遠ざけることに熱心に同意しません。オブジェクト指向のテナントに自由に違反するのではなく、DIの設計された機会が必要です。
優れたクラスは機能を公開し、状態を隠します
重要なのは、各クラスの単一の責任を識別することです。最初に機能を考え、次にそれをサポートするために必要な状態を考えます。これは、優れたSRPアプリケーションへのより信頼できるパスです。
Diligent design embiggens依存関係
interface
sホブルSRPアプリケーションの熱狂を許可しないでください。状態や機能を早期に分解します。
継承と構成は相補的です。分析および設計中にこれを進化させます。実際、どのマシンも別個の部品で作られているのと同じように、これが見られると期待しています。
問題のスタンスを探すソリューションからこれに近づかないでください。
コンポジションを使用して懸念を分離した場合は、常にDIを使用してください。
コンポジションを使用しているという事実は、テスト用のモックを使用している場合に限り、依存関係をスワップアウトすることを意味します。
代わりに継承を使用すると、継承チェーンを通じてロジックを具象クラスに焼き付けるときに、DIの機会が制限されます。
したがって、あなたの例では、さまざまな注入されたAccountStateオブジェクトによって管理されるさまざまな引き出しロジックを持つBusinessアカウントとCurrentアカウントがあるとします。
継承を使用している場合は、引き出しメソッドをオーバーライドする個別のBusinessAccountクラスとCurrentAccountクラスを作成します