「インターフェースに対してプログラミングする」という設計原則について

書籍「Head First デザインパターン」を読みながらデザインパターンを勉強していたら、設計について勉強になったことがあったのでメモ。

用語の確認

スーパークラス

スーパークラスとは、オブジェクト指向においてクラスの親子関係が成り立つ場合の親側のクラスのことである。

サブクラス

サブクラスとは、オブジェクト指向において、クラスの親子関係が成り立つ場合の子側のクラスのことである。

スーパータイプ

スーパータイプとは、オブジェクト指向において、型の親子関係が成り立つ場合の親側の型のことである。

サブタイプ

サブタイプとは、オブジェクト指向において、型の親子関係が成り立つ場合の子側の型のことである。

抽象クラス

この記事内で説明すると文章が膨大になってしまうので、他の記事にまとめてある。
抽象クラス(abstract)とインターフェース(interface)についての復習

インターフェース

この記事内で説明すると文章が膨大になってしまうので、他の記事にまとめてある。
抽象クラス(abstract)とインターフェース(interface)についての復習

「インターフェースに対してプログラミングする」とは

デザインパターンでは、設計原則として「実装に対してではなく、インターフェースに対してプログラミングする。」となっている。

インターフェースという言葉は、あまりにもいろいろな意味を含み過ぎている。インターフェースという概念もあるし、Java構文のinterface(インターフェース)もある。実際にJava構文のinterface(インターフェース)を使用しなくてもインターフェースに対するプログラミングは行うことができる。重要なのは、実行時にオブジェクトがコードに束縛されないようにするために、スーパータイプに対してプログラミングすることでポリモーフィズムを活用することである。

上記を理解する為に、以下より、「実装に対するプログラミング」と「インターフェース(スーパータイプ)に対するプログラミング」を実際にコードに落とし込みながら見ていくことにする。

これから登場するインターフェースとクラス

インターフェース

Animal

interface Animal {
    public void makeSound();
}

Animalインターフェースを使用したサブクラス

Dog

class Dog implements Animal {
    public void makeSound(){
        bark();
    }
    private void bark(){
        //犬の吠え声
        System.out.println('woof!woof!');
    }
}

「実装に対するプログラミング」

実装に対するプログラミングでは、以下のようになる。

Dog d = new Dog();
d.bark();

変数「d」をDog型(Animalの具象実装)として宣言すると、具象実装に対してコードディングをしなければいけなくなる。

「インターフェース(スーパータイプ)に対するプログラミング」

インターフェース(スーパータイプ)に対するプログラミングでは、以下のようになる。

Animal a = new Dog();
a.makeSound();

Dogであることは分かっていても、Animal型の参照を多態的(ポリモーフィズムを意識しながら)に使用できる。
これにより、変数を宣言するクラスが、実際のオブジェクトの型について知る必要がなくなる。

Animalのサブタイプが実際に何であるかは問題ではなく、気に掛けているのは、このサブタイプがmakeSound()に対する応答方法を知っているかどうかということである。

参考
書籍 Head First デザインパターン