Island Life

< 言語のコアが単純であることの効用 | Paul Grahamのエッセイの新作が... >

2005/01/17

yomoyomoさんとこより、 メンテナンス可能なコードの書き方、およびその関連で Great Programmers。 良いコードとは、つまるところ適切なカプセル化とユニットテストにある、という 主張には基本的に同意。

ただ、「カプセル化 encapsulation」はオブジェクト指向界隈で「実装の隠蔽」 の意味で使われることが多いような気がする () ので個人的には避けたい用語だ。本当に重要なのは、(a)あるコードの変更が 影響を及ぼす範囲が容易に確定できること、(b)あるコードの意味に影響を 与える範囲が容易に確定できること (両者は同じことの表と裏だが、実際に コードをいじっている時には両方を考慮している)、であるはずだ。 私はこれをモジュラリティと呼んでいる。

実装の隠蔽はモジュラリティを実現する一つのツールだが、必要でも十分でもない。 実装を完全に隠蔽してもモジュラリティの低いコードはいくらでも書けるし、

実装を隠蔽しないでもモジュラリティの高いコードは書ける。うっかりミスを 防ぐ意味では有用かもしれない、という程度だと思う。 しかも、本当に厄介なバグは実装の隠蔽だけでは防げないような種類のものだ。

モジュラリティにとってより重要なのは、状態の管理だ。 何かが状態を持ち、そのふるまいが状態によって変化する場合、 あるコードの意味はそのコードの静的なスコープだけでなく、 対象となるものの状態にも依存する。静的に確定できるスコープの 外でそのものの状態が勝手に変えられてしまうと、もはやコードの 見た目を信頼することができない。

実装の隠蔽によって状態をさわれる箇所を限定することでこの問題は ある程度軽減できるが、それは対症療法にすぎない。例えばpublicな getterとsetterを用意してインスタンス変数を隠蔽することは この問題に対してほとんど何の解決にもなっていない。 もう少しソフィスティケートされたAPIを用意したとしても、 実装者の想定した方法以外でオブジェクトの状態を変えるパスがどこかに 存在する限り、問題は残る。

本当の問題は、状態を持つことそのものにある。ただ、現実の世界が ステートフルである以上、そことつき合うプログラムもまた状態を 持たざるを得ない。状態という猛獣をいかに飼い慣らすかという視点で 設計を行うべきだろう。実装の隠蔽も、状態を常に実装者の想定に 従うように管理するという目的で使われる場合には、有用だ。 もっとも、状態遷移が明示的にAPIの一部になっていることの方が重要で、 カプセル化はその安全ネットを提供するにすぎないと思うが。

形式的に状態を制御して扱えるようにするには、現実的には 静的型が必須のように思える。(Schemeでモナドを書いても Haskellのように綺麗にならない。) ただ、そこまでやらなくても いくつかのアドホックなテクニックを使うことで、 かなり安全に状態を制御することはできるだろう。

Tag: Programming