Clean Architecture 達人に学ぶソフトウェアの構造と設計を読んで、オブジェクト指向の原則について学びがあったので、Rubyのコード例と共に内容を整理してみました。
Clean Architecture 達人に学ぶソフトウェアの構造と設計
- 作者: Robert C.Martin,角征典,高木正弘
- 出版社/メーカー: KADOKAWA
- 発売日: 2018/07/27
- メディア: 単行本
- この商品を含むブログを見る
※私の学びのメモなので理解が間違ってる可能性があります、間違ってたらすいません🙇♂️
リスコフの置換原則(Liskov substitution principle)とは?
プログラムの中にある任意のオブジェクトは、プログラムの正しさを変化させることなく、そのサブクラスのオブジェクトと置換できなければならない。 https://ja.wikipedia.org/wiki/SOLID
リスコフの置換原則は、サブクラスはスーパークラスの期待する要件を満たし、サブクラスのオブジェクトはスーパークラスのオブジェクトとしても振る舞えるように設計すべきという原則ですね。
単一責任の原則の例とコード
リスコフの置換原則を意識できていないコードと例
例えば下記のような、社員を扱うClass群があったとします。社員全体に関連するEmployee
、それを継承した管理者用とメンバ用のClassがそれぞれあるという構成です。
class Employee def initialize(attributes) # build employeer object. end def work # do work. end end class Manager < Employee def initialize(attributes) super @subordinates = attributes[:subordinates] # build employeer object. end def manage end end class Member < Employee def initialize(attributes) super @manager = attributes[:manager] # build employeer object. end def report end end
このようなClass群に対して、「メンバは正社員とは限らないのでパートの場合は別の振る舞いをさせたい」という仕様変更が入ったのでMember
を変更しました。
class Member < Employee def initialize(attributes) super @manager = attributes[:manager] @temporary = attributes[:temporary] # build employeer object. end def work if temporary super else temporary_work # diffarent progress... end end def report end end
一見良さそうに見えますが、Employee
として振る舞うことが期待されているMember
がtemporary
の値によっては全く違う挙動をするようになっていました。
この状態ではEmployee
のサブクラスでもtemporary
がtrue
のものに関しては除外するといった処理がアプリケーション内の様々なところで発生して、修正漏れや考慮漏れを誘発しそうです。。。
このような状態がリスコフの置換原則が守られていない状態と言えそうです。
リスコフの置換原則を意識してリファクタリングしてみる
全く違う挙動をするのであれば、そもそも別物だと思いますのでEmployee
を継承せずに別のClassとして実装してあげたほうが良さそうですね。
class PartTimer def initialize(attributes) # build TemporaryMember object end def work # diffarent progress... end end
また、あくまでEmployee
のサブクラスとして扱いたい場合はwork
メソッドはサブクラスに実装するものとして、Employee
のサブクラスクラスは同様の振る舞いをするということは期待せずに設計を見直して上げたほうがよさそうです。
class Employee def initialize(attributes) # build employeer object. end end class Manager < Employee def initialize(attributes) super @subordinates = attributes[:subordinates] # build employeer object. end def work end def manage end end class Member < Employee def initialize(attributes) super @manager = attributes[:manager] @temporary = attributes[:temporary] # build employeer object. end def work end def report end end class PartTimer < Employee def initialize(attributes) super @manager = attributes[:manager] @temporary = attributes[:temporary] # build employeer object. end def work end def report end end
サブクラスと定義している以上、スーパークラスに期待されている振る舞いは満たした上で拡張するようにしてあげると、ポリモーフィックにきれいに書けたりとメリットが非常に大きそうですね🙌
おわりに
今回はオブジェクト指向のSLID原則「リスコフの置換原則(Liskov substitution principle)」について自分の理解をRubyのコード例とともに説明してみました。
同様の振る舞いを期待しているはずなのにif文等でイレギュラーな処理をいれていくと、リスコフの置換原則に違反し、どんどんサブクラスとスーパークラスの関係が崩れていって、ぐちゃぐちゃになってしまいそうなので注意ですね💦
次回は「インターフェイス分離の原則(Interface segregation principle)」について書いてみようかと思います 🙇♂️
参考
Clean Architecture 達人に学ぶソフトウェアの構造と設計
- 作者: Robert C.Martin,角征典,高木正弘
- 出版社/メーカー: KADOKAWA
- 発売日: 2018/07/27
- メディア: 単行本
- この商品を含むブログを見る