回答:
ロガーは、「横断的関心事」と呼ばれるものです。それらは、アスペクト指向プログラミングなどの手法に屈します。クラスを属性で装飾したり、コードウィービングを実行したりする方法がある場合、それはオブジェクトとパラメーターリストを「純粋」に保ちながらロギング機能を得る良い方法です。
ロガーに渡すことができる唯一の理由は、異なるロギング実装を指定したい場合ですが、ほとんどのロギングフレームワークには、たとえば、異なるロギングターゲット用にそれらを設定できる柔軟性があります。(ログファイル、Windowsイベントマネージャーなど)
これらの理由から、ロギングを目的としてすべてのクラスにロガーを渡すのではなく、ロギングをシステムの自然な部分にすることを好みます。したがって、私が通常行うことは、適切なロギング名前空間を参照し、クラスでロガーを使用するだけです。
まだロガーを渡したい場合は、パラメーターリストの最後のパラメーターにすることをお勧めします(可能な場合はオプションのパラメーターにします)。それを最初のパラメータにすることはあまり意味がありません。最初のパラメーターは最も重要なパラメーターであり、クラスの操作に最も関連するパラメーターである必要があります。
関数がオーバーロードしている言語では、引数がオプションである可能性が高いほど、より適切であると主張します。これにより、オーバーロードが欠落している場所でオーバーロードを作成するときに一貫性が生まれます。
foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);
関数型言語では、逆の方が便利です。デフォルトを選択する可能性が高いほど、左にあるはずです。これにより、単に引数を適用することで、関数を簡単に特化できます。
addThreeToList = map (+3)
ただし、他の回答で述べたように、おそらくシステム内のすべてのクラスの引数リストにロガーを明示的に渡したくないでしょう。
あなたは間違いなくこの問題を過剰に設計することに多くの時間を費やすことができます。
正規のロギングを実装した言語の場合は、すべてのクラスで正規のロガーを直接インスタンス化するだけです。
正規の実装を持たない言語の場合、ロギングファサードフレームワークを見つけて、それに固執するようにしてください。slf4jはJavaで適切な選択です。
個人的には、単一の具体的なロギング実装に固執し、すべてをsyslogに送信します。優れたログ分析ツールはすべて、複数のアプリサーバーからのsysoutログを組み合わせて包括的なレポートを作成できます。
関数シグネチャに1つまたは2つの依存関係サービスと「実際の」引数が含まれる場合、依存関係を最後に配置します。
int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)
私のシステムではこのようなサービスが5つ以下である傾向があるため、すべての機能シグネチャでサービスが同じ順序で含まれるように常に確認します。アルファベット順は他と同じです。(さておき:ミューテックス処理のためのこの方法論的アプローチを維持すると、デッドロックが発生する可能性が低くなります。)
アプリ全体で十数個以上の依存関係を注入していることに気付いた場合、おそらくシステムを別々のサブシステムに分割する必要があります(マイクロサービスと言ってもいいですか?)。
ロガーは、文字通りあらゆる場所で利用できる必要があるため、少し特殊なケースです。
ロガーをすべてのクラスのコンストラクターに渡すことを決定した場合は、その方法について一貫した規則を確実に設定する必要があります(たとえば、常に最初のパラメーターは常に参照渡し、コンストラクターの初期化リストは常に開始されます) m_logger(theLogger)など)。コードベース全体で使用されるものは、いつか一貫性の恩恵を受けるでしょう。
または、すべてのクラスに、渡されるものを何も必要とせずに、独自のロガーオブジェクトをインスタンス化させることができます。数百の異なるクラスに正しく渡すよりもずっと保守しやすく、面倒ではなく、グローバル変数を使用して上記の面倒を回避するよりも間違いなくはるかに害が少ない。(確かに、ロガーはグローバル変数の非常に少数の正当なユースケースの1つです)
ロガーはクラスに渡すのではなく、静的にアクセスする必要があることを示唆している人々に同意します。あなたがそれを通過したい強い理由がある場合は(おそらく異なるインスタンスが異なる場所か何かにログインする)その後、私はあなたが、コンストラクタを使用して、それを通過しないことをお勧めではなく、その例を行うために別の電話をかけることClass* C = new C(); C->SetLogger(logger);
はなく、よりClass* C = new C(logger);
このメソッドを好む理由は、ロガーが本質的にクラスの一部ではなく、他の目的で使用される注入された機能であるためです。コンストラクタリストに配置すると、クラスの要件になり、クラスの実際の論理状態の一部であることを暗示します。もし、例えば、(すべてではないが、ほとんどのクラスで)、予想するのが妥当であるX != Y
そしてC(X) != C(Y)
あなたが同じクラスのあまりのインスタンスを比較している場合は、ロガー不平等をテストするとは考えにくいです。
言及する価値はありますが、ここで他の答えが触れていないのは、プロパティまたは静的を介してロガーを注入することにより、クラスのユニットテストが難しくなります。たとえば、プロパティを介してロガーを挿入する場合、ロガーを使用するメソッドをテストするたびにそのロガーを挿入する必要があります。この手段は、あなたは同様かもしれないクラスは、それを必要としないので、それはコンストラクタの依存関係として設定しています。
静的は同じ問題に役立ちます。ロガーが機能しない場合、クラス全体が失敗します(クラスがロガーを使用している場合)-ロガーは必ずしもクラスの責任の「一部」ではありませんが、プロパティインジェクションほど悪くはありませんが、少なくとも、ある意味でロガーは常に「そこ」にあることを知っています。
特にTDDを使用している場合は、考えてみてください。私の意見では、ロガーは実際にはクラスのテスト可能な部分の一部であってはなりません(クラスをテストするとき、ロギングもテストするべきではありません)。