技術の犬小屋

Webプログラミングを餌に生きる犬のメモ帳

Effective Javaを読んでいたところ、勉強になったことがあったのでメモしておく。
 

コンポジションとは

コンポジション(Composition)は、日本語で「混合物」を意味する単語である。あるクラスの機能を持つクラスのことを指す。
特定のクラスの機能を、自分が作るクラスにも持たせたい場合に、継承を使わずフィールドとしてそのクラスを持ち、そのクラスのメソッドを呼び出すメソッドを持たせること。そうすることで、クラスに他のクラスの機能を組み込むことができる。
厳密なオブジェクト指向では、継承は「機能の継承」を目的とせず、「スーパークラスはサブクラスの一種である」といういわゆる「is-a」の関係を持たなければならない。そのため、単に機能を持たせたい場合には、継承ではなくコンポジションとすることが推奨される。
 

継承とコンポジションをどう使い分けるか

継承を使ったほうが良い場合とコンポジションを使ったほうが良い場合があるが、具体的に何を基準にして使い分ければ良いだろうか。
この問題を解決するために、一般的な物の考え方の一つに「分類」と「分割」がある。
 
分類とは、A is a B.のことであり、日本語だと「AはBである」と表現できる。これをis-aの関係と呼ぶ。
例えば、「犬は動物である」「猫は動物である」「人間は動物である」・・・など、これらは分類で表現することが出来る。
is-aの関係で表せるものは、継承で表現するのが都合がいい。
 
分割とは、A has a B.のことであり、日本語だと「AはBを含んでいる」と表現できる。これをhas-aの関係と呼ぶ。
例えば、「パソコンはCPUを含んでいる」「学校は先生を含んでいる」「自転車はサドルを含んでいる」・・・など、これらは分割で表現することが出来る。
has-aの関係で表せるものは、コンポジションで表現するのが都合がいい。
 

継承とコンポジションの違い

ここで改めて、継承とコンポジションの違いを明確にするために、コードを例に挙げて説明する。
まず、親となるPersonクラスを定義する。Personクラスは「氏名」と「年齢」を保持し、要求に応じてそれを返す。

public class Person {
    private String name;
    private int age;

    public Person(String n, int a) {
        name = n;
        age  = a;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

 
ここでPersonクラスを元にして、新たに「学年(grade)」を保持し、要求に応じてそれを返すことが出来るクラスを作りたい場合を考えてみる。
以下より、継承とコンポジション、それぞれを使用した場合の違いをコードで説明する。
 

継承を使った場合のコード

public class Student extends Person {
    private int grade;

    public Student(String n, int a, int g) {
        super(n, a);
        grade = g;
    }

    public int getGrade() {
        return grade;
    }
}
Student yamada = new Student("山田", 20, 2);
String name = yamada.getName();
int age     = yamada.getAge();
int grade   = yamada.getGrade();

 

コンポジションを使った場合のコード

public class Student {
    private Person person;
    private int grade;

    public Student(Person p, int g) {
        person = p;
        grade  = g;
    }

    public String getName() {
        return person.getName();
    }

    public int getAge() {
        return person.getAge();
    }

    public int getGrade() {
        return grade;
    }
}
Person y       = new Person("山田", 20, 2);
Student yamada = new Student(y, 2);
String name = yamada.getName();
int age     = yamada.getAge();
int grade   = yamada.getGrade();

 
上記の例では、継承とコンポジション、どちらを使っても自然に記述することが可能だが「継承とコンポジションをどう使い分けるか」の項目で説明した通り、どちらか一方では上手く記述できない場合がある。そういったときは、一度立ち止まってis-aとhas-aのどちらに当てはまるかを考えてみることが必要だ。
 
書籍「Effective Java」の「項目16 継承よりコンポジションを選ぶ」には、今回載せたコンポジションのコードよりも頑強な設計のコードが載っている。しかし、少々理解し辛い部分があったので、富士通さんの記事に載っていたコードを引用させていただいて説明した。気になった方はEffective Javaを読んでみると良いと思う。
 
 
以上
 
 
参考
書籍 Effective Java
コンポジションとは : JavaA2Z
オブジェクト指向プログラミングへの道 5日目:オブジェクトコンポジション : 富士通
オブジェクト思考: is-a関係とhas-a関係: 継承と包含
is-a – Wikipedia
has-a – Wikipedia

Javaのcloneメソッドの使い方 arrow-right
Next post

arrow-left Javaでマルチスレッドプログラミング -スレッドの同期-
Previous post