両刃の剣、またはもう一度コードの脆弱性について

このトピックは、最近の記事「コードの脆弱な美しさを破壊する10のトリック」への回答として書いており、多くの論争が巻き起こりました。



コメントにはすでに多くのことが書かれていますが、多くは書かれていません。 ここで実際の例を示したいと思います。著者が間違っている多くの状況があり、彼が提案した解決策が干渉する場合があります。



著者は多くの点で正しいが、彼の信念はあまりにもカテゴリー的である。 プログラマーの職業は、最適な方法で問題の解決策を考え、説明することです。 他の技術と同様に、プログラマーがすべての問題を「テンプレートのみを代用する」ことで解決する場合、彼の決定はすべてナンセンスになります。 プログラマーは考える人であり、プログラミング言語は決定アルゴリズムを実装するための非常に柔軟なインターフェースを提供します。 したがって、言語の機能をあきらめないでください-テンプレートはそれを使用しないように言っているので-言語開発者はすでにこれらのメソッドを残すために100回すべてを熟考しており、ドキュメントで最も適切な使用方法を見つけることができると確信しています。



著者が言ったことは良いアドバイスです。 しかし、彼はコインの片側だけを見せました。 もう1つお見せします。



プログラムの開始時にすべての変数を宣言する



次の文は不合理だと思います。

関数の先頭ですべての変数を宣言することは、ひどい悪です。


はい、スティーブは正しく記述します。理想的なケースでは、最初の呼び出しの直前に各変数をすぐに宣言して定義します 。 しかし、これは真実ではありません-これは理想的なケースです。 また、まったく理想的ではないケースなど、さまざまなケースがあります。

この場合、コードの先頭でいくつかの変数を定義することをお勧めします-何が起こっているのかをよりよく理解するためです。



次の例では、関数が配列()を返すことは明らかです。

  1. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  2. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  3. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  4. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  5. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  6. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  7. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  8. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  9. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  10. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  11. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  12. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  13. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  14. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  15. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



  16. function sql2array ( $query , $field = false ) { $Result = array ( ) ; if ( DEBUG ) print ( "Query: $query \n " ) ; $queryResource = $this -> query ( $query ) ; if ( ! $queryResource ) return false ; while ( $Data = $this -> fetch_array ( $queryResource ) ) { if ( ! $field ) $Result [ ] = $Data ; else $Result [ $Data [ $field ] ] = $Data ; } $this -> free_result ( $queryResource ) ; return $Result ; }



チェック後に$ Result!$ QueryResourceを配置した場合、私の目では、この変数の型を探してコードをすばやく読み直す必要があります。

このため、Cでは、すべての変数が最初に定義されます。 これらはコードの目次(ブック)として読み、コードで何が起こるかを以前に理解していました。 しかし、これも万能薬ではなく、権力の反対側にすぎません。 :)



計算、ループでのみ使用されるが、たとえば返されない変数は、計算の前に宣言することができます(理想的な場合は、必要でさえあります)。 (たとえば、$ queryResourceを使用して行われます)。
想像してみてください:300行のコードの機能があります[2]。 200行目のどこかで、2つの変数を交換する必要があります。 これを行うには、関数の開始点まで200ランオフ高く登り、関数全体とは関係のない変数tempを宣言し、1つの場所で1回だけ使用してから、再度200行目に戻り、場所で変数を変更します...それはただの悪夢です。
これは怖くない。 コードを書くと、コードで何が起こっているかがわかります。 他の人がコードを読んで、必要な変数を300行で見つけようとすると、他の人は怖くなります。



したがって、優れたプログラマーはまず、各メソッドの最も単純なリファクタリング(特に300行を使用する場合)を行うことで冗長コードを取り除き、明確な名前を持つより軽いメソッドと関数に分割します。

この場合、すべてが簡単になり、「変数を宣言する必要がある場所」で不必要なトラブルが発生することはありません。 理想的なケースでは、それらは見える場所で宣言されます-最も可能性が高いのは開始時で、ブロックでほとんどすぐに(アトミックメソッドで)使用されます。



だから、メソッド/関数を何とどのように書くかを考えてください。 そして、より頻繁にリファクタリングを行います。 :)



パラメータを介して関数の結果を返します



繰り返しますが、誤解がここに表示されます。 著者は、必要がないときはreturnを使用する価値があると正しく述べています。 しかし、時々必要が生じます!



異なるタイプのいくつかの値を返す関数の実装:

  1. function sql2array $ query $ field = false $ error = NO_ERROR {
  2. $ Result = array ;
  3. if DEBUG
  4. print "クエリ: $クエリ \ n " ;
  5. $ queryResource = $ this- > query $ query ;
  6. if $ queryResource {
  7. $ error = QUERY_ERROR ;
  8. falseを 返し ます
  9. }
  10. while $ Data = $ this- > fetch_array $ queryResource {
  11. if $フィールド
  12. $結果 [ ] = $データ ;
  13. elseif isset $ Data [ $ field ]
  14. $ Result [ $ Data [ $ field ] ] = $ Data ;
  15. その他 {
  16. $ error = FIELD_ERROR ;
  17. falseを 返し ます
  18. }
  19. }
  20. $ this- > free_result $ queryResource ;
  21. return $ Result ;
  22. }
ご覧のとおり、関数は2つのパラメーターを返します-これは$エラーです-正しく実行された場合、関数と配列のステータスです。



しかし、そのような場合にそうすることも万能薬ではありません。 これはクラスメソッドであるため、SetError(errorType)およびerrorType GetLastError()メソッドをこのクラスに追加するのが最も適切です。 そして、それらをすべての方法で使用します。 実用的で便利で実装しやすい:
  1. クラス A {
  2. プライベート $エラー ;
  3. パブリック 関数 getLastError {
  4. $ this- > errorを 返し ます
  5. }
  6. プライベート 関数 setError $ error {
  7. $ this- > error = $ error ;
  8. }
  9. パブリック 関数 sql2array $ query $ field = false {
  10. $ Result = array ;
  11. if DEBUG
  12. print "クエリ: $クエリ \ n " ;
  13. $ queryResource = $ this- > query $ query ;
  14. if $ queryResource {
  15. $ this- > setError QUERY_ERROR ;
  16. falseを 返し ます
  17. }
  18. while $ Data = $ this- > fetch_array $ queryResource {
  19. if $フィールド
  20. $結果 [ ] = $データ ;
  21. elseif isset $ Data [ $ field ]
  22. $ Result [ $ Data [ $ field ] ] = $ Data ;
  23. その他 {
  24. $ this- > setError QUERY_ERROR ;
  25. falseを 返し ます
  26. }
  27. }
  28. $ this- > free_result $ queryResource ;
  29. return $ Result ;
  30. }
  31. }
出来上がり。

確かに、今ではこのコードはマルチスレッドプログラムで使用できません(過去は可能でした)が、ここではすべてタスクに依存しています。 さらに、 コメンテーターは私を修正します 、ここでエラー処理に使用できるもの-例外、私はそれらに完全に同意します。



ここでは、object.getProperty()およびobject.setProperty(value)を介してオブジェクトのプロパティへのアクセスを使用する必要がある例にスムーズに進みます



オブジェクトプロパティへのアクセス



値をチェックしてメソッドを改良することが明らかに必要な場合を調べてみましょう。

  1. クラス A {
  2. 保護された$ length ;
  3. パブリック 関数 getLength {
  4. $ this- > lengthを 返し ます。
  5. }
  6. }
  7. クラス B Aを拡張します {
  8. パブリック 関数 getLength {
  9. if $ this- > testLength
  10. return $ this- > length + 1 * 100 ;
  11. 親を 返す :: getLength ;
  12. }
  13. }
完全にクラスを持っていません。 しかし、変数を変更するためにどの方法を使用するかを考える価値がある理由はすでに明らかになっています。 そのすべてのために、クラスがマルチスレッドアプリケーションで、またはデータベースを操作するときなど、クラスでそれをどのように使用できるかを検討する価値があります。



それはすべてアーキテクチャ(およびプログラミング言語)に依存します。 プロパティが存在する場合はプロパティを使用し、プロパティがない場合はメソッドを使用することができます(そして必要です)。 :)

場合によっては、public(構造体の値を保存/読み取りする必要がある場合に不要なオーバーロードなし)を使用することが正当化される場合もあります。



名前付き関数のパラメーターがありません



ここで、キャンプはいくつかのグループに分けられます。 OOP愛好家と関数型プログラミング愛好家。



名前付きパラメーターがどこでも機能しないことを示します。

サポートのない言語では、すぐに問題が発生します。

1)すべての着信パラメーターの手動検証の必要性。

2)これらのパラメーターの名前と量を知る必要がある。 (通話中)

そしてもう一つ。

いくつかの機能があると想像してください。 DrawRectangle、DrawCircle、DrawConus、DrawLine、DrawPoly ...リストは延々と続きます。



著者は、各オブジェクトの構造を宣言して使用する(たとえばC)ことを提案します。 つまり、機能ごとに独自の構造が必要であることがわかります-これはせん妄に同意します。 同時に、構造には座標、色、透明度などが含まれます。



手続き型プログラミングでは、良い意味で、それらは予備初期化の「アトミック」関数に分割されます。 たとえば、OpenGLで実装されています。 しかし、著者の例を挙げます。
  1. BackgroundColor 1、1、255 ;
  2. BorderColor 255、1、1 ;
  3. BorderWidth 2 ;
  4. DrawRotation 30 ;
  5. AlphaColor 20 ;
  6. DrawRectangle 80、25、50、75 ;


コードは明白で明確です。 同時に、追加のトリックなしで、パラメータを任意に多くすることができます。 そして、関数は受け入れるべきパラメーターを受け入れます。



そして今、著者のアプローチのマイナス面について。 彼が言うように(名前付きパラメーターをサポートしていない言語で)しないでください:
  1. void DrawRectangle Rectangle rect {
  2. 行行;
  3. float x1 y1 ;
  4. float x2 y2 ;
  5. x1 =長方形。 x 長方形です。 / 2 ;
  6. y1 =長方形。 y 長方形です。 高さ / 2 ;
  7. x2 =長方形。 x + rect。 / 2 ;
  8. y2 =長方形。 y + rect。 高さ / 2 ;
  9. 行。 =長方形。 borderColor ;
  10. 行。 weigth = rect。 borderWeigth ;
  11. 行。 アルファ =長方形。 アルファ ;
  12. //回転などを想像して、忘れてしまった...
  13. 行。 x1 = x1 ;
  14. 行。 y1 = y1 ;
  15. 行。 x2 = x2 ;
  16. 行。 y2 = y1 ;
  17. DrawLine ;
  18. 行。 x1 = x2 ;
  19. 行。 y1 = y1 ;
  20. 行。 x2 = x2 ;
  21. 行。 y2 = y2 ;
  22. DrawLine ;
  23. 行。 x1 = x2 ;
  24. 行。 y1 = y2 ;
  25. 行。 x2 = x1 ;
  26. 行。 y2 = y2 ;
  27. DrawLine ;
  28. 行。 x1 = x1 ;
  29. 行。 y1 = y2 ;
  30. 行。 x2 = x1 ;
  31. 行。 y2 = y1 ;
  32. DrawLine ;
  33. }
実際には、コマンドパターンを使用すると同じことが起こります。

したがって、すべての方法が適切であるとは限りません。 そして、著者が説明したのは万能薬ではありません。



すべてのパラメーターを1つの関数に渡すことは必ずしも有用ではありません。

手続き型プログラミングを使用すると、完成したDrawLineの結果(x1、y1、x2、y2)を単純に転送できます。 このようになります。 (追加の初期化なしでColor、Alpha、LineWeightなど)



データの複製について



za4toからの最初のコメントを参照してください)システム最適化の段階を既に説明していることだけを追加します。

最適化するとき-まず第一に、すべてのツールが優れている最適化ルールがすでに機能しています:)(非正規化もあります)。 それだけでなく、最適化であっても、コードが読みやすいようにする必要があります。



結論の代わりに



プログラマーの芸術は、タスクが解決されるだけでなく、特定のスキルが磨かれる複雑な芸術です。 これらのスキルは役立つ場合がありますが、あまり気にしないでください。 場合によっては、他の実装方法が問題を解決するためにより適切で、高品質で、より視覚的になります。



力の両側を使用します。 :)

成功。 そして、補完的なコメントを期待しています。



All Articles