Fujitsu The Possibilities are Infinite

 

オブジェクト指向プログラミングへの道
5日目:オブジェクトコンポジション


知史

知史さん

航祐

航祐君

そして、次の日…。 新人の航祐(こうすけ)君とそのチューターの知史(さとし)さんの会話です。

今日はオブジェクトコンポジションです。 継承との違いや長所、短所などについての話です。


航祐

「知史先輩、こんにちは」


知史

「やあ航祐君。 今日はオブジェクトコンポジションについて話をするんだったね。 オブジェクトコンポジションは、前にちょっと出て来たが覚えているかい」


航祐

「はい。 あるクラスに別のクラスのオブジェクトを取り込むことをオブジェクトコンポジション、そのオブジェクトに処理をまかせることを委譲と言う、とのお話でした」


知史

「そう。 前の例ではUserクラスの中にPlanクラスのオブジェクトを取り込んで料金プランの処理をそこに任せていた」


航祐

「オブジェクトコンポジションなんて初めて聞きました」


知史

「航祐君は継承は知っているね」


航祐

「はい。 スーパークラスを基に機能を追加したサブクラスを作ることです」


知史

「そうだね。 継承はJava言語の機能だからJavaを学んだ人は誰でも知っている。 オブジェクトコンポジションは継承と同じように、他のクラスの力を借りて機能を果たすわけだが、単なる使い方なので、知る人ぞ知る、と言う状態なんだ」


航祐

「ああ、そうなんですか」


知史

「しかし、オブジェクトコンポジションの役割は重大で、継承より良く使われてもよいはずなんだ」


航祐

「え~、そうなんですか。 でも誰も教えてくれませんでした」


知史

「そうだね。 通常のJava教育ではJava言語の規則を教えるだけで精一杯で、とても使い方までは手が回らないんじゃないかな」


航祐

「ふ~ん」


知史

「前に触れたGof(4人組)の書いたデザインパターンの本でも、再利用可能なオブジェクト指向設計の原理としてインターフェースと並んで、オブジェクトコンポジションを多用することが挙げられているんだ」

「継承とオブジェクトコンポジションとの相違を見てみよう。 まず、親となるPerson(個人)クラスとそれを呼ぶメインプログラムを示そう。 Personクラスは氏名と年齢を保持し、要求に応じてそれを返す」

【Personクラス】
図解: Personクラス

【メインプログラム】
図解: メインプログラム(Personクラス)

「メインプログラムを実行させると、次のように出力される」

図解: 実行結果(Personクラス)

「このとき、Personクラスを継承して、学年を保持するStudent(学生)クラスを作ってみよう。 Studentクラスでは新たに変数「grade(学年)」を宣言しているね。 StudentクラスではPersonクラスから継承した、氏名と年齢に加えて学年も変数として持つことができるようになったわけだ」

【Studentクラス】継承を使ったコード
図解: Studentクラス(継承)

【メインプログラム】
図解: メインプログラム(Studentクラス:継承)

「このプログラムを実行させると、次のように出力される」

図解: 実行結果(Studentクラス:継承)

「一方、継承ではなく、オブジェクトコンポジションを使ってStudentクラスを作ると、こうなる。 追加する変数はやはりgradeだ」

【Studentクラス】オブジェクトコンポジションを使ったコード
図解: Studentクラス(オブジェクトコンポジション)

【メインプログラム】
図解: メインプログラム(Studentクラス:オブジェクトコンポジション)


航祐

「この(1)の部分がオブジェクトコンポジションですね」


知史

「そうだね。 航祐君、これらを比べて、気がつくことを挙げてみて」


航祐

「えーと。 オブジェクトコンポジションの方がプログラムの量が多いですね。 Studentクラスでは、まず(1)のPersonクラスの取込の部分が継承にはありません」


知史

「これは継承ではclassの宣言文でextends Personとしているから不要なのだね」


航祐

「次に、(2)のgradeの宣言は共通ですね。 (3)のコンストラクタであるStudentの部分は変数の初期化の仕方が少し異なりますが、基本的には同じです。 あれ、(4)と(5)の部分が、継承の方にはありませんね。 (6)は共通です」


知史

「(4)ではStudentクラスのgetNameメソッドの処理を行っている。 やっていることはPersonクラスのgetNameメソッドを呼び出しているだけだ。 (5)はgetAgeに対して同じことをやっている。 継承はJavaが提供している機能なので、(4)と(5)は明に書かなくてもJavaがやってくれるんだ。 しかし、オブジェクトコンポジションはプログラマーが制御しなくてはならないので(4)や(5)のようにいちいちを書かなければならない」


航祐

「ふ~ん。 ちょっと面倒ですね」


知史

「そうだね。 しかし、スーパークラスのすべてのメソッドをこのように書き直す必要はないんだ。 使うものだけを書けばよい」


航祐

「ああ、そうなんですか」


知史

「逆に継承を使うと、当面使わないものまで全部変換されてしまう。 だから継承を使ったときは、スーパークラスを変更したときには、そのサブクラスを全部調べて、変更の影響を修正する必要がある。 だから継承とコンポジションは、それぞれの特長をよく見極めて使うことが大切だ。 それぞれの得失をまとめておこう」

  クラスの継承(is-a関係) コンポジション(has-a関係)
長所 Javaの標準機能なので容易にコードの再利用が図れる 2つのクラスは完全に分離している
短所 親クラスを修正すると、その影響が子クラスに及ぶことがある わざわざ別にインスタンスを生成しなければならない
必要なメソッドはいちいち取り込まなければならない

航祐

「適切な使い分けは、なかなか難しそうですね」


知史

「一般にAはBの特別な種類であるときは継承関係、AはBの性質であるときにはオブジェクトコンポジションを使うとよいとされている。 StudentというのはPersonの一種だろうか、それともPersonの性質にすぎないだろうか」


航祐

「性質のような気がしますが」


知史

「そうだね。 学生でかつ社会人なんて言う人がいるケースを扱わねばならないとしたら、性質と捉えてオブジェクトコンポジションを使うべきだろうね。 しかし、もっと単純でそんなケースはないモデルを扱うなら継承しても良いかもしれない。 要はケースバイケースだ。 興味があれば識者が書いた本を参考にするとよい(注1)


知史

「今日は大分コンポジションの肩を持ったから、次回は継承の活躍について話をしよう」


航祐

「ありがとうございました」


(注1) 例えば『UMLによるJavaオブジェクト設計 第2版』(ピーターコード著、Pearson Education)の中で著者は、オブジェクトコンポジションではなく継承を使用した方がよい条件として以下を挙げている。

  1. 「何々の特別な種類」であって「何々によって果たされる役割」ではない場合。
  2. 何か別のクラスのオブジェクトに変形する必要は生じない場合。
  3. スーパークラスを拡張するのであって、スーパークラスをオーバライドまたは無効化するものではない場合。
  4. ユーティリティクラス(何度も利用したい便利な機能を持つクラス)に過ぎないクラスをサブクラス化するのではない場合。
  5. PD(問題領域)の中では役割、トランザクション、または物の特別な種類を表す場合。