私は仕事中の茶碗で少し嵐があります、そして私は私が正しいか、間違っているか、あるいは両方の少しかもしれないかどうかを理解しようとしています。
それはすべて無邪気に始まりました。別のチームの開発者が、私のチームのコードのコードレビュー中にコメントしていました。私たちは[比較的]新しいチームであり、他のチームとは少し異なるコードを書く傾向があります。クリーンコード、SOLIDの原則、TDDなどに従おうとします。残りのコードベースは詳細...クラシック...固定レイヤー、巨大なインターフェース/神オブジェクト、アネミックドメインモデルなどを使用したエンタープライズ設計と実装。
コードベース自体は、多くのビジネスクリティカルなトランザクションを処理するフラッグシップアプリケーション用です
歴史的にそれは急いで建てられました、そしてここ数ヶ月だけで私達は以前のデザインの選択の多くを改善しようとする時間がありました。私はそれが行われた方法のファンではありませんが、私がしなければならないところでトレードオフをするつもりです。
とにかくコードはこのように見えました
public class Something
{
readonly IFoo _foo;
public Something(IFoo foo)
{
_foo = foo.ThrowIfNull("foo");
}
}
(ThrowIfNull
は、通常の「if x == null throw new ArgumentNullException」をラップする単純な汎用拡張メソッドです)。私は個人的にはファンではありませんが、他の人はそれが好きなのでクールです。
さて、ここに提案されたものがあります:
public class Something
{
readonly IFoo _foo;
public Something(IFoo foo)
{
#if debug
_foo = foo.ThrowIfNull("foo");
#else
_foo=foo;
}
}
理論的根拠は、これにより本番コードのオーバーヘッドをいくらか節約できるということでした。これは実際には心配する必要はなく、クラスよりも重要であると指摘しました(たとえば、はるかに重要です)。何らかの理由で誰かまたは何かがnull
を構築しようとした場合、迅速に失敗する必要があるため、不変条件(この場合はその依存関係)は保護されます(つまり、_foo
をSomething
にすることはできません)。 nullのIFoo
。
しかし、その後、シニアアーキテクトがチャイムを鳴らして、これは私たちが心配すべきではないことだと主張しました。これらは多かれ少なかれ議論であり、私の反論もあります(もちろんすべて言い換えられます)。私も建築家ですが、新しく造られたので、まだそれほど影響力はありません:)
IOCライブラリと緊密に結合されており、依存関係がnullにならないように処理します。したがって、このコードはIOCライブラリを間接的にテストしているだけなので、削除する必要があります =これは、依存関係のあるすべてのクラスがIOCによって管理されていることを前提としています。実際に使用されている場合でも、それは重要ではないと思います。自分自身を結合するべきではありません。
後で_fooを使おうとするコードはとにかく失敗します私にとって、これは飛行機の飛行前チェックをスキップすることを正当化するようなものです。「ねえ、翼が落ちたら飛行中、それなら知ってますよね?」
(この最後のものは私を非常に怖がらせたものでした)
だから問題はこれです。
私は間違っていますか、正しいですか、それとも両方の少しですか?私の反論に挙げた理由から、不変条件を保護することは基本的なことであり、あなたがしなければならないことだといつも教えられていました。私はこれをやめるべきです(政治やキャリアの観点から尋ねるのではなく、他の人が私と同じように感じたとしても、それは本当に重要であり、戦う価値があります)
あなたは間違いなくここにいます。これはかなり基本的な防御プログラミングであり、それに反対する本当の議論はありません。
さて、議論のために:
IoCとフェイルファストの問題への結合は、あなたが言ったとおりです。
3番目の引数については、この1つのクラスに引数nullチェックがあるからといって、戻って他のすべてのクラスを変更する必要があるわけではありません。これは、このコードが他のコードよりも優れていることを意味します。
そして、「生産のオーバーヘッド」に関しては、それはばかげています。そのコンストラクターは数回呼び出されます。地獄、それがアプリケーションの存続期間中に何百万回も呼び出されたとしても、それでもコンパイラ条件付きでコードを複雑にすることを正当化することはできません。そして、彼らが本当にそのようにしたいのであれば、より良い方法はThrowIfNull
メソッドの2つのバージョンを作成することです。 1つはデバッグモードでスローし、もう1つはリリースモードで空になります。次に、コンパイラはそれを最適化します。