なになれ

IT系のことを記録していきます

A Philosophy of Software Designの11章要約

本内容は「A Philosophy of Software Design」の11章を要約する記事です。

過去記事

10章の紹介

他担当の方が要約した10章の内容を引用します。

  • 10章 エラーを存在しないものとして定義する
  • 例外処理は、ソフトウェアシステムを複雑にする最悪の原因の1つです
    • 開発者はしばしば例外をどう扱うかを考えずに例外を定義しがちです
  • この章では、なぜ例外が複雑さの原因として不釣り合いなのかを説明し、次に例外処理を単純化する方法を示している
  • 重要なのは例外を処理しなければならない場所を減らすこと
  • 不必要な例外を定義することで、例外処理に関する問題を悪化させることがよくある
    • 多くのプログラマは、「エラーを検出して報告することが重要」と教えられ、それを「エラーは多ければ多いほど良い」と解釈してしまう(だがそうではない)
    • この結果、少しでも怪しいと思ったものは例外で拒否するという過剰防衛的なスタイルになり、結果的に不要な例外が多発してシステムが複雑化する
      • 具体例として著者のTclの例
    • 難しい状況に対処するのを避けるために例外を使うのは魅力的:それを処理するきれいな方法を考え出すよりも、ただ例外を投げて問題を呼び出し元に押し付けるほうが楽
    • 問題を他の誰かに転嫁し、システムの複雑さを増すだけ
  • クラスが投げる例外は、そのクラスのインターフェースの一部
    • 例外を多く持つクラスはインターフェースが複雑で、例外の少ないクラスよりも浅くなる可能性がある
    • 例外は、インターフェースの中でも特に複雑な要素
  • 例外は捕捉されるまでにいくつかのスタックレベルに伝搬するため、メソッドの呼び出し元だけでなく、より上位の呼び出し元(とそのインターフェース)にも影響を与える
  • 例外処理による複雑さの被害を減らす最良の方法は、例外を処理しなければならない場所の数を減らすこと
  • 例外処理の単純化の方法
      1. 処理すべき例外が存在しないようにAPIを定義すること(エラーを存在しないように定義する)
      1. 例外マスキング
      2. 例外状態をシステムの低レベルで検出して処理するため、高位のソフトウェアがその状態を認識する必要はありません
        • 具体例: TCPのようなネットワーク伝送プロトコル
        • TCPはその実装内で失われたパケットを再送することでパケットロスをマスクし、最終的にすべてのデータが通過するため、クライアントはパケットロスを意識することはありません
      3. 例外を(マスキングせず)報告することは事態を改善するのではなく、悪化させることになります
        • 具体例: NFS(ネットワークファイルシステム)
        • NFSファイルサーバーが何らかの理由でクラッシュしたり応答しなかったりすると、クライアントは、問題が最終的に解決されるまで、サーバーへの要求を何度も何度も発行し直します
        • NFSクライアントはユーザーのコンソールに "NFS server xyzzy not responding still trying" という形式のメッセージを表示します
        • 最良の代替案は、NFSがエラーを隠蔽し、アプリケーションをハングアップさせることです。このアプローチでは、アプリケーションはサーバーの問題に対処するためのコードを必要とせず、サーバーが復旧すればシームレスに再開することができます。
      4. 例外マスキングはすべての状況で機能するわけではありませんが、機能する状況では強力なツールです。
        • 例外をマスクするコードの形で機能を追加するため、結果的にクラスを深くすることができるのです。例外のマスキングは、複雑さを下方に引っ張り出す例です
      1. 例外集約
      2. 例外集約の背後にある考え方は、多くの例外を一つのコードで処理すること
      3. 個々の例外に対して個別のハンドラを書くのではなく、一つのハンドラですべての例外を処理する
      4. 前項で説明した集約は、カプセル化情報隠蔽の観点から良い特性を持っている
        • トップレベルの例外ハンドラは、エラーレスポンスを生成する方法についての知識をカプセル化しますが、特定のエラーについては何も知りません
      5. 例外の集約は、例外が処理される前にスタックの数レベル上に伝搬する場合に最も効果的
        • これにより、より多くのメソッドからの例外が同じ場所で処理されるようになります
      6. 例外の集約を考える一つの方法は、それぞれが特定の状況に合わせて作られたいくつかの特別な目的のメカニズムを、複数の状況に対応できる単一の汎用メカニズムに置き換えるというもの
      1. アプリケーションをクラッシュさせること
      2. 処理する価値のない特定のエラーが発生するが(エラーは処理するのが難しいか不可能)これらのエラーに対して行う最も単純なことは、診断情報を表示し、
        • 具体例:メモリ不足のエラー
        • メモリ不足のエラーを処理することに意味があることはほとんどありません。これは、あまりに少ない利益に対してあまりに多くの複雑さを生み出す
        • これらのエラーは頻繁に発生するものではないので、アプリケーションの全体的な使い勝手に影響を与えることはまずない
  • 例外を定義しない、あるいはモジュール内部でマスクすることは、例外情報がモジュール外部で必要とされない場合にのみ意味を持つ
    • この考え方は行き過ぎの可能性があり、具体的にはネットワーク通信のモジュールはネットワーク例外をすべてマスクしていた
    • このモジュールを使用するアプリケーションは、メッセージが失われたり、ピアサーバーに障害が発生したりしても、それを知る術がなく、この情報なしには、堅牢なアプリケーションを構築することは不可能なケースが有る
    • この場合、モジュールのインターフェイスが複雑になるとはいえ、例外を公開することはモジュールにとって不可欠なことになる
  • 何が重要で何が重要でないかを判断する必要があり、重要でないものは隠すべき、重要なものは、公開しなければなりません(21章で詳しく説明)

11章要約

  • 11章 2回設計しよう
  • 概要
    • ソフトウェアの設計は難しいので、最初に考えたものが最適になるとは限らない
    • 2回設計しよう
    • 1つ目の設計と2つ目の設計を比較して、長所と短所をリストアップする
    • 2回設計するという少しのコストをかけることで、優れた設計が得られることは長期的にメリットが大きい
    • 経験を積むとより困難な問題を抱えた環境に移るため、経験を積んだからといって1回で設計が最適化できるという思い込みは禁物
    • 2回設計するというアプローチは設計自体を向上させるだけでなく、自分の設計スキルを向上させることにもつながる

感想

  • 2回設計しようというプラクティスはそのとおりと思えるのだけど、いざというときに忘れてしまいそう。早く実装したいという誘惑に勝てないかもしれない。誘惑に勝たなければと強く思う章だった