SOLID原則メモ
名前 | 日本語 | 何がダメか? | 改善方法 |
---|---|---|---|
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
で問題ない。
参照
Discussion