A Philosophy of Software Design ch5. Information Hiding (and Leakage)
Information Hiding (and Leakage)
- ch4.で述べたこと: モジュールは深くあるべき
- 本章: どう作るの
Information hiding
- 知識の隠蔽
- 深いモジュールを作る上で最重要テクニック
- in 1972 David Parnas
- 実装のhowが隠蔽される
- 2つの点で複雑性を減らす
- モジュールを設計する際は、なんの知識を閉じ込めるかよく考えよ
- 隠蔽すればするほど、インタフェースは単純になる
- つまり、モジュールは「深く」なる
- 【補】利用者にとって重要な知識は隠蔽してはいけない(ch.6)
- 知識の隠蔽はゼロ百ではない、部分的でも価値がある
- クラスの知識が全クライアントコードに暴露されているよりは、一部のクラスにのみ公開されているほうがマシ
- 知識を必要とするクラス向けのメソッドを用意する
- 【補】Repository向けのsetterとかはこれ?
- 知識を必要とするクラス向けのメソッドを用意する
- クラスの知識が全クライアントコードに暴露されているよりは、一部のクラスにのみ公開されているほうがマシ
Information leakage
- 隠蔽できてないやつ
- 設計(how)の変更があちこちに跳ねる
- ある知識が複数のモジュールに反映されている
- 重複
- ひとかたまりであるべきものが分散している(Back-door leakage)
- 例: データフォーマットのread/writeが別々のモジュールに分かれている
- ひと目でわからないので厄介
- 知識がインタフェースに漏れている
- ある知識が複数のモジュールに反映されている
- 知識の漏出はソフトウェア設計上、最重要危険信号
- 知識の漏出に敏感であることは最重要スキル
- 知識の漏出に出くわしたら自問自答せよ:
- 「これらのクラスを再構成して、この知識が1つのクラスにしか影響を与えないようにするには?」
- クラス群が小さく、漏出した知識と密に紐付いているなら、1つのクラスにまとめることを検討する
- あるいは、知識を別クラスに切り出して委譲する
- 切り出した新しいクラスのインタフェースに知識が漏出していたら意味がないことに注意する
- Back-door leakageがインタフェース越しの(普通の)漏出になったたけ
- 切り出した新しいクラスのインタフェースに知識が漏出していたら意味がないことに注意する
Temporal decomposition
- ソフトウェア構造がタスク発生順に対応している
- 時間分割してしまっている例
- クラス群
- HogeReader
- HogeModifier
- HogeWriter
- Hogeフォーマットについての知識がReaderとWriterに散らかっている
- Back-door leakage
- まとめよう
- クラス群
- タスクの発生順ではなく、各タスクが必要とする知識に着目する
- 【補】この場合、Hogeフォーマットの読み書き
Example: HTTP server
- HTTPとはなんぞや(略)
- Request/Responseの話
Example: Too many classes
- やりがちなミス: 時間分割
- HTTPリクエストを読み、文字列を返すクラス
- 【補】Receiverとする
- 文字列をパースするクラス
- 【補】Parserとする
- HTTPリクエストを読み、文字列を返すクラス
- パースの知識のBack-door leakage
- 知識の隠蔽は、クラスを少し大きくすることで改善することが多い
- 関連機能がまとまる
- インタフェース単純化
- 【補】クラス減る、メソッド減る
- 深いモジュールにつながる
- かといってアプリケーション全体を1クラスにしていいわけもない
- いつクラスを分割すべきかについてはch9にて論ずる
Example: HTTP parameter handling
public Map<String, String> getParams() { return this.params; }
- こうあるべき
public String getParameter(String name) {...} public int getIntParameter(String name) {...}
- 「内部表現が
Map<String, String>
である」という知識を隠蔽した getIntParameter
はさらに下記メカニズムを隠蔽- 文字列から整数への変換
- 変換失敗時の例外送出
Example: defaults in HTTP responses
- やりがちな間違い: デフォルト値を設定すべきものに設定しない
- 例
- HTTPレスポンスのHTTPバージョン
- リクエストのものと一致するべき
- DATEヘッダ
- HTTPレスポンスのHTTPバージョン
- モジュール利用者が設定するのは、知識の漏出にあたる
- 例
- 「デフォルト値」は、知識の部分的な隠蔽の例
- 普遍的なケースが可能な限り単純になるように設計する、という原則にしたがっている
- 利用者は、普遍的なケースにおいて、デフォルトが設定された項目の存在について知らない
- レアケースでは
- デフォルト値をオーバライドする
- デフォルトのふるまいを変えるための特別なメソッドを起動する
- 【補】UnixのファイルIOの
lseek()
的なやつ
- 【補】UnixのファイルIOの
- クラスはよしなに「正しいことをする」べき
- デフォルト値はこの一種
- JavaのファイルI/Oの姿勢はこれに反する
- ファイルI/Oにおけるバッファリングは誰もが欲する機能
- わざわざ指定しなくても有効になってほしい
- 利用者は存在すら知らないのが望ましい
Red Flag: Overexposure
- 普遍的な機能を利用するために、めったに利用されない機能についても学ばなければならない
- 認知の負荷の増大
Information hiding within a class
- 知識の隠蔽は、なにもシステムに対するクラスだけの話ではない
- クラスの中に知識をカプセル化し、システムの残りの部分から隠蔽する
- クラスに対するメソッド
- privateメソッドの中に知識をカプセル化し、クラスの残りの部分から隠蔽する
- インスタンス変数の利用箇所を減らせ
- 依存を排除し、複雑性を減らす
Taking it too far
- 隠蔽していいのは、「モジュール外で必要とされない知識」
- モジュール外で必要とされる知識は隠蔽してはいけない
- 例: モジュールのパフォーマンスチューニング用コンフィグ
- ユースケースごとに異なる設定が必要
- モジュール外で必要な情報は最小化せよ
- 設定値を自動調整できるなら、そのほうが設定項目を公開するよりも良い
- どの知識がモジュール外で必要か認識し、それを確実に公開することが重要
Conclusion
- 知識の隠蔽と深いモジュールとは深く関連している
- 知識を隠蔽するほど…
- モジュールが提供する機能は増える
- インタフェースは小さくなる
- つまり、モジュールは深くなる
- 逆もまた然り
英語
- pernicious
- 有害な、悪質な