私のコードでは、コンストラクターのパラメーターリストを使用して、多くのクラスにロガーを挿入しています
ランダムに配置していることに気づきました。リストの最初の場合もあれば、最後の場合もあります。
好みはありますか?私の直感では、この場合は一貫性が役立つと私は言います。私の個人的な好みは、最初にそれを置くことです。欠けているときに気づきやすく、そこにあるときにスキップしやすくなります。
ロガーは「横断的関心事」と呼ばれるものです。それらは、アスペクト指向プログラミングなどの手法に屈します。クラスを属性で装飾したり、コードを織り込んだりする方法がある場合、それはオブジェクトとパラメーターリストを「純粋」に保ちながらログ機能を取得する良い方法です。
ロガーを渡す必要がある唯一の理由は、さまざまなロギング実装を指定する必要がある場合ですが、ほとんどのロギングフレームワークには、たとえばさまざまなロギングターゲットに対してそれらを構成できる柔軟性があります。 (ログファイル、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)など)。コードベース全体で使用されるものは、いつか一貫性の恩恵を受けるでしょう。
あるいは、何も渡される必要なしに、すべてのクラスに独自のロガーオブジェクトをインスタンス化させることもできます。ロガーが機能するためには、「マジックによって」いくつかのことを知る必要がありますが、クラス定義でファイルパスをハードコーディングすると、何百もの異なるクラスに正しく渡すよりも保守性が高く、面倒が少なく、間違いなく、グローバル変数を使用して上記の面倒な作業をバイパスするよりもはるかに邪魔ではありません。 (確かに、ロガーはグローバル変数の合法的なユースケースのごく一部です)
ロガーはクラスに渡されるのではなく静的にアクセスされるべきであるという提案に同意します。ただし、渡したい強い理由がある場合(おそらく、異なるインスタンスが別の場所などにログを記録したい場合)は、コンストラクタを使用して渡すのではなく、個別に呼び出すことをお勧めします。たとえば、 Class* C = new C(); C->SetLogger(logger);
ではなくClass* C = new C(logger);
このメソッドを選択する理由は、ロガーが本質的にクラスの一部ではなく、注入された機能が他の目的で使用されるためです。コンストラクターリストに配置すると、クラスの要件となり、クラスの実際の論理状態の一部であることを意味します。たとえば、(すべてではないがほとんどのクラスで)_X != Y
_の場合はC(X) != C(Y)
であると期待するのが妥当ですが、同じクラス。
言及する価値があります。ここで触れている他の回答を見たことがないのは、プロパティまたは静的を介してロガーを挿入することにより、クラスのユニットテストが困難になることです。たとえば、プロパティを介してロガーを注入する場合、ロガーを使用するメソッドをテストするたびに、そのロガーを注入する必要があります。これは、クラスがそれを必要とするため、同様の可能性があるをコンストラクタの依存関係として設定することを意味します。
Staticは同じ問題に役立ちます。ロガーが機能しない場合は、クラス全体が失敗します(クラスがロガーを使用している場合)-ロガーは必ずしもクラスの責任の「一部」ではありませんが、プロパティインジェクションほど悪いわけではありません。少なくとも、ロガーは常にある意味で「そこにある」ことを知っています。
特にTDDを採用している場合は、考えてみてください。私の意見では、ロガーはクラスのテスト可能な部分の一部であってはなりません(クラスをテストするときは、ロギングもテストするべきではありません)。
ロガーオブジェクトを各クラスインスタンスに渡すのが面倒です。したがって、私のコードでは、これらの種類のものは静的フィールドにあるか、静的フィールドのスレッドローカル変数にあります。後者は一種のクールで、スレッドごとに異なるロガーを使用でき、マルチスレッドアプリケーションで意味のある予期される何かを実行するロギングをオンまたはオフにするメソッドを追加できます。