Madogiwa Blog

主に技術系の学習メモに使っていきます。

Rubyで学オブジェクト指向 SOLID原則「開放閉鎖の原則(Open–closed principle)」

Clean Architecture 達人に学ぶソフトウェアの構造と設計を読んで、オブジェクト指向の原則について学びがあったので、Rubyのコード例と共に内容を整理してみました。

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Clean Architecture 達人に学ぶソフトウェアの構造と設計

※私の学びのメモなので理解が間違ってる可能性があります、間違ってたらすいません🙇‍♂️

開放閉鎖の原則(Open–closed principle)とは?

ソフトウェアのエンティティは(中略)拡張に対して開かれていなければならないが、変更に対しては閉じていなければならない。 https://ja.wikipedia.org/wiki/SOLID

この原則は、既存の振る舞いを変更することなく機能の拡張を行えるように設計するという原則ですね。

単一責任の原則の例とコード

開放閉鎖の原則を意識できていないコードと例

例えば下記のようなファイルパスとファイル形式を受け取って、パースした何らかのオブジェクトを返すようなClassがあったとします。

Parser.call('./records.csv', :csv)
Parser.call('./records.json', :csv)

class Parser
  def self.call(file_path, type)
    case type
    when :csv  then parse_from_csv
    when :json then parse_from_json
    end
  end

  def parse_from_csv
    # return parsed object.
  end

  def parse_from_json
    # return parsed object.
  end
end

このClassに「変換対象としてXML形式を追加する」といった対応が発生した場合はどうでしょうか? 引数でファイル形式に依存した実装が含まれているため、ファイル形式が追加されるたびに実装の修正が必要そうですね・・・!

開放閉鎖の原則を意識してリファクタリングしてみる

以下のようにレコードの項目のhashを引数にParser.callを呼び出すような実装にするとどうでしょうか?

csv_hashes = CSV.read('./records.csv').map(&:to_h)
json_hashes = JSON.parse(File.read './records.csv').map(&:to_h)

Parser.call(csv_hashes)
Parser.call(json_hashes)

class Parser
  def call(hashes)
    # rturn parsed objects.
  end
end

Hashのオブジェクトを使って抽象化して渡すというルールを守ることでParserクラスはどんなファイル形式でも変更が必要なくなりましたね。 また、オブジェクトを作るという部分に関して関心を持っていないので、オブジェクトの作成方法が変わってもParserのコードを修正する必要はありません。

このように直接ではなく抽象化した共通なものを使うことによって実装を変更することなく拡張することが出来ますね🙌

おわりに

今回はオブジェクト指向のSLID原則「開放閉鎖の原則(Open–closed principle)」について自分の理解をRubyのコード例とともに説明してみました。

開放閉鎖の原則を意識してコードを書くと、ソフトウェアの拡張性を担保して汎用性の高いコードを書くことができそうですね!

次回は「リスコフの置換原則(Liskov substitution principle)」について書いてみようかと思います 🙇‍♂️

参考

Clean Architecture 達人に学ぶソフトウェアの構造と設計

Clean Architecture 達人に学ぶソフトウェアの構造と設計

marupeke296.com