仕様(インターフェイス)と実装の詳細 (1)
APIの公開/非公開が意識できるようになると共に、「仕様(インターフェイス)」と「実装の詳細」を意識できるようになるとよい。
という2つの角度でクラスをとらえる。前者については昨日のエントリでも示した通り、可視性がpublic,protectedなものが公開APIであり、その他は非公開APIである。機械的に判断できますね。では後者についてはどう判断すればいいか?
注:下の例で「このクラスのインターフェイスは?」と聞かれると単純に「Baz」と答えるかもしれない。がここで言いたいのは「Bazを介したFooのインターフェイス」ではなく「Foo自身が純粋に外部に公開する仕様としてのインターフェイス」のこと。インターフェイスとは何か?についてはinterfaceについて本気出して考えてみた - 都元ダイスケ IT-PRESS参照。この「操作パネル」のことです。対する実装の詳細とは、この「内部の電子回路」のことです。
/** * ... */ public class Foo extends Bar implements Baz { /** * ... */ public static final String CONST = "xxx"; /** * ... */ String hoge; /** * ... */ private String piyo; /** * ... */ public Foo(String hoge, String piyo) { this.hoge = hoge; } /** * ... */ public String getHoge() { return hoge; } /** * ... */ void setHoge(String hoge) { this.hoge = hoge; } /** * ... */ protected String getPiyo() { return piyo; } /** * ... */ private setPiyo(String piyo) { this.piyo = piyo; } // ...いろいろ }
さて、上記のコードの「仕様」と「実装の詳細」はどの部分だと言えるだろうか。簡単な判断方法としては、このFooをインターフェイスに変えてみると分かる。実際には、FooインターフェイスはBarクラスをextendsできないけども。Barもクラスじゃなくてインターフェイス化したと考え、ともかくインターフェイスにしてみる。
フィールドは、public static final なもの以外はエラーが出てしまうので削除。メソッドもpublicなもの以外はエラーが出てしまうので削除。メソッドシグネチャの後の { ... } はあるとエラーになってしまうので削除。という手順だ。残ったのはこれ。
/** * ... */ public interface Foo extends Bar, Baz { /** * ... */ public static final String CONST = "xxx"; /** * ... */ String getHoge(); // ...いろいろの一部 }
これが、このFooクラスの「仕様(インターフェイス)」だ。消した部分が「実装の詳細」と言われる部分である。ざっくりとした判断方法なので、protectedの扱いが微妙に間違っているのだが。protectedのシグネチャも仕様の一部である…*1。まぁまとめると、
- classをinterfaceに変えて残った部分+protectedなメソッドのシグネチャ部 → 仕様(インターフェイス)
- classをinterfaceに変えて消した部分(publicメソッドのボディ { ... } も含む!) → 実装の詳細
である。ちなみに、「仕様(インターフェイス)」と「公開API」はおおよそ一致する。従って、2次元的に分析すると、クラス定義の各要素は以下の3種類に分類できるんじゃないかな。
- 公開API かつ 仕様(インターフェイス) → 前述
- 非公開API かつ 実装の詳細 → interface化して消えた「シグネチャ」(protectedを除く)
- 単なる実装の詳細 → interface化して消えた「メソッドボディ」( {...} の部分)
以上がはっきり分かれて見えるようになったら、Fooクラスを使う(Fooクラスに依存する)クラスXを書く場合、そのXはFooの「仕様」に依存すべきであり、「実装の詳細」に依存すべきではないという指針に従う。
これも簡単に言い直すと、「Xを書く際に、実装の詳細を眺めながら書いてはならない。仕様(シグネチャとJavadoc)だけを頼りにXを書くべし」。Xを書く時に、Fooのメソッドボディを見てはならない*2。
という立場になって考えると、自ずと「FooのJavadocに書くべき事柄」が決まってくるはずだ。こんな情報を与えてやらないと、実装の詳細を見なければXを書けないよな…、と。
昔から高凝集低結合というスローガンがあるが、誤解を恐れずにに言えば「モジュールを超えて実装を追う必要がない状態」が低結合だ。Javadocがないだけで、結合度を高めてしまうことになる。気をつけよう。
http://d.hatena.ne.jp/daisuke-m/20091014/1255506192
手前味噌だが、FooにJavadocが無かった場合、Xの実装者はFooの実装の詳細を見てXを書く。つまり実装の詳細に依存しはじめ、FooとXの結合度が高まってしまう。