はじめに
以前、SOLID 原則のであるオープンクローズドの原則について以下でまとめました。
今回は、単一責任の原則について実際のコードを交えながら説明していきます。
単一責任の原則
単一責任の原則は SOLID 原則の S の部分であり、英語ではSingle Responsibility Principleと呼ばれます。
クラスは 1 つの責務だけを持つべきであり、それ以上の責務を持つべきできではないというのがこの原則です。
言い換えると、クラスは一つの一つのアクター(クラスを利用するユーザーやステークホルダー)に対して
責任を持つべきということです。
悪い例
悪い例から見ていきましょう。
クラス図は以下のようなものになります。
Animal クラスは飼育員、獣医、研究者の 3 つのアクターに対して責任を持ってますね...
さて、コードは以下の通りです。
class Animal {
constructor(public name: string, public species: string) {}
// 飼育員がアクター
feed() {
this.getFood();
console.log(`${this.name}に餌を与えました`);
}
// 獣医がアクター
checkHealth() {
this.getFood();
console.log(`${this.name}の健康状態をチェックしました`);
}
// 研究者がアクター
saveData() {
console.log(`${this.name}のデータを保存しました`);
}
private getFood() {
// 仕様変更前
console.log("飼育員と獣医の共通のロジック");
// // 仕様変更後
// console.log("飼育員の仕様変更済み");
}
}
function run() {
const animal = new Animal("ライオン", "哺乳類");
console.log("飼育員");
animal.feed();
console.log("");
console.log("獣医");
animal.checkHealth();
}
run();
上記を実行して出力するとコンソール上に以下の実行結果が出力されます。
飼育員
飼育員と獣医の共通のロジック
ライオンに餌を与えました
獣医
飼育員と獣医の共通のロジック
ライオンの健康状態をチェックしました
逆に、仕様変更前のものをコメントアウトして、
仕様変更後と書かれているところをコメントアウトを解除すると...
飼育員
飼育員の仕様変更済み
ライオンに餌を与えました
獣医
飼育員の仕様変更済み
ライオンの健康状態をチェックしました
当然ですが、獣医側で利用している getFood()
メソッドも変更されてしまいます。
今回は、すぐ近くで利用しているかつ単純なため気づくことができすが、
実際のサービスは複雑であるため、多くの箇所で利用されている場合原因を特定するのも苦労します。
良い例
良い例を見ていきましょう。
class Animal {
constructor(public name: string, public species: string) {}
}
class Keeper {
private getFood() {
console.log("飼育用の餌を取得するロジック");
}
feed(animal: Animal) {
this.getFood();
console.log(`${animal.name}に飼育員が餌を与えました`);
}
}
class HealthCoordinator {
private getFood() {
console.log("健康を維持するための餌を取得するロジック");
}
checkHealth(animal: Animal) {
this.getFood();
console.log(`${animal.name}の健康状態をチェックしました`);
}
}
class AnimalRepository {
saveData(animal: Animal) {
console.log(`${animal.name}のデータを保存しました`);
}
}
const run = () => {
const animal = new Animal("猫", "哺乳類");
const keeper = new Keeper();
const healthCoordinator = new HealthCoordinator();
console.log("飼育員");
keeper.feed(animal);
console.log("");
console.log("獣医");
healthCoordinator.checkHealth(animal);
};
run();
飼育員
飼育用の餌を取得するロジック
猫に飼育員が餌を与えました
獣医
健康を維持するための餌を取得するロジック
猫の健康状態をチェックしました
良い例では、getFood
が独立しているので各アクターごとに他に影響がなく修正することができます。
このように、アクターごとにクラスを分割することで、単一責任の原則に違反しないより安全で変更しやすい設計ができるようになります。
終わりに
いかがでしたでしょうか。
今回、アドベントカレンダーということもあり SOLID 原則について
1つずつ解説していこうと思います。
また、実際にサンプルコードで原則に違反しているパターンと違反していないパターンを書くと定着も早いと思うので是非試してみてください!
参考資料