SOLID原則の単一責任原則(SRP)についてRubyで解説
プログラミング #ruby #SOLID #オブジェクト指向 #クリーンコードオブジェクト指向プログラミングにおいて、コードの保守性、可読性、再利用性を高めるために、SOLID原則と呼ばれる5つの重要な設計原則があります。
今回は、このSOLID原則のうち、単一責任原則 (Single Responsibility Principle, SRP)についてRubyのサンプルコードを用いて解説していきます。
SOLIDとは
SOLIDとは、5つの原則の頭文字を取った略語です。
- Single Responsibility Principle(SRP): 単一責任原則
- Open Closed Principle(OCP): オープン・クローズドの原則
- Liskov Substitution Principle(LSP): リスコフの置換原則
- Interface Separation Principle(ISP): インターフェース分離の原則
- Dependency Inversion Principle(DIP): 依存性逆転の原則
単一責任原則 (Single Responsibility Principle, SRP)とは
単一責任原則は、たいへんシンプルで1つのクラスは1つの責務のみを持つべきであるという原則です。
つまり、クラスは1つの機能や目的に特化し、それ以外の責任を持たないようにします。
これにより、機能ごとにクラスを分離することができ、変更を加える際に関係のない箇所へ意図しない影響を与えないようにすることが出来ます。
ここで疑問となるのが、どこまでを一つの責務とすべきか?だと思います。
こちら解説していきます。
責務の定義方法
これはニーズによって定義出来ると考えております。
例えばですが、外部APIを使用してデータを作成・読み込みが出来る以下のクラスがあったとします。
class SampleApiClient
URL = 'https://example.com/api/v1'.freeze
def initialize
# URLを元にを初期化
end
def fetch(**args)
# ...
end
def create(**args)
# ...
end
end
class SampleUseCase
def initialize(api_client: SampleApiClient.new)
@api_client = api_client
end
def execute
@api_client.fetch
@api_client.create
end
end
こちら SampleApiClient
にはfetch
とcreate
があり、機能としては2つ存在しています。
これを単一の責務にしなきゃとクラスを分けると、もし、バージョンが変わった場合、利用する側は分けたクラス分だけ変更を強いられます。(URL
定数で定義されているURL先のAPIを使用する際、fetch
もcreate
も同じバージョンでないといけないという前提で説明)
この場合は、SampleApiClient
の責務を「外部API用のインターフェースを提供」という風に定義し、クラスは分けないほうが扱いやすいです。
今回の例のように、クラスを利用する側がどんな時に「別のクラスや新しいバージョンに交換したいか」や「期待する処理が変わるか」を想像することが大事です。この想像こそが、責務を定義する際に役に立ちます。
責務を分けたほうが良い例
以下のような場合は分けた方が良いです。
なぜなら、下書きと公開処理は分かれていても問題がないのと、将来的に変更が入るのが想像でき、分かれていたほうが影響を考慮しなくて良く変更しやすいです。
class PostOperator
class << self
def draft(post, writer)
# ...
end
def publish(post, manager)
# ...
end
end
end
# 下書き作成画面からの呼び出し
PostOperator.draft(post, writer)
# 公開画面からの呼び出し
PostOperator.publish(post, manager)
分けるとしたら以下のようになると思います。
class DraftOperator
def self.call(post, writer)
# ...
end
end
class PublishingOperator
def self.call(post, manager)
# ...
end
end
# 下書き作成画面からの呼び出し
DraftOperator.call(post, writer)
# 公開画面からの呼び出し
PublishingOperator.call(post, manager)
こうすることで、下書きを作成する時の事情が変わったけれど、公開する時は従来通りということが実現できます。
これは今は処理が少ないので、そこまでメリットが感じにくいと思いますが、様々な条件分岐が入って処理が複雑になったり、private
なメソッドが増えてくると、クラスが分かれている方が他の処理への影響を意識しなくて良いため管理がしやすいです。
最後に
これはクラスではなく、メソッド単位でも同じことが言える原則だと思います。
単一責任原則を意識してコーディングすると、一つの責務だけに意識を集中できるため、テストコードを書く際の負担も減り、結果保守性を向上出来ます。
単一責任原則を習得し、保守性の高いシステムを開発していけると良いですね。