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

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` メソッドを持たせます。

```ruby:person.rb
class Person
  def greet
    print word
  end
end
```

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

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

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

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

`English`・`Japanese`、どちらでも挨拶できます。

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

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

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

#### Composition の例

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

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

その `Greeting` クラスを `English` クラスに持たせます（合成します）。

```ruby: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` クラスも定義します。

```ruby:japanese.rb
class Japanese
  attr_reader :greeting

  def initialize
    @greeting = Greeting.new
  end

  def greet
    greeting.greet(word)
  end

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

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

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

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

#### Composition の例 (Go ver.)

参考として、Go で上の Composition を表すとこのようになります → [こちら (Go Playground)](https://play.golang.org/p/VAGqfevadtc)

# なぜ、Compositionが良いのか？

ここからが本題です。

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

## Composition のメリット

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

### 1. 考えやすい

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


### 2. 変更しやすい

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

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

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

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

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

## Composition のデメリット

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

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

### デメリットの回避

この Composition のデメリットを回避するために、Go では [Embedding types](https://golang.org/doc/effective_go.html#embedding) が用いられます。


先ほどあげた [Go Playground](https://play.golang.org/p/VAGqfevadtc) のコード例でも `Greeting` タイプを `English`・`Japanese` タイプに組み込むことで、それぞれのタイプで `greet` メソッドを定義することを回避しています。

```go
// 一部抜粋
type Greeting struct{}

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

# まとめ

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

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

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

# Sources

* [Composition over inheritance - Wikipedia](https://en.wikipedia.org/wiki/Composition_over_inheritance)
* [Why Go’s structs are superior to class-based inheritance - Ian Macalinao](https://medium.com/@simplyianm/why-gos-structs-are-superior-to-class-based-inheritance-b661ba897c67)
* [オブジェクト指向と10年戦ってわかったこと - @tutinoco](https://qiita.com/tutinoco/items/6952b01e5fc38914ec4e)
* [継承より合成ってなに？ - Mastering Python](https://python.ms/namespace/composition-over-inheritance/#%E7%B6%99%E6%89%BF%E3%82%88%E3%82%8A%E5%90%88%E6%88%90%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%AB%EF%BC%9F)
* [Ruby : Composition over Inheritance because The Force is Strong with Composition - Kartik Jagdale](https://medium.com/aviabird/ruby-composition-over-inheritance-3ff786ad9e5d)
* [I would love to see some composition examples - Ruby Chat](https://metaruby.com/t/i-would-love-to-see-some-composition-examples/265/3)