TL;DR

内容

情報隠蔽

深いモジュールを作るために最も重要な技法は、情報隠蔽である。データ構造やアルゴリズム、低レベルの詳細など、実装の方法に関する知識をモジュールの中にカプセル化する。たとえば、TCP の実装方法や JSON の解析方法がそれにあたる。

情報隠蔽は 2 つの方法で複雑さを減らす。1 つは、インタフェースを単純にすることである。もう 1 つは、システムの変更を容易にすることである。隠された情報にはモジュールの外からの依存関係がないので、その情報に関わる設計変更はモジュールの中で完結する。

どの情報を隠すかを吟味することが大切である。より多くの情報を隠せれば、インタフェースはよりシンプルになる。なお、private にした変数やメソッドでも getter や setter を通じて情報が公開されることがあるので、情報隠蔽は変数やメソッドを private にすることとは違う。

情報は完全に隠すのが理想だが、部分的な情報隠蔽にも価値がある。たとえば、一部の利用者だけが必要とする機能や情報は、一般的な使い方からは見えないように、別のメソッドから使えるようにする。

情報漏洩

実装の詳細がインタフェースに表れていると、情報漏洩が起きる。インタフェースに表れていなくても起きることがあり、これを裏口からの漏洩(back-door leakage)と呼ぶ。たとえば、ある形式のファイルを読むクラスと書くクラスがあると、形式が変わったときに両方を直す必要がある。

情報漏洩はレッドフラグの 1 つである。優れた設計者になるには、情報漏洩に敏感である必要がある。情報漏洩を見つけたら、関係するクラスを 1 つにまとめられないかを考える。影響を受けるクラスから情報だけを抜き出して、新しいクラスを作る方法もある。ただし、それが有効なのは、詳細を隠すシンプルなインタフェースを見つけられたときだけである。新しいクラスがインタフェースを通じて情報を公開してしまえば、裏口からの漏洩をインタフェースによる漏洩に置き換えただけになる。クラスを少し大きくすると改善できることが多いが、大きすぎるクラスも問題である。

時間的分解

情報漏洩の主な原因は、時間的分解(temporal decomposition)と呼ばれる設計である。時間的分解とは、処理が実行される順番がシステムの構造にそのまま反映されている状態を指す。処理の順番は重要なので、アプリケーションのどこかには表れる。しかし、情報隠蔽と一致する場合を除き、順番をモジュールの構造に反映させるべきではない。

時間的分解もレッドフラグの 1 つである。モジュールを設計するときは、処理の順番ではなく、各処理を実行するのに必要な知識に注目する。

クラスの中での情報隠蔽

情報隠蔽はクラスの中でも使える。各メソッドが特定の情報や機能をカプセル化し、クラスの他の部分から隠すように設計する。また、インスタンス変数を使う場所を減らせば、クラス内の依存関係が減り、複雑さも減る。

やりすぎに注意

情報隠蔽に意味があるのは、隠す情報がモジュールの外で必要とされない場合だけである。どの情報がモジュールの外で必要かを見極め、必要なら公開する。そのうえで、公開する情報を最小限にすることを目指す。たとえば、設定パラメータによってモジュールの性能が変わる場合、モジュールが自動で設定を調整できるなら公開せず、調整できないならパラメータとして公開する。

まとめ

情報隠蔽と深いモジュールは密接に関係している。モジュールが多くの情報を隠すほど、提供する機能は多くなり、インタフェースは小さくなる。これが深いモジュールである。逆に情報を隠さないモジュールは、機能が少ないか、インタフェースが複雑になる。モジュールは処理の手順ではなく、必要な知識の単位でカプセル化する。手順に注目すると情報漏洩や浅いモジュールにつながる。

キーワード

自分の理解

部分的な情報隠蔽は、いまいちイメージしづらかった。次のようなイメージだろうか。ピクセルを変更する機能は一部の利用者だけが使い、通常の使い方では意識させない。

class GraphicsLibrary
  def initialize(image)
    @image = image
  end
 
  def change_pixel(x, y, color)
    # ここでピクセルの変更の実装が行われると仮定
    puts "Pixel at (#{x}, #{y}) changed to color #{color}."
  end
 
  def display_image
    # 通常の使用ケースではピクセルの変更についての詳細は意識させない
    puts "Displaying the image."
  end
end