🐞

SOLID原則メモ

2022/12/15に公開
名前 日本語 何がダメか? 改善方法
Single Responsibility 単一責任 1つのクラスに複数の機能を入れた クラス分割
Open Closed 開放閉鎖 あっちこっち変更しないといけない ストラテジーパターンなど
Liskov Substitution リスコフの置換 継承後にまったく違う機能にした 親クラスの方針を察する
Interface Segregation インタフェース分離 抽象クラスが複雑(とは?) 抽象クラスを分割
Dependency Inversion 依存関係逆転 クラスに依存 同じメソッドを持っていれさえすればいい

単一責任

1つのクラスに複数の機能を入れないようにしようという話。

問題があるとされているコード
class Foo
  def a
    "機能A"
  end

  def b
    "機能B"
  end
end

Value Object は良い例。その逆は神クラス。とはいえ、分割しすぎるとクラス爆発してしまう。たしかに単一責任は大切だがバランスや効率を見極めないといけない。

開放閉鎖

  • 解放 → 拡張しやすい
  • 閉鎖 → 拡張しても元のコードに影響がない

という話だけど逆に強迫観念に駆られないようにしたい。そうしないと際限がない。どこを拡張しやすくするか絞らないといくらでも拡張しやすくできてしまう。YAGNI原則から言えば拡張する必要が出てきてからやっと拡張しやすくするぐらいでよい。

元のコード
class Player
  def roll
    3.times.collect { rand(1..6) }
  end
end
今すぐやる必要のなかったストラテジーパターン
class Dice
  def roll
    rand(1..6)
  end
end

class Player
  def initialize(dice)
    @dice = dice
  end

  def roll
    3.times.collect { @dice.roll }
  end
end

リスコフの置換

サブクラスを親クラスに置き換えても動かないといけない。そうしないと不整合が起きるから。例えば次のように文字列を返すと決まっているのに数値を返してはいけない。

問題があるとされているコード
class Foo
  def to_s
    1
  end
end

──ということかと思ったが、SOLIDの図では「珈琲」を出すクラスのサブクラスで「水」を出すなと説明していてわりと主観的な内容だった。

インタフェース分離

いまいちピンとこないけど関係ないものを入れるなということだと思われる。

問題があるとされているコード
module NandemoHelper
  def x
  end

  def y
  end
end

# x を使いたいだけのクラスに y まで入っている
class A
  include NandemoHelper
end

たしかにそうだけど影響が出てから直すぐらいでいいんじゃないかな(小声)

依存関係逆転

問題があるとされているコード
class A
  def foo
    B.new.bar
  end
end

class B
  def bar
  end
end

まず逆転とはなんだろうか? 私は、上のように A が B に依存している点を「逆転(してしまっている)」と表現し「依存関係(を)逆転(させてはいけない)」と主張しているのだと思っていたので混乱してしまった。

まず A は B に依存しているので依存関係は A → B になる。それを A → I ← B にしろと言っている。B に向かってくる矢印が逆になっている点を指して「逆転(させるべき)」と表現している。体言止めよくない。

次に I とはなんだろうか? Java ではインターフェイスを指している。B にかますことで A から I っぽいやつを呼べるようになるという仕組みだけど、ダックタイピングな言語から見ればそもそも何の悩みがあったのかよくわからない。したがって I は単に bar メソッドを呼べるもの と置き換えるとわかりやすい。

仰々しい名前がついているけど要は「クラスをハードコーディングしなければ融通が効く」というだけの話だった。また余計なことを書くとYAGNI原則により A が B しか使わないのであれば A → B で問題ない。

参照

https://ja.wikipedia.org/wiki/SOLID
https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898

Discussion