このシナリオを検討してください。時々ログに書き込むために必要となるいくつかのビジネスロジックがあります。
interface ILogger
{
void Log(string stuff);
}
interface IDependency
{
string GetInfo();
}
class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var intermediateResult = PerformInterestingStuff(input, info);
if (intermediateResult== "SomethingWeNeedToLog")
{
// How do I get to the ILogger-interface?
}
var result = PerformSomethingElse(intermediateResult);
return result;
}
}
どのようにしてILoggerインターフェイスを取得しますか?主な可能性は2つあります。
あなたはどちらの方法を選びますか、そしてその理由は?それとももっと良いパターンはありますか?
pdate:すべてのメソッド呼び出しをログに記録する必要がないことに注意してください。メソッド内で発生する場合と発生しない場合があるいくつかの(まれな)イベントをログに記録したいだけです。
個人的には両方を混ぜてやっています。
ここに私の慣習があります:
これにより、テスト容易性の適切なバランスが得られると思います。 DIを使用するよりもService Locationを使用するクラスに対してテストを設定するのが少し難しいので、これがService Locationがルールではなく例外になる理由です。ただし、その使用方法には一貫性があるため、どのような種類のテストを記述する必要があるかを覚えておくのは難しくありません。
DIがコンストラクターを混乱させる傾向があるという懸念を提起する人もいます。これは問題ではないと思いますが、このように感じる場合、DIを使用する代替案がいくつかありますが、コンストラクターのパラメーターは使用しないでください。 NinjectのDIメソッドのリストを次に示します。 http://ninject.codeplex.com/wikipage?title=Injection%20Patterns
ほとんどのInversion of ControlコンテナーにはNinjectと同じ機能があることがわかります。彼らが最も簡潔なサンプルを持っているので、私はNinjectを示すことにしました。
うまくいけば、これは役に立ちます。
編集:明確にするために、Unityと Common Service Locator を使用します。私はDI用のUnityコンテナーのシングルトンインスタンスを持っています。IServiceLocatorの実装は、そのシングルトンUnityコンテナーのラッパーです。これにより、型マッピングを2回行う必要がなくなります。
また、AOPがトレース以外に特に役立つとは思いません。わかりやすくするために、手動ロギングの方が好きです。ほとんどのAOPロギングフレームワークが両方に対応していることは知っていますが、前者(AOPのパンとバター)はほとんどの場合必要ありません。もちろん、これは個人的な好みです。
ロガーは明らかにビジネスロジックが依存するサービスであるため、IDependency
と同じ方法で依存関係として扱う必要があります。ロガーをコンストラクターに挿入します。
注: AOPがtheロギングを注入する方法として記載されている場合でも、この場合の解決策であることに同意しません。 AOPは実行トレースに最適ですが、ビジネスロジックの一部としてロギングするためのソリューションにはなりません。
私の小さな経験則:
クラスライブラリ内にある場合は、コンストラクターインジェクションまたはプロパティインジェクションのいずれかをnullオブジェクトパターンと共に使用します。
メインアプリケーション内にある場合は、サービスロケータ(またはシングルトン)を使用します。
Log4netを使用する場合、これはかなりうまくいくと思います。クラスライブラリが存在しない可能性のあるものに手を伸ばしたくはありませんが、アプリケーションプログラムでは、ロガーが存在することを知っており、log4netのようなライブラリはサービスロケーションパターンに大きく基づいています。
ロギングは、本当にDIを必要としないほど十分に静的なものと考える傾向があります。特に、そこにあるすべてのロギングフレームワークは非常に柔軟で拡張が容易であるため、アプリケーションのロギング実装を変更することはほとんどありません。異なるロガーをすでに使用している複数のアプリケーションでライブラリを使用する必要がある場合、クラスライブラリではより重要です。
もちろんYMMV。 DIは素晴らしいですが、すべてをDIする必要があるという意味ではありません。
すべてのロギング/トレーシングをPostSharp(AOPフレームワーク)属性に切り替えました。メソッドのロギングを作成するために必要なことは、属性をメソッドに追加することだけです。
利点:
this を確認してください。
たぶんこれは少し話題から外れますが、クラスの冒頭でタイプするだけでロガーを注入する必要があるのはなぜですか?
Logger logger = LogManager.GetLogger("MyClassName");
ロガーは、開発中およびその後のメンテナンス中に変更されません。現代のロガーは高度にカスタマイズ可能であるため、議論
テキストロガーをデータベースに置き換えたい場合はどうなりますか?
逃した。
依存性注入を使用して否定するのではなく、あなたの心に興味があるだけです。
別のタイプを派生させることができます。 LoggableBusinessObject
は、コンストラクターでロガーを取ります。つまり、それを使用するオブジェクトのロガーのみを渡します。
public class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public virtual string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var result = PerformInterestingStuff(input, info);
return result;
}
}
public class LoggableBusinessObject : MyBusinessObject
{
private ILogger _logger;
public LoggableBusinessObject(ILogger logger, IDependency dependency)
: base(dependency)
{
_logger = logger;
}
public override string DoSomething(string input)
{
string result = base.DoSomething(input);
if (result == "SomethingWeNeedToLog")
{
_logger.Log(result);
}
}
}
シングルトンサービスを希望します。
依存性注入はコンストラクタを混乱させます。
AOPを使用できる場合は、それが最適です。
ここではDIがうまく機能します。もう1つ注目するのは [〜#〜] aop [〜#〜] です。
これらのアプローチはどちらもお勧めしません。アスペクト指向プログラミングを使用する方がよい。ロギングはAOPの「hello world」です。