Dependency Injection は、new
のカジュアルな使用を禁止します。それは古き良き参照渡しを支持します。これは、new
を呼び出しスタックのできるだけ高い位置に置くことを示しています。
Convention over Configuration は、一般的なパスがイージーパスである必要があることを示しています。何かの典型的なバージョンを構築するのに、それほどの労力は必要ありません。
これらの考えは矛盾しているようです。それらを調整する方法はありますか?
たとえば、次のリファクタリングよりも上手くできますか?
class WelcomePage{
showAboutDialog(){
new AboutDialog().show();
}
}
これは、拡張機能に対して開かれていないため、OCPに違反しています。ただし、これ:
class WelcomePage{
AboutDialog aboutDialog;
WelcomePage(AboutDialog aboutDialog)
showAboutDialog(){
aboutDialog.show();
}
}
構成に関する規約に違反しています。はい、OCPでの延長が許可されています。しかし、aboutDialogをパラメーターとして使用することで、構成状況を強制しました。
考慮してください:
class WelcomePage{
AboutDialog aboutDialog = new AboutDialog();
WelcomePage(){}
WelcomePage(AboutDialog aboutDialog){
this.aboutDialog = aboutDialog;
}
showAboutDialog(){
aboutDialog.show();
}
}
AboutDialog
はオーバーライド可能なデフォルト値であるため、これはOCPに従います。それは拡張可能です。 AboutDialog
は今では慣例ですが、必須ではありません。以前のバージョンの欠点は、AboutDialog
が使用されていない場合でも、WelcomePage
と共に展開する必要があることです。以前のものは、AboutDialog
を完全に抽象化することで、それを伴う実装のドラッグを回避できました。
これは、重要な境界を越えて、個別に展開することを目的としたユニットに使用するソリューションではありません。しかし、すべてのクラスの境界をそれほど正式に扱う必要があるとは思いません。クラスがあると便利なだけでクラス境界が存在する場合があります。
オーバーライド可能なデフォルト値は、デプロイメント境界を超えない限り、純粋な依存性注入スタイルで構成より規約を達成できるようです。
より自由な方法はありますか?無視した考慮事項?もっと上手くできる?
設定に関する規約は、「簡単な道を歩もう」という意味ではありません。これは、特定のプログラミング決定がデフォルトですでに行われていることを意味します。これは、開発者が行う必要のある決定の数を減らすためにソフトウェアフレームワークによって使用されます。カスタム構成の柔軟性を失うことなく。シンプルさの向上は、開発者がそれらを上書きしたくない場合を除き、フレームワークが使用されるたびに構成を決定する必要がないことによるものです。
ASP.NET MVCでのコンベンションオーバーコンフィグレーションの非常に簡単な例から始めましょう。
ASP.NET MVCでは、ProductsController
というクラスをController
に置くだけで、URL http:// localhost/Products/List をコントローラーにマップできます。フォルダ。 ASP.NET MVCは正しいことを行い、List
クラスのProductsController
メソッドを喜んで呼び出します。これ以上の作業は必要ありません。これを機能させるために、クラスを属性で装飾する必要はなく、構成ファイルも必要ありません。コントローラークラスは、(URLルーティングで識別される)あるドメインに対応する名前とController
のサフィックスを持つという慣例に従う必要があるだけです。
依存性注入は、インターフェースと具象型の間のマッパーと考えてください。 TDDの原則に従っている場合、クラスにはインターフェースをパラメーターとして取るコンストラクターが必要です。集約ルートで、DIコンテナーに各インターフェースに対応する具象型を指示し、DIコンテナーがクラスのコンストラクターの1つでそのインターフェースを検出すると、そのインターフェースに指定した型のインスタンスを作成します(追加のいくつかとともに)オブジェクトの存続期間管理などの便利な機能)を使用して、コンストラクタパラメータに自動的に渡します。
言い換えると、DIコンテナは /のような規約の形式です特にタイプのようなトリックを使用する場合検出と名前のマッピングISomeClass
からSomeClass
まで。プログラマーが提供するマッピングが存在しない場合にインスタンス化するクラスを決定します。
構成に関する規約 のように見える可能性があります 「遠くでの不気味なアクション」規約を覚えていない場合、それに大きく依存しているシステムをデバッグするのは難しい場合があります。 Mark Heathによると、 "[すべての参照を検索]は、これらの規則が使用されている場合は何も返しません。リフレクションは通常、呼び出されるメソッドを検出するために実行時に使用されるためです。これは一体どのように機能するのでしょうか?」
私はロバートの回答が好きです(私はそれに投票しました)が、私はこの種の状況で私が使用するパターンをいくつかの成功とともに見ることに興味があるかもしれないと思いました。
の代わりに:
class WelcomePage{
AboutDialog aboutDialog = new AboutDialog();
WelcomePage(){}
WelcomePage(AboutDialog aboutDialog){
this.aboutDialog = aboutDialog;
}
showAboutDialog(){
aboutDialog.show();
}
}
次のように書くことができます:
class WelcomePage{
IAboutDialog aboutDialog = null;
WelcomePage(){}
WelcomePage With(IAboutDialog aboutDialog){
this.aboutDialog = aboutDialog;
return this;
}
WelcomePage WithDefaultDependencies(){
return this.With(new AboutDialog());
}
showAboutDialog(){
aboutDialog.show();
}
}
したがって、通常の(デフォルト)ケースでは、次のようにインスタンス化します。
var welcomePage = new WelcomePage().WithDefaultDependencies();
しかし、単体テストスイートで、または拡張したい場合は、これを行うことができます。
var welcomePage = new WelcomePage().With(new MySpecialAboutDialog());
これにより、デフォルトの設定をかなり簡単に使用できますが、拡張も可能です。