# クリーンコード 第7章~第9章 ## 第7章 エラー処理 ### リターンコードではなく例外を使用する 昔は例外を持たない言語が多く、エラーフラグ又はエラーコードを返すしかなかった。 現在は例外で対処できる。例外の長所は、本来の処理とエラー処理の2つの関心ごとが分離されている点である。 ### try-catch-finally文を利用する try-catch-finally文を利用すると、トランザクションとしての性質を備えた例外処理ができる。 #### 手順 1. try文とcatch文を作成し、必ずエラーが起こるようにしてエラー処理を確認する。 2. try文、finally文に正しい処理を加えて、正常処理を確認する。 ### チェック例外と非チェック例外 チェック例外とは、呼び出し元に渡る可能性のある例外が列挙され、メソッドの型の一部となる例外処理の方法である。 厳密な例外処理が可能になる一方で、開放・閉鎖原則に違反している。3レベル以上先の呼び出し元でcatchした場合、その間にあるメソッド全てに例外を追加しなければならず、階層が深くなれなるほどこの副作用は大きくなる。 従って、こうした依存性がない非チェック例外の使用が推奨される。 ### 例外で状況を伝える 例外のスタックトレースは、どのような例外が発生したかは教えてくれるが、失敗した処理の意図は教えてもらえない。 エラーメッセージに十分な情報を与え、状況を伝える必要がある。 ### 呼び出し元が必要とする例外クラスを定義する 複数の例外で同じ処理(エラーの記録)を行いたい場合は、個別にcatchするのではなく、APIをラップして共通の例外型を返すようにする。ラッパは例外をキャッチし翻訳する。 こうしたラッパを用いた共通化の最大の利点は依存性を最小限にすることができる点である。特定のAPI設計に依存することなくアプリケーションを組み立てられるメリットがある。 ### 正常ケースのフローを定義する 例外がロジックを分断している場合、オブジェクトを返すことによって例外処理自体を無くしてしまうこともできる。これをスペシャルケースパターンという。 ### nullを返さない nullチェックを逐次実行していくようなコードでは、チェックする要素が増えるごとに次々とnullチェックが増えてしまう。 こうした場合はスペシャルけースオブジェクトが良い解決策になる。 ### nullを渡さない nullを渡すとnullPointerExceptionの例外が発生する。 こうしたコードは新たな例外型のスローをするほかない。 nullを渡すのは原則禁止すべきである。 ## 第8章 境界 ライブラリやオープンソース等の外部のコードと、自分が書いたコードとを、時に接続しなければならない場合がある。 ### サードパーティーのコードを使用する サードパーティー製のインターフェースは、多くのアプリケーション環境で動作する必要があり、製作者は多くの顧客のフィードバックを受けて改善を試みる。 一方で利用者側は、利用者の特定のニーズに特化したものを求める。この両者の緊張が、システムの境界で問題を引き起こす場合がある。 例えば、java.util.Mapの場合、様々な機能を提供しており便利であるが、たとえ特定の型のオブジェクトしかMapを使わないとしても、それをインターフェース側で矯正することはできない。 Mapは多くの場合洗練されたジェネリクスが用いられるが、この場合、オブジェクトから特定の型を強制することはできず、Mapは本来必要な機能以上のものを提供してしまっていることになる。これを放置すると、インターフェース変更時に大きな影響を受けることになってしまう。 こうした場合は、インターフェースをprivate化し、アプリケーションへの影響を最小限にとどめることで解決できる。 ### 境界の調査と学習 サードパーティー製のライブラリのメリットは、豊富な機能を簡単に実現できることである。 一方でデメリットは、サードパーティー側のバグや変更に対処するのが難しくなることである。特に、バグがサードパーティー側のバグなのか、我々のコード側で埋め込んだバグなのか判断が難しい。 また、サードパーティー製ライブラリの理解に時間がかかるデメリットもある。 こうした場合、実際に開発中のコードで実験しつつ、サードパーティーライブラリの調査のためのテストコードを書く「学習テスト」が行われる。 ### 学習テストはタダ以上のものである 学習テストは、いずれ使わなければならないものについての理解を深めるための実験と考えれば、結果的にコストはかからずに済む。 また、テストを埋め込むことで、最新バージョンに変更した際の互換性について簡単に調べることができる。逆に、テストが無ければ、現バージョンから更新できなくなり、サポート終了による脆弱性にさらされる危険性がある。 **外部境界のテストは、将来の移行を簡単に行うことができる有用性がある。** ### まだ存在しないコードを使用する 学習テストと同様に、コードのうち、まだ知識が不十分な部分がある場合、明確・不明確の境界のテストを作るのが望ましい。 ### きれいな境界 良い設計では、過大な投資無しに変更への対処が可能となる。 サードパーティー製ライブラリへの依存を最小化することも、良い設計の一つである。 影響を最小化し、利点を生かす使用方法が求められる。 ## 第9章 単体テスト テスト駆動開発(TDD)は急速に知られていったが、あまりに性急にテストを取り込んだため、多くのプログラマが、優れたテストを書くための本質や重要な点を見失っている。 ### TDDの三原則 1. 失敗する単体テストのコードを書く前に製品のコードを書いてはならない。 2. コンパイルが通り、適切に失敗する単体テストができるまでは、次の単体テストを書いてはならない。 3. 現在失敗している単体テストが通るまで、次の製品コードを書いてはならない。 ### テストをきれいに保つ テストコードは汚くても通ればよい、というわけではない。 テストが汚いほど変更は困難になり、新しいテストを追加するのも困難になる。テストが足を引っ張る形で保守コストが増加してしまい、テストを捨てると次は不具合が多発するようになってしまう。 **テストは製品コードと同じようにきれいに保たなくてはならない。** #### テストはxxx性を可能とする コードの柔軟性や保守容易性、再利用性を維持・提供するのは単体テストである。 ### クリーンテスト 洗練されたテストには、**明瞭さ、単純さ、表現の密度**が必要である。 **構造・操作・検査の3ステップ**が明確になったテストデータは、誰もが見やすく理解可能となる。 #### ドメイン特化テスト言語 APIの代わりに、APIを使った関数とユーティリティを作成して、もっと簡単に読み書きできるような、テスト特化型APIを作成すると良い。 これらはテスト言語であり、修練を積む中で温めていくものである。 #### 二重規範 本番環境と異なり、開発環境でしか使わないテストでは、一定の規則違反も許される。例えばテストする順番を明確にするために、変数名をメンタルマッピングを利用した頭文字のメモで表したり、少し汚いStringBufferの利用を行ったりと、本番環境の制限を無視した表現が可能となる。 ### 1テスト1アサート 1テスト1アサートという見解は、1つの結論にたどり着くという面で好ましいが、テストでは、2アサートがあっても問題ない。 ### 1つのテストでは1つの概念を扱う 1テスト1概念という考え方は必要である。 複数の概念がテストされてしまうと、分かりにくくなり、抜け漏れが生じやすい。 ### クリーンテストの規則FIRST * **Fast**:頻繁に実行するテストは**高速**である必要がある。 * **Independent**:テスト同士が相互関連してはならない。**独立性を保つ**必要がある。 * **Repeatable**:**どのような環境においても再現可能**でなければならない。 * **Self-Validating**:テスト結果は**成功・失敗の2択**とし、他の資料との比較無しに**自己検証可能**でなければならない。 * **Timely**:テストは**必要な時にすぐに書ける適時性**がなければならない。 ###### tags: `読書`