Deep Side of Java〜Java 言語再入門 第3回 〜 クラス設計とデザインパターン

継承の2つのパターン






継承の2つのパターン

設計の観点から見ると、「クラス継承」には2通りのやり方がある。1つは今まで見て来たような Java のクラス継承に関する言語的機能を使うものであることは当然である。もう1つは「委譲」と呼ばれ、形式的にはクラス継承ではないのだが、クラス継承とほぼ同様の効果を得ることができる。

あるクラスを継承する場合、普通にクラス継承(inherit)すれば次のようになる。

class Base {
        private int x, y;
        public int methodA( ) { ..... }  /* このクラスは上書きされる */
        public int methodB( ) { ..... }  /* このクラスは上書きされない */
}

class Delivered extends Base {
        public int methodA( ) {          /* 基底クラスの上書き */
                super.methodA();
                ..... 
        }
        public int methodC( ) { ..... }  /* まったく新規 */
}

しかし、次のようなことも可能なのである。これが「委譲(delegate)」である。

class Delivered {
        Base b = new Base();     /* インスタンスとして基底クラスを持つ */
        public int methodA( ) {  /* 上書きの要領 */
                b.methodA();     /* 必要に応じて内部でインスタンスのメソッド */
                 .....           /* として呼び出す。あと固有処理。 */
        }

        public int methodB( ) {
                b.methodB();    /* 上書きしない継承を模倣 */
        }

        public int methodC( ) {
                 .....          /* 固有処理 */
        }
}

呼び出すクラスではそれが基底クラスの「継承」なのか「委譲」なのかを気にする必要はまったくなく、双方とも「クラス継承」のように動作する。この「委譲」は「継承」の代わりとして、「クラス継承」の手法として使えるのであり、デザインパターンの中で大活躍する。しかし、若干の違いもある。

  1. 「継承」では基底クラスのメソッドを何もせずに使うことができるが、「委譲」ではわざわざ「基底クラスのインスタンスを使ってメソッドを呼び出」す必要がある。これにはメリットとデメリットがある。デメリットは当然、そのまま使いたいメソッドが多い場合にはわざわざ「基底クラスのインスタンスを通じてメソッド呼び出し」をするだけの細かいメソッドを沢山定義する必要があることである。逆にこれがメリットになる場合もある。なぜなら、アクセスを禁止したい基底クラスのメソッドは、「委譲」されたクラスで定義しなければ、呼び出されようがないのである。これは基底クラスのメソッドを整理し、アクセス制限をする有効な手段である。これは Facade として知られている。

  2. 「委譲」の方がクラス同士の相互作用に幅を持たせることがしやすい。このため、デザインパターンではクラス継承以上に「委譲」が活躍する。また、「委譲」を使って Java では許されない多重継承のような手法を使うことさえ可能である。要するに2つのクラスのインスタンスを変数として保持し、その2つのインスタンスのメソッドをすべて、当該インスタンスのメソッド呼び出しするメソッドとして実装すれば良いのである。



copyright by K.Sugiura, 1996-2006