堅牢なコードを定義するものは何ですか?


42

私の教授は、「堅牢な」コードについて話すとき、このJavaの例を参照し続けます。

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

彼は、「堅牢なコード」とは、プログラムがすべての可能性を考慮に入れており、エラーなどは存在しないことを意味すると主張しています。

しかし、私は疑っています。変数がブール値の場合、3番目の状態が論理的に不可能なときに3番目の状態をチェックするポイントは何ですか?

「エラーのようなものを持たないこと」もばかげているようです。Googleアプリケーションでさえ、エラーを静かに飲み込んだり、何らかの形でそれらを有効な状態と見なしたりするのではなく、ユーザーに直接エラーを表示します。そしてそれは良いことです-私は何かがうまくいかないときを知ることが好きです。そして、アプリケーションにエラーが発生することは決してないだろうという主張はかなりのようです。

それでは、「堅牢なコード」の実際の定義は何ですか?



4
これは、強く型付けされていない言語でのみ有効です。強く型付けされた言語でブール型(booleanとしてではない、いくつかの整数ポーズ)の変数は、唯一の真または偽することができ、何番目のオプションはありません...
マージャンVenema氏

23
堅牢なコードでは必ずテストが必要になるため、3番目のケースのカバレッジをどのようにテストしますか。3番目のケースをテストできなければ、そこに潜む可能性のあるバグを見つけることができません。
gbjbaanb

2
@Marjan-強く型付けされていない言語では、おそらく次のように書くでしょう:if(var){} else {}
kevin cline

2
xと!xの両方が真になる可能性のある言語は知りません。「if(x == true)...」を提案していないことに注意してください。私はそのような比較を嫌います。
ケビンクライン

回答:


33

3番目の状態が論理的に不可能なときに3番目の状態をチェックするポイントは何ですか?

何についてのBoolean?を可能にすることNULLもない真でも偽である状態。それでは、ソフトウェアは何をすべきでしょうか?一部のソフトウェアは、ペースメーカーのように耐衝突性が高くなければなりません。誰かがデータベースに列を追加しBoolean、現在のデータを初期化するのを見たことがありますNULLか?私はそれを見たことを知っています。

ソフトウェアの観点から堅牢であるとはどういうことかを説明するリンクがいくつかあります。

ここで「ロバスト」の定義に普遍的に同意されているものがあると思う場合は、幸運を祈ります。爆弾防止やばか防止などの同義語があります。 ダクトテーププログラマーは、少なくとも私の用語の理解において、通常堅牢なコードを書く人の例です。


13
これがnullを許可するブール値である場合、Javaとc#の両方がスローするため、最初にnullをチェックする必要があります。
エスベンスコフペダーセン

猫や犬が何であるかの定義について、普遍的に合意されているものはないようです。
Tulainsコルドバ

11

私の議論のために、BoolはTrueまたはFalseの2つの状態を持つことができます。それ以外は、プログラミング言語仕様に準拠していません。ツールチェーンが仕様に準拠していない場合は、何をしても問題はありません。開発者が3つ以上の状態を持つタイプのBoolを作成した場合、それは彼が私のコードベースで実行する最後のことです。

オプションA

if (var == true) {
    ...
} else if (var == false) {
    ...
} else {
    ...
}

オプションB

if (var == true) {
    ...
} else {
    ...
}

オプションBの方が堅牢であると断言します。....

任意のtwitは、予期しないエラーを処理するように指示できます。それらは通常、一度考えれば簡単に検出できます。あなたの教授が与えた例は、起こる可能性のあるものではないため、非常に貧弱な例です。

Aは、複雑なテストハーネスなしではテストできません。作成できない場合、どのようにテストしますか?コードをテストしていない場合、それがどのように機能するかをどのように知っていますか?動作がわからない場合は、堅牢なソフトウェアを作成していません。彼らはまだそれをCatch22と呼んでいると思います(素晴らしい映画、いつか見てください)。

オプションBは簡単にテストできます。

次の問題は、教授に「ブール値がTrueでもFalseでもない場合、それについて何をしてほしいですか?」と尋ねます。それは非常に興味深い議論につながるはずです.....

ほとんどの場合、コアダンプは適切であり、最悪の場合ユーザーに迷惑をかけたり、多額の費用がかかります。モジュールがスペースシャトルのリアルタイム再突入計算システムであるとしたらどうでしょうか?どのような不正確な答えであっても、ユーザーを殺す中絶よりも悪いことはありません。そのため、答えが間違っている可能性があることがわかっている場合は、50/50に進むか、中止して100%の失敗に進みます。私が乗組員だった場合、私は50/50を取るだろう。

オプションAは私を殺すオプションBは私に生存の機会を与えてくれます。

しかし、待ってください-それはスペースシャトルの再突入のシミュレーションです-そして何ですか?中止してください。良いアイデアのように聞こえますか?-NOT-出荷する予定のコードでテストする必要があるため。

オプションAはシミュレーションには適していますが、展開することはできません。無駄なオプションBはデプロイされたコードなので、シミュレーションはライブシステムと同じように実行されます。

これが有効な懸念事項であったとしましょう。より良い解決策は、エラー処理をアプリケーションロジックから分離することです。

if (var != true || var != false) {
    errorReport("Hell just froze over, var must be true or false")
}
......
if (var == true){
 .... 
} else {
 .... 
}

さらに読む -Therac-25 Xrayマシン、Ariane 5 Rocketの障害など(リンクには多くのリンク切れがありますが、Googleが役立つ十分な情報があります)


1
「..予期しないエラー。それらは通常、考えれば簡単に検出できます」-しかし、考えてみれば、予期しないエラーではなくなりました。
-gbjbaanb

7
あなたのコードが代わりにあるべきかどうかについていくつかの質問があります。if (var != true || var != false) {&&

1
私は、真でも偽でもないブールを簡単に考えることができますが、それはまだ予想外です。あなたがブールが他のものではないという場合、文字が数字であるかどうかを確認し、それを整数値に変換すると、その整数値が0未満または9より大きいと簡単に考えることができますが、それでも予想外。
gnasher729

1
Nullブール値はJavaとC#でサポートされており、実際のアプリケーションがあります。人のリストを含むデータベースを考えてみましょう。しばらくすると、性別(isMale)フィールドが必要になります。ヌルとは、「決して聞かないので、知らない」という意味です。trueは男性を意味し、falseは女性を意味します。(OK、トランスジェンダーは簡単にするために省略されています...)。
キウィロン

@kiwiron:より良い解決策は、列挙型「男性」、「女性」、「尋ねなかった」を使用しないことです。列挙型の方が優れています-必要に応じて拡張できます(たとえば、無性生殖、雌雄同体、「回答拒否」が思い浮かびます)。
マッテンツ

9

実際、あなたのコードはより堅牢ではありませんが、より堅牢です。最後elseは、テストできない単純なデッドコードです。

宇宙船などの重要なソフトウェアでは、デッドコードと、より一般的にはテストされていないコードは禁止されています。SEUが堅牢なコードの一部をアクティブ化すると、(予期しない)動作が制御されたままになります。


私は宇宙船でそれを取得してはいけないデッドコードは禁止されていますか?すなわち、あなたは他の最後を書くことができませんか?あなたはそれをテストできないので、あなたはそれを入れることができませんか?しかし、「SEUが堅牢なコードの一部をアクティブにした場合、(予期しない)動作は制御されたままになります」という意味です。
みすぼらしい

5
はい。宇宙で重要なソフトウェアのテスト範囲は100%でなければならず、その結果、到達不能コード(別名デッドコード)は禁止されています。
ムービシエル

7

教授は「エラー」と「バグ」を混同していると思います。堅牢なコードには、バグがほとんど/まったくないはずです。堅牢なコードは、敵対的な環境では、優れたエラー管理を備えている必要があります(例外処理または厳密なリターンステータステスト)。

教授のコード例は馬鹿げているが、私ほど馬鹿げているわけではないことに同意します。

// Assign 3 to x
var x = 3;
x = 3;   // again, just for sure
while (x < 3 or x > 3) { x = 3; }  // being robust
if (x != 3) { ... }  // this got to be an error!

1
確かに最後のトリガーが発生した場合、それほどそれほど労力は必要ありません。経験豊富なCプログラマは、値が突然変化するのを見てきました。もちろん、論理的には、制御されたシングルスレッド環境では、これは決して起こらないはずです。実際には、if内のコードは最終的に発生します。その中にできる便利なものがない場合は、コーディングしないでください!(特定のソフトウェア開発中に面白い経験がありました。そこでは、不可能なことが起こった場合に、呪いの言葉で例外を発生させました...何が起こったと思いますか?)。
アレックス

2
実話:boolean x = something(); if (x) { x = True // make sure it's really true, ... }
アンドレスF.

6

堅牢なコードの定義については合意がありません。プログラミングの多くのことは多かれ少なかれ主観的です...

教授の例は言語によって異なります。

  • Haskellでは、BooleanいずれかになりますTrueFalse、何番目のオプションはありません
  • C ++では、boolすることができtruefalseまたは(残念ながら)未知の収納ケースに入れて、いくつかの怪しげなキャストから来た...これは、する必要があり、以前のエラーの結果として、起こるかもしれないが、ありません。

ただし、教授がアドバイスしていることは、コアプログラムの途中で発生しないはずのイベントに無関係なロジックを導入することでコードを曖昧にしているため、代わりにDefensive Programmingを示します。

大学の場合、契約による設計戦略を採用することで、さらに強化することもできます。

  • クラスの不変式を確立する(たとえば、リストsize内のアイテムの数data
  • 各関数の事前条件と事後条件を確立します(たとえば、この関数はa未満の場合にのみ呼び出すことができます10
  • 各関数の入り口と出口でそれらのそれぞれをテストします

例:

class List:
  def __init__(self, items):
    self.__size = len(items)
    self.__data = items

  def __invariant(self):
    assert self.__size == len(self.__data)

  def size(self):
    self.__invariant()

    return self.__size

  def at(self, index):
    """index should be in [0,size)"""
    self.__invariant()
    assert index >= 0 and index < self.__size

    return self.__data[index]

  def pushback(self, item):
    """the subsequent list is one item longer
       the item can be retrieved by self.at(self.size()-1)"""
    self.__invariant()

    self.__data.append(item)
    self.__size += 1

    self.__invariant()
    assert self.at(self.size()-1) == item

しかし、教授はJavaであると具体的に述べ、varのタイプは具体的には述べていません。ブール値の場合、true、false、またはnullになります。それ以外の場合は、trueとfalseの両方に等しくない可能性があります。はい、堅牢で、防御的で、妄想的です。
アンディ・キャンフィールド

2
C、C ++、およびObjective-Cでは、boolは他の型と同様に不定値を持つことができますが、割り当てによってtrueまたはfalseに設定され、それ以外は何も設定されません。例:bool b = 0; b ++; b ++; bをtrueに設定します。
gnasher729

2

あなたの教授のアプローチは完全に見当違いです。

関数、またはほんの少しのコードには、それが何をするのかを示す仕様が必要であり、すべての可能な入力をカバーする必要があります。そして、コードの動作が仕様と一致することが保証されるようにコードを作成する必要があります。例では、次のように非常に単純な仕様を記述します。

Spec: If var is false then the function does "this", otherwise it does "that". 

次に、関数を作成します。

if (var == false) dothis; else dothat; 

コードが仕様を満たしています。だからあなたの教授は言う:もしvar == 42だとしたら?仕様を見てください:関数は「それ」を行うべきだと書かれています。コードを見てください:関数は「それ」を行います。関数は仕様を満たしています。

あなたの教授のコードが物事を完全に不安定にするのは、彼のアプローチでは、varがtrueでもfalseでもないとき、それは以前に呼び出されたことがないコードを実行し、完全にテストされていないコードであり、まったく予測できない結果です。


1

@ gnasher729の声明に同意します。あなたの教授のアプローチは完全に見当違いです。

堅牢性とは、想定をほとんど行わず、切り離されているため、破損/障害に強いことを意味します:自己完結型、自己定義型、ポータブルです。また、変化する要件に適応できることも含まれます。つまり、コードは永続的です。

これは通常、呼び出し元から渡されたパラメーターからデータを取得する短い関数に変換され、具体的な実装コードを含む関数ではなく、抽象メソッド、ラッパー、インダイレクション、COMスタイルインターフェースなどのコンシューマ向けのパブリックインターフェイスの使用に変換されます。


0

堅牢なコードとは、単に障害を適切に処理するコードです。これ以上でもそれ以下でもありません。

失敗には、誤ったコード、不完全なコード、予期しない値、予期しない状態、例外、リソースの枯渇など、多くの種類があります。堅牢なコードはこれらをうまく処理します。


0

あなたが与えたコードは、防御的なプログラミングの例として考えます(少なくともこの用語を使用する場合)。防御的プログラミングの一部は、システムの他の部分の動作について行われる仮定を最小限にする選択を行うことです。たとえば、次のうちどれが良いですか:

for (int i = 0; i != sequence.length(); ++i) {
    // do something with sequence[i]
}

または:

for (int i = 0; i < sequence.length(); ++i) {
    // do something with sequence[i]
}

(違いがわからない場合は、ループテストをチェックしてください:最初の使用!=、2番目の使用<)。

これで、ほとんどの状況で、2つのループはまったく同じように動作します。ただし、最初の(と比較して!=)は、i反復ごとに1回だけ増分されるという仮定を行います。値をスキップするとsequence.length()、ループがシーケンスの境界を超えて続行し、エラーが発生する可能性があります。

したがって、2番目の実装はより堅牢であるという議論を行うことができます。ループ本体が変化するかどうかに関する仮定に依存しませんi(注:実際にiは、決して負ではないという仮定を立てています)。

なぜそのような仮定をしたくないのかという動機付けを与えるために、ループが文字列をスキャンしてテキスト処理をしていると想像してください。ループを書くと、すべてがうまくいきます。ここで要件が変更され、テキスト文字列でエスケープ文字をサポートする必要があると判断したため、エスケープ文字(バックスラッシュなど)を検出した場合、エスケープのi直後の文字をスキップするように増分するようにループ本体を変更します。テキストの最後の文字がバックスラッシュである場合、ループ本体が増分iし、ループはシーケンスの終わりを超えて継続するため、最初のループにバグがあります。


-1

私は個人的に、この1つの重要な属性を備えた「堅牢な」コードを記述しています。

  1. 私の母がそれの前に座ってそれで働くなら、彼女はシステムを壊すことができません

ここで、ブレークとは、システムを不安定な状態にするか、UNHANDLED例外を引き起こすことを意味します。時には、単純な概念のために、複雑な定義と説明を作成できることを知っています。しかし、単純な定義を好むでしょう。ユーザーは堅牢なアプリケーションを見つけるのが得意です。アプリケーションのユーザーがバグ、状態の喪失、直感的でないワークフローなどについて多くのリクエストを送信した場合、プログラミングに何か問題があります。

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