どこでもデータチェックを導入するのに適したコードスタイルですか?


10

プロジェクトのサイズが十分に大きいので、頭の中ですべての側面を維持することはできません。その中でいくつかのクラスと関数を扱っており、データを渡しています。

時間の経過とともにエラーが発生し続けることに気づきました。別の関数にデータを渡すときに、データの正確な形式を忘れてしまったためです(たとえば、ある関数が文字列の配列を受け入れて出力し、別の関数は後で記述します)。辞書などに保持されている文字列を受け入れるため、操作している文字列を配列に入れてから辞書に入れるように変換する必要があります)。

どこがどこでどこが壊れているのかを常に把握する必要がないように、私は各関数とクラスを「分離されたエンティティ」として扱うようになりました。場合によっては、データが間違った形式で指定されている場合は、データを再キャストします。

これにより、渡すデータがすべての関数に「適合」することを確認するために費やす時間が大幅に短縮されました。クラスと関数自体が、入力に問題がある場合に警告を発し(場合によってはそれを修正することもあり)、デバッガーを使用してコード全体を処理し、問題が発生した場所を特定する必要があります。

一方、これによりコード全体も増加しました。
私の質問は、このコードスタイルがこの問題の解決に適切かどうかです。
もちろん、最善の解決策は、プロジェクトを完全にリファクタリングし、データがすべての機能に対して均一な構造を持っていることを確認することです。 。

(参考:私はまだ初心者なので、この質問が素朴だった場合は失礼します。私のプロジェクトはPythonで行われています。)



3
@gnat似ていますが、私の質問に答えるのではなく、OPが言及した特定のインスタンスに対してアドバイスを提供します(「できるだけ多くの防御力がある」)。これは、より一般的なクエリとは異なります。
user7088941

2
「しかし、このプロジェクトは絶えず成長しているので、実際に新しいものを追加するよりも、クリーンなコードについて多くのことを費やして心配することになります」-これは、クリーンなコードについて心配し始める必要があるように聞こえます。そうしないと、既存のコードのために機能の新しいビットを追加することがますます難しくなるため、生産性がどんどん遅くなっていきます。何か新しいものを追加するために感動コードとは、あなたが後で再訪したいものをメモしておくだけで、それが触れた既存のコード、リファクタリングのハードでない場合は、すべてのリファクタリングのニーズは、「完全」であることを
マットはfreake

3
これは、弱く型付けされた言語を使用する場合によく直面する問題です。より厳密に型指定された言語を望まないか、または切り替えることができる場合、答えは単に「はい、このコードスタイルはこの問題の解決に適しています」です。次の問題?
Doc Brown

1
厳密に型指定された言語では、適切なデータ型が定義されていれば、コンパイラーがこれを行います。
SD

回答:


4

より良い解決策は、Python言語の機能とツールをより活用することです。

たとえば、関数1では、予期される入力は文字列の配列です。最初の文字列は何かのタイトルを示し、2番目の文字列は書誌参照です。関数2では、期待される入力は文字列の配列ですが、文字列の役割が逆になっています。

この問題はで軽減されますnamedtuple。それは軽量であり、配列のメンバーに簡単な意味上の意味を与えます。

言語を切り替えずに自動型チェックの利点を活用するには、型ヒントを利用できます。優れたIDEでは、これを使用して、何かばかげたことを通知できます。

また、要件が変更されたときに関数が古くなるのではないかと心配しているようです。これは自動テストで検出できます。

手動でのチェックが適切であるとは決して言えませんが、使用可能な言語機能をより適切に使用することで、この問題をより保守可能な方法で解決できます。


+1と私にnamedtuple他のすべての良いものを指し示すため。私は約ないなかったnamedtuple-と私は自動テストを知っていながら、私は本当に多く、それを使ったことがないし、それがこのケースで私を助けてどのくらい実現しませんでした。これらはすべて、静的分析と同じくらい優れているようです。(静的分析では捉えられないような微妙なことをすべて捉えることができるので、自動テストの方が優れているかもしれません!)他にご存知の場合は、お知らせください。しばらく質問を開いたままにしますが、他に回答がなければ、あなたの質問を受け入れます。
user7088941

9

OK、実際の問題はこの回答の下のコメントに記載されています:

たとえば、関数1では、予期される入力は文字列の配列です。最初の文字列は何かのタイトルを示し、2番目の文字列は書誌参照です。関数2では、期待される入力は文字列の配列のままですが、文字列の役割が逆になります

ここでの問題は、順序が意味を表す文字列のリストの使用です。これは本当にエラーが発生しやすいアプローチです。代わりにtitle、およびという名前の2つのフィールドを持つカスタムクラスを作成する必要がありますbibliographical_reference。そうすれば、それらを混同することはなく、将来この問題を回避できます。もちろん、すでに多くの場所で文字列のリストを使用している場合、これにはいくつかのリファクタリングが必要ですが、私を信じて、長期的には安くなるでしょう。

動的型付け言語での一般的なアプローチは「ダックタイピング」です。つまり、渡されるオブジェクトの「型」は本当に気にせず、呼び出すメソッドをサポートするかどうかだけを気にします。あなたのケースでは、bibliographical_reference必要なときに呼び出されたフィールドを読み取るだけです。渡されたオブジェクトにこのフィールドが存在しない場合、エラーが発生し、関数に誤ったタイプが渡されたことを示します。これはどんなタイプチェックでも良いです。


時々問題はさらに微妙です:私は正しいタイプを渡していますが、入力の「内部構造」が関数を台無しにしています:たとえば、関数1では、期待される入力は文字列の配列で、最初の文字列は何かのタイトルを示し、2番目は書誌参照です。関数2では、予想される入力は文字列の配列ですが、文字列の役割が逆になっています。最初の文字列は書誌参照で、2番目の文字列は書誌参照です。このチェックは適切だと思いますか?
user7088941

1
@ user7088941:あなたが説明する問題は、「title」と「bibliographical_reference」という2つのフィールドを持つクラスを作成することで簡単に解決できます。あなたはそれを混同しないでしょう。文字列のリストの順序に依存すると、エラーが発生しやすくなります。多分これは根本的な問題ですか?
JacquesB

3
これが答えです。Pythonはオブジェクト指向言語であり、文字列の辞書のリストから整数指向の(または何でも)言語のリストではありません。したがって、オブジェクトを使用します。オブジェクトは自分の状態を管理し、自分の不変条件を適用する責任があります。他のオブジェクトオブジェクト破壊することはありません(正しく設計されている場合)。非構造化データまたは半構造化データが外部からシステムに入力された場合、システム境界で一度検証および解析し、できるだけ早くリッチオブジェクトに変換します。
イェルクWミッターク

3
「私は絶えずリファクタリングを避けたい」-この精神的なブロックはあなたの問題です。良いコードはリファクタリングからのみ生じます。たくさんのリファクタリング。単体テストでサポートされています。特に、コンポーネントを拡張または進化させる必要がある場合。
Doc Brown、

2
分かりました。すべてのすばらしい洞察とコメントの+1。そして、彼らの信じられないほど役立つコメントをありがとう!(私がいくつかのクラス/オブジェクトを使用していたとき、私が言及したリストをそれらに散在させましたが、今見ると、それは良い考えではありませんでした。質問は、これを実装する最良の方法であり、JETMの回答からの具体的な提案を使用しました、バグのない状態を達成する速度の点で本当に大きな違いを
もたらしました

3

まず第一に、あなたが現在経験しているのはコードのにおいです - コードのにおいに気づくとすぐに-そして簡単に- においを意識するようになる原因を思い出し、「精神的な」鼻を磨くようにしてください根本的な問題を修正できます。

どこがどこでどこが壊れているのかを常に把握する必要がないように、各関数とクラスを「分離されたエンティティ」として扱い始めました。これは、正しい入力を与える外部コードに依存できず、入力チェック自体を実行する必要があるという意味です。

防御プログラミングは、この手法と呼ばれるもので、有効でよく使用されるツールです。ただし、すべてのものと同様に、適切な量を使用することが重要です。チェックが少なすぎると、問題がキャッチされず、多すぎてコードが肥大化します。

(または、データが誤った形式で指定されている場合は、データを再キャストします)。

それはあまり良い考えではないかもしれません。プログラムの一部が誤ってフォーマットされたデータを使用して関数を呼び出していることに気付いた場合は、FIX THAT PART、呼び出された関数を変更して、不良データをダイジェストできるようにします。

これにより、渡すデータがすべての関数に「適合」することを確認するために費やす時間が大幅に短縮されました。クラスと関数自体が、入力に問題がある場合に警告を発し(場合によってはそれを修正することもあり)、デバッガーを使用してコード全体を処理し、問題が発生した場所を特定する必要があります。

コードの品質と保守性を改善することは、長期的には時間の節約になります(その意味で、一部の関数に組み込まれた自己修正機能に対して再度警告する必要があります。これらはバグの潜伏する原因となる可能性があります。プログラムはクラッシュせず、書き込みも正しく機能するわけではありません...)

最後にあなたの質問に答えるには:はい、防御的プログラミング(つまり、提供されたパラメーターの有効性の検証)は-健全な程度で-良い戦略です。は言っても、あなたが言ったように、あなたのコードは一貫性がなく、匂いのある部分をリファクタリングするためにある程度の時間を費やすことを強くお勧めします-あなたはいつもクリーンなコードについて心配したくないので、より多くの時間を費やしていると言いました新機能よりも「掃除」...あなたはバグを潰しにクリーンなコードを保っていないから、「保存」2倍の時間を費やす可能性があり、新しい機能を実装するハードの時間を持つことになりますきれいに、あなたのコードを維持していない場合は- 技術的負債の缶あなたをつぶします。


1

大丈夫。私はFoxProでコードを作成していましたが、ほとんどすべての大きな関数にTRY..CATCHブロックがありました。今、私はJavaScript / LiveScriptでコーディングし、「内部」または「プライベート」関数のパラメーターをほとんどチェックしません。

「どれだけチェックするか」は、コードのスキルに依存するよりも、選択したプロジェクト/言語に依存します。


1
TRY ... CATCH ... IGNOREだったと思います。あなたは、OPが求めていることとは逆のことをしました。私たちの意見は、あなたがあなたのプログラムがヒットしたときにプログラムが爆破しないことを保証している間、矛盾を回避することです。
maaartinus

1
@maaartinus正解です。プログラミング言語は通常、爆破の適用を防ぐために簡単に使用できる構造体を提供しますが、矛盾を防止するために使用する構造体プログラミング言語は、使用するのがはるかに難しいように見えます:私の知る限り、常にすべてをリファクタリングし、コンテナ化に最適なクラスを使用アプリケーション内の情報フロー。これはまさに私が求めていることです-これを修正する簡単な方法はありますか?
user7088941

@ user7088941弱く型付けされた言語を避ける理由です。Pythonは素晴らしいだけですが、もっと大きなものについては、他の場所で行ったことを追跡できません。したがって、私はJava(LombokとJava 8の機能はそれほど多くない)を好んでおり、静的分析のための厳密な型指定とツールを備えています。私はそれを解決する方法がわからないので、いくつかの厳密に型の言語を試すことをお勧めします。
maaartinus

それは厳密な/緩やかな型付きパラメーターについてではありません。パラメータが正しいことを知ることです。(整数4バイト)を使用する場合でも、たとえば0..10の範囲にあるかどうかを確認する必要がある場合があります。パラメータが常に0..10であることがわかっている場合は、チェックする必要はありません。FoxProは例えば連想配列を持っていない、それはあなたがチェックチェックをチェックするために持っている理由...だと...その変数、その範囲とし、その上で動作するのは非常に難しい
マイケル・クワッド

1
@ user7088941 OOではありませんが、「フェイルファスト」ルールがあります。すべての非プライベートメソッドは、その引数をチェックして、何か問題がある場合にスローする必要があります。トライキャッチも修正もせず、空高く吹き飛ばしてください。もちろん、より高いレベルでは、例外がログに記録されて処理されます。テストではほとんどの問題が事前に検出され、問題が隠されることはないため、コードはエラーに強い場合よりもはるかに速くバグのないソリューションに収束します。
maaartinus
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.