オブジェクト指向設計実践ガイド 第2章 「単一責任のクラスを設計する」

### 「[オブジェクト指向設計実践ガイド](https://www.amazon.co.jp/dp/B01L8SEVYI)」 <br>第2章 「単一責任のクラスを設計する」 のまとめです

---

## 心に残ったことば

### *設計とは、アプリケーションの可変性を維持するために技巧を凝らすことであり、**完璧を目指すための行為ではない*

---

### 今回は「単一責任の原則」についてです

---

## 単一責任の原則（Single Responsibility Principle）とは？

- クラス、**メソッド**はそれぞれ一つの責任（役割）に徹すること
    - メソッドの単一責任が大事！！
- 一つの責任に徹することで、
  - ✅変更の副作用が少なくなる
  - ✅変更を取り込みやすくなる
  - ✅再利用しやすくなる
- 単一責任のクラス、メソッドは変更に強いアプリケーションの大前提

---

## クラスに属するものを決める
- 最も目立つのは「クラス」だが、「メッセージ（メソッド）」こそがオブジェクト指向設計の核
- メソッドをグループに分けクラスにまとめる
- プロジェクト初期の知識量では、正しくグループ分けなんて到底できない
- **変更がかんたんなようにしておくことが大事**

---

## 「変更がかんたん」とは？
- 変更による副作用がない
- 要件の変更が小さければ、コード変更量も小さい
- 既存のコードをかんたんに再利用できる
- コードの追加がかんたん
  - 追加するコードはそれ自体変更が容易なもの

---
## 「変更がかんたん」とは、TRUEなコードのこと
- **T**ransparent（見通しが良い）
    - 変更がもたらす影響が明白
- **R**easonable（合理的）
    - 変更にかかるコストが利益にふさわしい
- **U**sable（利用性が高い）
    - 予期していなかった環境でも再利用できる
- **E**xemplary（模範的）
    - コードに変更を加える人が、**品質を自然と保つ**
- TRUEなコードは、将来的なニーズを満たすために変更しやすい
- TRUEなコードを書くために、明確に定義された**単一の責任を持つ**よう徹底する

---
## なぜ単一責任が重要なのか

- 変更がかんたんなアプリケーションは、再利用がかんたんなクラスで構成されている
- 再利用がかんたんなクラスは、周りとの絡み合いが少ない疎結合なクラスのこと
- 2つ以上の責任（役割）を持つクラスは、クラス「内部」に絡みついてしまい、かんたんに再利用できない
    - 目的の用途と関係ない理由で変更され、アプリケーションを予期せず壊す可能性が高まってしまう
- メソッドも同じ
    - 2つ以上の責任（役割）を持つメソッドは変更に弱い

---

## アプリケーション例：自転車とギア（1/2）

- チェーンリングとコグの歯数を受け取り、ギア比を計算する
- ギアに関するデータ、振る舞いしか無いのでこれは単一責任のクラス

```ruby
class Gear
  attr_reader :chainring, :cog

  def initialize(chainring, cog)
    @chainring = chainring
    @cog = cog
  end

  def ratio
    chainring / cog.to_f
  end
end
```

---
## アプリケーション例：自転車とギア（2/2）
- ギアインチを計算できるようにする
- ギアがタイヤのことを知る必要が出た
    - 単一責任のクラスではなくなった

```ruby
class Gear
  attr_reader :chainring, :cog, :rim, :tire

  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire # <- ギアがタイヤまで扱うっておかしくない？？？
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * (rim + (tire * 2))
  end
end
```

---

## クラスが単一責任かどうか見極める

- クラスの持つメソッドを質問に言い換えてみる
    - 「Gearさん、あなたの比を教えてください」
        - 🙆‍♂️ギアのことを聞いているのでOK
    - 「Gearさん、あなたのタイヤはなんですか？」
        - 🙅‍♂️ギアにタイヤのこと聞くのはおかしい
- 1文でクラスを説明してみる
    - 「それと」が含まれていたら、2つ以上の責任を負っている
    - 「または」が含まれていたら、2つ以上の責任があり、さらにそれらがあまり関係ない

--- 

## 単一責任のクラスじゃないことが分かったら・・
- 即座に設計変更すべきとは限らない
    - Gearクラスは「見通しが良い」、「合理的」な状態
    - Gearへ依存するものがないので問題ない
- 将来どんな要求がくるか分からない
    - 多くの情報が揃うまでできるだけ決定を遅らせるべき
    - そのために、**変更を歓迎するコード**を日頃から書いておくことが大事
- 「いますぐ改善」と「あとで改善」のトレードオフを理解してコストを最小にすることが大事
    - 「早すぎる最適化」に気を付ける

---

## 変更を歓迎するコード(1/3)
- データではなく、振る舞いに依存する
    - attr_readerでインスタンス変数を隠蔽する
    - 外部から直接インスタンス変数にアクセスしない
    - 利用する側からしたらデータでもオブジェクトでもどっちでもよい
- データ構造を隠蔽する
    - HashやArrayのデータに対するインデックスアクセスを色んなところでやらない
    - Structを作ってインデックスアクセスは一箇所に集約する
    - 他ではそのStructのフィールドを利用する

---

## 変更を歓迎するコード(2/3)
- メソッドを単一責任にする
    - 繰り返し処理と繰り返し実行される処理を別のメソッドにする
    - すべてのメソッドが単一の責任を持つようにすることで、クラスの責任を明確にする効果がある
- メソッド内のコードにコメントが必要なら、そのコードをメソッドに抽出する
    - そのメソッド名がコメントの目的を果たす

---
## 変更を歓迎するコード(3/3)
- クラスの余計な責任を隔離する
- GearからWheelを追い出しながら、Wheelに関する決定を遅らせる
    - これ面白いなあと思った

```ruby
class Gear
  attr_reader :chainring, :cog, :wheel

  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @wheel = Wheel.new(rim, tire)
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * wheel.diameter
  end

  Wheel = Struct.new(:rim, :tire) do
    def diameter
      rim + (tire * 2)
    end
  end
end
```

--- 

## まとめ

- 変更が簡単なアプリケーションは、単一責任のクラス、メソッドから構成される
- クラス、メソッドはそれぞれ一つの役割に徹するべき
    - 複数の役割を持つと変更・再利用しづらくなる
- 多くの情報が揃うまで待ち、できるだけ決定を遅らせること
    - そのために、変更に強い単一責任のクラス、メソッドを日頃から意識して書くことが大事
    - 特にメソッドの単一責任が大事!!！

---

### まずは、メソッドの単一責任からはじめましょう！！!

---

### おしまい