RIT Tech Blog

株式会社RITのエンジニアが知見を共有する技術ブログです。

単一責任の原則を意識してリファクタしてみてよかったこと

エンジニアの前田です。

概要

単一責任の原則とクラス名を適切につけることを意識してリファクタしたので感想と疑似コードでのリファクタリング過程を共有します。

単一責任の原則とは

オブジェクト指向で用いられる五つの原則の頭字語である、SOLIDのうちSの部分です。
ざっくりと説明すると、一つのクラス・関数は、一つの明確な役割を持つべきだという原則になります。

責任を明確にしていないことによる弊害

単一責任の原則を意識する前は、一つのクラスに複数の役割を持たせてしまっていたり、クラス名とそのクラスの役割が違ってしまっていました。
その結果、クラスの使用方法を間違えてしまうなどの問題が起きてしまっていました。 例えば、リファクタ以前は以下のようなクラスが存在していたとします。(疑似コードなのでエディタにコピーしても動きません)

class 炭酸ジュースサービス {
  invoke() {
    return createJuice(); // ジュースを作る関数
  }
}

炭酸ジュースサービスなので、本来は炭酸ジュースに関することをしていないといけないのですが、実際はジュースを作成していました。
ただ、システム上の挙動は上手くいっていたのでなぜだろうと思い、調査をしたところ以下のようなクラスが別にありました。

class 炭酸ジュース
  async invoke() {
 const juice = createJuice(); // ジュースを作る関数
    await otherFunction(); // 炭酸ジュースを作る上で全く必要のないことをしている関数
    return createCarbonJuice(); 炭酸ジュースを作る関数
  }

リファクタ前のクラスをまとめると以下のようになります。

  • 炭酸ジュースサービス -> ジュースを作成するクラス

  • 炭酸ジュース -> 炭酸ジュースを作るクラス、ただし炭酸ジュースを作る上で全く必要のないことを実行している箇所がある

これだとクラス名を見て何をしているクラスかが分からないので困りますね。
例えば、炭酸ジュースサービスクラスという名前を見ただけでは、何をしているクラスなのかが分かりません。
また、炭酸ジュースクラスを使ったときに、炭酸ジュースを作ることができるのはいいものの、炭酸ジュースを作る上で全く必要のないことをしてしまっています。
それを意識していないままクラスを使ってしまうと、例えばそれがデータベースに何かを追加するような処理だった場合に、意図せずデータが書き換わってしまって後々不整合が起きてしまいます。

リファクタ後

正しい形にリファクタしなおしました。

  • Createジュースサービス -> ジュースを作成するクラス

  • Create炭酸ジュース -> 炭酸ジュースを作るクラス、ただし炭酸ジュースを作る上で全く必要のないことを実行している箇所は別の場所に移しました。

class Createジュース {
  invoke() {
    return createJuice(); // ジュースを作る関数
  }
}

class Create炭酸ジュース
  async invoke() {
 const juice = new Createジュース.invoke()
    return createCarbonJuice(juice); ジュースを引数にとって炭酸ジュースを作る関数
  }
}

以上がリファクタ後のクラスです。
まず、Createジュースクラスですが、クラス名を見ただけで何をするクラスなのかが分かりますね。
次に、Create炭酸ジュースクラスですが、クラス名を見ただけで何をするクラスなのかが分かる上に、炭酸ジュースを作る上で全く必要のないことを実行している箇所を排除したので、安全に使えるクラスになりました。

最後に

クラスの命名と役割の明確化は初期設計時にちゃんと決めておいた方がいいと思いました。