私のコードはどの程度「防御」すべきでしょうか?
-
22-08-2019 - |
質問
私は同僚の 1 人と、コードをどの程度防御する必要があるかについて議論していました。私は防御的なプログラミングのプロですが、どこでやめるべきかを知っておく必要があります。私たちは他の人によって保守されるプロジェクトに取り組んでいますが、これは開発者が行う可能性のあるすべてのクレイジーなことをチェックする必要があるという意味ではありません。もちろん、そうすることもできますが、コードに非常に大きなオーバーヘッドが追加されます。
どこに線を引くべきかどうやってわかりますか?
解決
私は本当にこれを答えるためにどのような方法があることを知りません。それはあなたが経験から学ぶだけで何かです。あなただけの潜在的な問題があると思われる方法を共通に自分自身を尋ねると、審判の判定をする必要があります。また、あなたは必ずしものHAVE の常に守備コーディングしないことを検討してください。時にはそれだけであなたのコードのドキュメントの任意の潜在的な問題に注意することは許容できるのです。
最終的に、しかし、私はこれがちょうど人は上の自分の直感に従うことを持っているものだと思います。それを行うには正しいか間違っている方法はありません。
他のヒント
何ものユーザーの直接的または間接的に、あなたがすべきの常にの健全性チェックが入ります。それを越えて、ここで、いくつかのassert
s、そこに傷つけることはありませんが、あなたが本当にとにかく、あなたのコードを編集し、破壊狂気のプログラマについて多くを行うことはできません -
私は、言語に基づいて、私は自分のコードに入れ防衛の量を変更する傾向があります。私の考えは、その方向に漂流しているので、今日は主にC ++で働いています。
Cで作業する場合、++十分な防御的プログラミングがあることはできません。私は核の秘密を守ることだし、他のすべてのプログラマがそれらを得るために出ているかのように私は私のコードを扱います。 、アサートスロー、深コードレビューや一般パラノイアでコンパイル時エラーテンプレートハック、引数の検証、ポインタを排除するには、すべての公正なゲームです。 C ++は、邪悪な素晴らしい言語Iの両方の愛と深刻な不信感です。
私は、用語「防御的プログラミング」のファンではありません。私にはそれがこのようなコードを提案します:
void MakePayment( Account * a, const Payment * p ) {
if ( a == 0 || p == 0 ) {
return;
}
// payment logic here
}
これは、間違って間違って、間違っているが、私はそれを何百回も見ている必要があります。この関数は最初の場所でヌルポインタで呼び出されていないん、静かにそれらを受け入れることは全く間違っています。
ここで正しいアプローチは議論の余地があるが、最小の溶液がアサートを使用するか、例外をスローすることによってのいずれかで、騒々しく失敗することである。
の編集の私はここにいくつかの他の回答やコメントに同意しない - 私はすべての機能がそれらのパラメータをチェックしなければならないことはないと思う(多くの機能のために、これは単に不可能です)。代わりに、私はすべての機能が他の値は未定義の動作につながることを許容された値と状態を文書化すべきであると考えています。これは、これまでに書かれた最も成功し、広く使用ライブラリで撮影したアプローチである - 。CおよびC ++標準ライブラリ
そして今、
... downvotes始めましょうあなたがコンポーネントのパブリックAPIに取り組んでいる場合、その価値は、パラメータの検証の良い量を行います。これは、どこにでも検証を行うための習慣を持つように私を導きました。間違いのthats。すべてのことの検証コードは、テストされていない、潜在的にそれが必要以上にシステムが複雑になります決してます。
今、私はユニットテストによって検証することを好みます。検証は間違いなく、外部ソースからのデータのためではなく、非外部の開発者からの呼び出しのために起こります。
私はいつも私の仮定をDebug.Assertのます。
私の個人的なイデオロギー:プログラムの防衛は、潜在的なユーザーベースの最大の認識の甘/無知に比例する必要があります。
API コードを使用する開発者に対して防御することは、通常のユーザーに対して防御することとそれほど変わりません。
- パラメータをチェックして、パラメータが適切な範囲内にあり、予期されるタイプであることを確認します。
- 実行できる API 呼び出しの数が利用規約の範囲内であることを確認してください。一般にスロットルと呼ばれ、通常は Web サービスとパスワード チェック機能にのみ適用されます。
それ以外にやるべきことは、問題が発生した場合にアプリが適切に回復することを確認し、何が起こっているのかを理解できるように常に開発者に十分な情報を提供すること以外にやるべきことはありません。
防御的なプログラミングは、契約を守るための 1 つの方法にすぎません。 契約による設計 コーディングの方法。
残りの2つは、
- トータルプログラミング そして
- 名目上のプログラミング。
もちろん自分を守るべきではありません あらゆるクレイジーなこと 開発者が行うこともできますが、その場合は、前提条件を使用して、どのようなコンテキストで期待されることを実行するかを記述する必要があります。
//precondition : par is so and so and so
function doSth(par)
{
debug.assert(par is so and so and so )
//dostuf with par
return result
}
テストを作成しているかどうかという問題も持ち込む必要があると思います。コーディングでは防御的であるべきですが、JaredPar が指摘したように、それは使用している言語に依存するとも思います。アンマネージ コードの場合は、非常に防御的になる必要があります。それが管理されていれば、少しは余裕があると思います。
テストがあり、他の開発者がコードを間引きしようとすると、テストは失敗します。. 。ただし、繰り返しになりますが、コードのテスト カバレッジ (存在する場合) に依存します。
私は守備のより多くが、右の敵対ダウンしているコードを書いてみます。何かがうまくいかないと、私はそれを修正することができる場合、私はなります。ない場合は、スローまたは例外を渡すと、それよその問題を作ります。ファイル・システム、データベース接続、ネットワーク接続不良にunereliableとやすい考慮されるべきである - 物理デバイスと対話何か。これらの障害を予測し、それらをトラップすることは非常に重要です。
あなたはこの考え方を持っていたら、、キーはあなたのアプローチで一貫しています。あなたは、コールチェーンの問題をcomminicateたり、例外のように行うには、ステータスコードを手の甲ことを期待します。混合モデルは、あなたを殺すか、少なくとも、あなたが飲むために駆動します。重く。あなたはよそのAPIを使用している場合は、あなたが使う用語でトラップ/レポートメカニズムにこれらの事を隔離します。これらラッピングインターフェイスを使用します。
、あなたは何ができるかには限界があります。あなたの仮定を主張するのテスト範囲とリベラル使用して、契約を強制することは、おそらくあなたができる最善であり、それは、理想的には、コードを乱雑にしないと、将来の非悪のメンテナのために難しい仕事を作る方法で行われるべきコード。アサートは読んで理解し、コードの特定の部分の仮定が何であるか、それを明確にするのは簡単ですので、彼らは通常、素晴らしいアイデアだ。
ユーザーのアクションに対して防御的コーディング全く別の問題である、と私は使ったアプローチは、ユーザーが私を取得するために出ていることを考えることです。すべての入力がよう慎重に私が管理できるよう検討し、私は私のコードを持っているためにあらゆる努力をフェールセーフ作るれる - 場合など、正常に終了できない場合、どこのことができます正しい、厳密に吟味されていない任意の状態を保持しないようにしようあなたはそれが正しい考え方であなたを取得し、外部のエージェントがあなたのコードに犯さことができ、すべてのボゾ物事を考えています。
など、ご使用のプラットフォームや他のモジュールとして、他のコードに対して防御的にコーディング、ユーザーとまったく同じである:彼らはあなたを取得するために外出。 OSは常に、都合の悪い時に、あなたのスレッドをスワップアウトしようとしているネットワークは常に間違っている時に離れて行くつもりされており、一般的に、悪が隅々の周りにあふれて。メンテナンスでコストが安全性の増加価値はないかもしれない - - あなたはそこにすべての潜在的な問題に対してコーディングする必要はありませんが、それは確かにそれについて考えることを傷つけることはありません。そして、それは通常、あなたが考えたシナリオが、何らかの理由のために重要でないとして点があるかどう明示的にコード内のコメントを傷つけることはありません。
システムは、守備のチェックが起こるうまく設計された境界を持っている必要があります。プログラマの異なるチームで符号化されたユーザー入力が検証された場所についての決定例えば、その他の潜在的な守備の問題はチェックが必要な場所((何の境界で)、サードパーティの統合ポイント、公に利用可能なAPIは、ルールエンジンの相互作用、または異なる単位をがあるはずです)。それよりももっと守備のチェックは、多くの場合にDRYに違反して、ちょうどは非常に少ないbenifitのメンテナンスコストを追加します。
言われていること、あなたも被害妄想することはできません特定のポイントがあります。バッファオーバーフロー、データの破損と同様の問題の可能性が非常に厳しくに対して擁護しなければなりません。
私は最近、ようやくそれが実際に使用されたメソッドを取得するためのユーザ入力データはリモートファサード・インターフェースを介して伝播されたシナリオ、そして地元のファサード・インターフェース、そして他のいくつかのクラスを、持っていました。検証値である必要があります場合は、のの私は値のみが実際に使用された最後のクラスに検証コードを追加しました:私は私の自己質問をしてたのか。伝搬路に敷設クラスで他の検証コードスニペットを追加すると、私にとってはあまりにも防御的プログラミングだろう。唯一の例外は、リモート・ファサードかもしれないが、私もそれをスキップします。
良い質問、私はフリップは、健全性チェックをやって、それをやっていない間フロップてきました。その50/50
の状況、私はおそらくミドルアースを取ると思いますどこだろう唯一の「防弾」です任意のルーチンます:
(a)のプロジェクトで複数の場所から呼び出されます。
(b)は、変化しやすいロジックを有する
(C)あなたは、デフォルト値を使用することはできません。
(d)のルーチンは、
優雅に '失敗' することはできませんDarknight