パラメータリスト内のロガーの位置はどうあるべきか[閉じた]


12

私のコードでは、コンストラクターのパラメーターリストを介して、多くのクラスにロガーを挿入しています

ランダムに配置していることに気付きました。リストの最初に表示される場合もあれば、最後に表示される場合もあります。

好みはありますか?私の直感では、一貫性はこの場合に役立ち、私の個人的な好みはそれを最初に置くことです。

回答:


31

ロガーは、「横断的関心事」と呼ばれるものです。それらは、アスペクト指向プログラミングなどの手法に屈します。クラスを属性で装飾したり、コードウィービングを実行したりする方法がある場合、それはオブジェクトとパラメーターリストを「純粋」に保ちながらロギング機能を得る良い方法です。

ロガーに渡すことができる唯一の理由は、異なるロギング実装を指定したい場合ですが、ほとんどのロギングフレームワークには、たとえば、異なるロギングターゲット用にそれらを設定できる柔軟性があります。(ログファイル、Windowsイベントマネージャーなど)

これらの理由から、ロギングを目的としてすべてのクラスにロガーを渡すのではなく、ロギングをシステムの自然な部分にすることを好みます。したがって、私が通常行うことは、適切なロギング名前空間を参照し、クラスでロガーを使用するだけです。

まだロガーを渡したい場合は、パラメーターリストの最後のパラメーターにすることをお勧めします(可能な場合はオプションのパラメーターにします)。それを最初のパラメータにすることはあまり意味がありません。最初のパラメーターは最も重要なパラメーターであり、クラスの操作に最も関連するパラメーターである必要があります。


11
+!ロガーをコンストラクターのパラメーターから遠ざけます。私は誰もが同じことを使用している知っているので、私は、静的なロガーを好む
スティーブンA. Loweの

1
@ StevenA.Lowe注意が必要な場合は、静的ロガーを使用すると他の問題が発生する可能性があることに注意してください(C ++の静的初期化順序の失敗など)。ロガーをグローバルにアクセス可能なエンティティとして持つことには魅力があることに同意しますが、そのような設計がアーキテクチャ全体に適合するかどうか、およびどのように適合するかを慎重に評価する必要があります。
ComicSansMS

@ComicSansMS:もちろん、スレッド化の問題など。個人的な好み-「できるだけ単純だが単純ではない」;)
スティーブンA.ロウ

静的ロガーを持つことが問題になる場合があります。依存関係の注入が難しくなります(DIコンテナーによってインスタンス化されたシングルトンロガーを意味する場合を除きます)。また、アーキテクチャを変更したい場合は痛いかもしれません。たとえば、現在、各関数の実行にパラメーターとしてロガーを渡すAzure関数を使用しているため、コンストラクターを介してロガーを渡すことを後悔しています。
Slothario

@Slothario:それは静的ロガーの美しさです。いかなる種類の注入も必要ありません。
ロバートハーベイ

7

関数がオーバーロードしている言語では、引数がオプションである可能性が高いほど、より適切であると主張します。これにより、オーバーロードが欠落している場所でオーバーロードを作成するときに一貫性が生まれます。

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

関数型言語では、逆の方が便利です。デフォルトを選択する可能性が高いほど、左にあるはずです。これにより、単に引数を適用することで、関数を簡単に特化できます。

addThreeToList = map (+3)

ただし、他の回答で述べたように、おそらくシステム内のすべてのクラスの引数リストにロガーを明示的に渡したくないでしょう。


3

あなたは間違いなくこの問題を過剰に設計することに多くの時間を費やすことができます。

正規のロギングを実装した言語の場合は、すべてのクラスで正規のロガーを直接インスタンス化するだけです。

正規の実装を持たない言語の場合、ロギングファサードフレームワークを見つけて、それに固執するようにしてください。slf4jはJavaで適切な選択です。

個人的には、単一の具体的なロギング実装に固執し、すべてをsyslogに送信します。優れたログ分析ツールはすべて、複数のアプリサーバーからのsysoutログを組み合わせて包括的なレポートを作成できます。

関数シグネチャに1つまたは2つの依存関係サービスと「実際の」引数が含まれる場合、依存関係を最後に配置します。

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

私のシステムではこのようなサービスが5つ以下である傾向があるため、すべての機能シグネチャでサービスが同じ順序で含まれるように常に確認します。アルファベット順は他と同じです。(さておき:ミューテックス処理のためのこの方法論的アプローチを維持すると、デッドロックが発生する可能性が低くなります。)

アプリ全体で十数個以上の依存関係を注入していることに気付いた場合、おそらくシステムを別々のサブシステムに分割する必要があります(マイクロサービスと言ってもいいですか?)。


プロパティインジェクションを使用してcalculateFooBarSumを呼び出すのは私にとって厄介なようです。
プー

2

ロガーは、文字通りあらゆる場所で利用できる必要があるため、少し特殊なケースです。

ロガーをすべてのクラスのコンストラクターに渡すことを決定した場合は、その方法について一貫した規則を確実に設定する必要があります(たとえば、常に最初のパラメーターは常に参照渡し、コンストラクターの初期化リストは常に開始されます) m_logger(theLogger)など)。コードベース全体で使用されるものは、いつか一貫性の恩恵を受けるでしょう。

または、すべてのクラスに、渡されるものを何も必要とせずに、独自のロガーオブジェクトをインスタンス化させることができます。数百の異なるクラスに正しく渡すよりもずっと保守しやすく、面倒ではなく、グローバル変数を使用して上記の面倒を回避するよりも間違いなくはるかに害が少ない。(確かに、ロガーはグローバル変数の非常に少数の正当なユースケースの1つです)


1

ロガーはクラスに渡すのではなく、静的にアクセスする必要があることを示唆している人々に同意します。あなたがそれを通過したい強い理由がある場合は(おそらく異なるインスタンスが異なる場所か何かにログインする)その後、私はあなたが、コンストラクタを使用して、それを通過しないことをお勧めではなく、その例を行うために別の電話をかけることClass* C = new C(); C->SetLogger(logger);はなく、よりClass* C = new C(logger);

このメソッドを好む理由は、ロガーが本質的にクラスの一部ではなく、他の目的で使用される注入された機能であるためです。コンストラクタリストに配置すると、クラスの要件になり、クラスの実際の論理状態の一部であることを暗示します。もし、例えば、(すべてではないが、ほとんどのクラスで)、予想するのが妥当であるX != YそしてC(X) != C(Y)あなたが同じクラスのあまりのインスタンスを比較している場合は、ロガー不平等をテストするとは考えにくいです。


1
もちろん、これには、ロガーをコンストラクターが使用できないという欠点があります。
ベンフォークト

1
私はこの答えが本当に好きです。ロギングを使用することとは別に気にしなければならないクラスの副次的な懸念事項になります。多数のクラスのコンストラクターにロガーを追加する場合、おそらく依存性注入を使用している可能性があります。すべての言語について話すことはできませんが、C#では、一部のDI実装がプロパティ(getter / setter)に直接注入することも知っています
jpmc26

@BenVoigt:それは事実であり、これをこのようにしないことは致命的な理由かもしれませんが、通常は、ロガーが設定されたことに応じてコンストラクタで行うロギングを行うことができます。
ジャックエイドリー

0

言及する価値はありますが、ここで他の答えが触れていないのは、プロパティまたは静的を介してロガーを注入することにより、クラスのユニットテストが難しくなります。たとえば、プロパティを介してロガーを挿入する場合、ロガーを使用するメソッドをテストするたびにそのロガーを挿入する必要があります。この手段は、あなたは同様かもしれないクラスは、それを必要としないので、それはコンストラクタの依存関係として設定しています。

静的は同じ問題に役立ちます。ロガーが機能しない場合、クラス全体が失敗します(クラスがロガーを使用している場合)-ロガーは必ずしもクラスの責任の「一部」ではありませんが、プロパティインジェクションほど悪くはありませんが、少なくとも、ある意味でロガーは常に「そこ」にあることを知っています。

特にTDDを使用している場合は、考えてみてください。私の意見では、ロガーは実際にはクラスのテスト可能な部分の一部であってはなりません(クラスをテストするとき、ロギングもテストするべきではありません)。


1
hmmm ...したがって、クラスでロギングを実行する必要があります(ロギングは仕様に含まれている必要があります)が、ロガーを使用してテストする必要はありません。これは可能ですか?あなたの主張は非ゴーだと思います。テストツールが破損している場合、テストできないことは明らかです。テストツールに依存しない方法で設計することは、少し過剰に設計することです。IMHO
Hogan

1
私のポイントは、コンストラクターを使用してクラスのメソッドを呼び出しても、プロパティを設定しなかったために失敗する場合、クラスのデザイナーはコンストラクターの背後にある概念を誤解しているということです。クラスでロガーが必要な場合は、コンストラクターに挿入する必要があります-これがコンストラクターの目的です。
ダンパントリー

うーん、そうでもない。「フレームワーク」のロギングシステムの部分を検討する場合、コンストラクターの一部としては意味がありません。しかし、これは他の回答で明確に述べられています。
ホーガン

1
私はプロパティインジェクションに反対しています。コンストラクターでの注入の使用を必ずしも推奨しているわけではありません。私の意見では、プロパティインジェクションよりも望ましいと言っているだけです。
ダンパントリー

「これは可能ですか?」また、はい、IL織が存在し、中で言及されたものであるトップの答え ... mono-project.com/docs/tools+libraries/libraries/Mono.Cecil
ダン・パントリー

0

ロガーオブジェクトを各クラスインスタンスに渡すのは面倒です。したがって、私のコードでは、これらの種類のものは、静的フィールドまたは静的フィールドのスレッドローカル変数のいずれかに置かれます。後者は一種のクールで、スレッドごとに異なるロガーを使用できます。また、マルチスレッドアプリケーションで意味があり期待されることを行うメソッドを追加して、ログのオンとオフを切り替えることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.