Java.use(better, Jython)《2》継承に警鐘を鳴らす(その弐)

記事一覧 Java.use(better, Jython)《Jython2.5》

《こちらに移動中です》2006年6月 6日 (火)

継承に警鐘を鳴らす(その弐)

《著》後藤いるか・伊藤うさぎ・小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
第1版♪2003/05/23

継承に警鐘を鳴らす(その弐)

クラス継承とメソッド委譲とを比較して、その問題点を検証します。

■ クラスを継承する

継承によって実現したものは「ホワイトボックス」型の再利用とも呼ばれ、継承元のクラスの存在が知る所となります。そのため、クラスを実現する方法(継承)に依存した、「情報隠蔽の原則」に反する「カプセル化の盲点」を付いた不当な操作を許します。

//                                      Java
public class Stack
  extends LinkedList ...          // クラス継承
//                                      Java
Stack stack = new Stack();

stack.push("A");
stack.push("B");
stack.push("C");
...
stack.remove(2);  // 不当な操作を許す

たとえば、継承元のクラス LinkedList が提供するメソッド remove(2) を利用すると、2番目の要素 "B" を取出せます。これは、スタックであることを無視した、不当な操作と見なせます。

委譲は善で、継承は悪なのでしょうか。必ずしもそうとは限りません。そこで注目したいのが、インターフェースの存在です。

■ メソッドを委譲する

委譲によって実現したものは「ブラックボックス」型の再利用とも呼ばれ、委譲先のクラスの存在は知る由もありません。そのため、委譲元のクラスとは独立した存在となり、「情報隠蔽の原則」に反する「カプセル化の盲点」を付いた不当な操作を許しません。

//                                      Java
public class Stack ...
   LinkedList element;            // メソッド委譲
//                                      Java
Stack stack = new Stack();

stack.push("A");
stack.push("B");
stack.push("C");
...
stack.remove(2);  // コンパイル時のエラー


たとえば、委譲先のクラス LinkedList が提供するメソッド remove(2) を利用しても、2番目の要素 "B" を取出せません。つまり、スタックであることを無視した、不当な操作は拒否されます。

■ インターフェースを導入する

インターフェースを利用すると、クラスの実現方法には依存しなくなります。

■ クラスを継承する

クラス Client は、インターフェース StackIF で規定したメソッド呼出しに依存します。しかし、クラス Stack が、クラス LinkedList を親クラスとしているという実現方法に、依存することはありません。

//                                      Java
public class Stack extends LinkedList
  implements StackIF ...          // クラス継承
//                                      Java
StackIF stack = new Stack();

stack.push("A");
stack.push("B");
stack.push("C");
...
stack.remove(2);  // コンパイル時のエラー
                  // Stack に対しては有効

生成されたオブジェクト stack は、StackIF で規定したメソッド呼出し push() および pop() だけを利用できます。しかし、メソッド呼出し remove() は、インターフェース StackIF で規定したものではないので、コンパイル時のエラーとなります。

■ メソッドを委譲する

クラス Client は、インターフェース StackIF で規定したメソッド呼出しに依存します。しかし、クラス Stack が、クラス LinkedList をフィールドとしているという実現方法に、依存することはありません。

//                                      Java
public class Stack implements StackIF ...
   LinkedList element;            // メソッド委譲
//                                      Java
StackIF stack = new Stack();

stack.push("A");
stack.push("B");
stack.push("C");
...
stack.remove(2);  // コンパイル時のエラー
                  // Stack に対しても無効

インターフェースの規定に従って記述したコードは、クラスの実現方法(継承または委譲)には依存しません。そのため、コードの再利用性と柔軟性とを向上するなどの効果が期待できます。さらに、インターフェースの規定に従わないコードは、コンパイル時に検証できるようになります。

》こちらに移動中です《
TOP


関連記事

Last updated♪2009/08/01