55
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Goやるなら知っておきたい「Composition over inheritance」

Posted at

Goを勉強している中で、「Composition over inheritance」という概念が出てきました。ちゃんと理解していなかったので、ここで改めて掘り下げます。

特に、普段 Ruby や Rails を書いている初中級者の方は、Go を勉強する前におさらいしておくことをオススメします。なぜなら、Go では、Rubyで馴染みのある Inheritance(継承)ではなく、Composition(合成)のみが使われるからです。

「Composition over inheritance」とは

「Composition over inheritance」は、日本語だと「継承より合成」と表現されます。

これは、オブジェクト思考プログラミングにおいて、親クラスやベースクラスを「継承」するよりも、「合成」によってコードを共通化・再利用する方が望ましい、という考え方です。

つまり、継承より合成の方が良いということです。

それを理由に、Go では合成のみが採用されており、型の継承はできません

Composition と Inheritance の比較

Composition と Inheritance、それぞれについておさらいします。

両者の対比は、次のようにまとめられます。

英語 日本語 型の関係 考え方の例
Composition 合成 has-a ソファには綿が入っている
Inheritance 継承 is-a ソファは家具である

具体的なコード例

文字だけでは抽象的なので、具体的なコード例で考えてみましょう。

ここでは、挨拶ができる「英語圏の人」と「日本人」をコードで表してみます。

Composition と Inheritance を対比させたいので、どちらも表現しやすい Ruby を用います(独断と偏見)。

Inheritance の例

Inheritance で設計する場合、まず「挨拶ができる」スーパークラスとして Person クラスを定義し、greet メソッドを持たせます。

person.rb
class Person
  def greet
    print word
  end
end

その Person クラスを継承する English クラスを定義します。

english.rb
class English < Person
  def word
    'Hello!'
  end
end

同様に Japanese クラスも定義します。

japanese.rb
class Japanese < Person
  def word
    'こんにちは!'
  end
end

EnglishJapanese、どちらでも挨拶できます。

e = English.new
e.greet # 'Hello!'

j = Japanese.new
j.greet # 'こんにちは!'

Inheritance は、普段 Ruby・Railsを書いてる人にとって、ごく一般的で馴染みのある設計だと思います。

Composition の例

Composition で設計する場合、「挨拶ができる」ことに着目し、そのビジネスロジックを Greeting クラスとして切り出します。(モジュールを include するやり方は、クラス継承ツリーに含まれてしまうので、Composition の純粋な例としては使えません。)

greeting.rb
class Greeting
  def greet(word)
    print word
  end
end

その Greeting クラスを English クラスに持たせます(合成します)。

english.rb
class English
  attr_reader :greeting

  def initialize
    @greeting = Greeting.new
  end

  def greet
    greeting.greet(word)
  end

  def word
    'Hello!'
  end
end

同様に Japanese クラスも定義します。

japanese.rb
class Japanese
  attr_reader :greeting

  def initialize
    @greeting = Greeting.new
  end

  def greet
    greeting.greet(word)
  end

  def word
    'こんにちは!'
  end
end

これで先ほどと同様に、EnglishJapanese どちらでも挨拶できます。

e = English.new
e.greet # 'Hello!'

j = Japanese.new
j.greet # 'こんにちは!'

Composition の例 (Go ver.)

参考として、Go で上の Composition を表すとこのようになります → こちら (Go Playground)

なぜ、Compositionが良いのか?

ここからが本題です。

なぜ「Composition over inheritance」、つまり、継承より合成の方が良いのでしょうか?

Composition のメリット

一般的に考えられているメリットは次の2つです。

1. 考えやすい

Composition の場合、その共通化させるビジネスロジック(上の例では、挨拶できること)に着目してコードを設計します。そのため、Inheritance の場合と違って、抽象化させるためのスーパークラス名や、継承ツリーの構成などに頭を悩まされることはありません。

2. 変更しやすい

コードに変更を加える場合、Composition では、そのビジネスロジックを持っている当事者のみが影響を受けます。また、あるクラスにビジネスロジックを追加する場合も、Composition として共通化していれば簡単に対応できます。

一方 Inheritance では、スーパークラスのコードを変更すると、その継承ツリーの下位クラス全体に影響があります。そのため、スーパークラスに新しい振る舞いを追加することは、意図しないエラーを生むリスクがあります。

例えば、挨拶できるロボットがいた場合...

Composition だと、新たな Robot クラスに Greeting クラスを持たすだけでOKです。

一方 Inheritance では、RobotPerson ではないので、スーパークラス名や継承ツリーの構成を再検討しないといけません。また、スーパークラスに変更を加えると、その下位クラスで意図しないエラーが起きるリスクがあります。

Composition のデメリット

一般的に考えられているデメリットは1つで、合成したメソッドを呼ぶためのメソッドを書かないといけないことです。上の Ruby の例で言う、English#greetJapanese#greet ですね。

一方の Inheritance では、継承したスーパークラスのメソッドをそのまま呼び出せます。したがって、スーパークラスに基本的なビジネスロジックを持たせておけば、Composition よりも少ないコードで同じ振る舞いを実装することが可能です。(ただし、何でもかんでも詰め込むと「神クラス」となって収集がつかなくなるので注意!)

デメリットの回避

この Composition のデメリットを回避するために、Go では Embedding types が用いられます。

先ほどあげた Go Playground のコード例でも Greeting タイプを EnglishJapanese タイプに組み込むことで、それぞれのタイプで greet メソッドを定義することを回避しています。

// 一部抜粋
type Greeting struct{}

type English struct {
	Greeting
}
type Japanese struct {
	Greeting
}

まとめ

継承は、抽象化したコードで効率的な実装ができるので、変更の可能性が少ない部分などにはとても有効な設計手法です。そして、Ruby を使っている人にとっては、とても馴染みがあります。

しかし、Go では、合成のみが採用されており、型の継承はできません

この言語仕様の違いを、背景にある「Composition over inheritance」と一緒にしっかり理解しておけば、より早く Go の世界に馴染むことができるでしょう。

Sources

55
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?