Zenn
🐈

"いい"コードとは 3(SOLIDの原則 I 編)

2025/04/05に公開

前回の記事を見ていない人は、

https://zenn.dev/fuji_1218/articles/87e986a0d440a7
https://zenn.dev/fuji_1218/articles/1bfd429f134dda

から見てくれると助かる。

Interface Segregation Principle (インターフェース分離の原則)

結論から言うと、
「抽象クラスやインターフェース継承先で使わないメソッドがないようにわけよう」
ということ。

つまるところ、余計な結合をなくしましょうということである。

具体例

毎度のとおり、犬猫でたとえてみよう。

//動物の行動
interface AnimalTasks {
  walk(): void;
  fly(): void;
  swim(): void;
}

このようなanimalの行動がまとめられていたインターフェースがあるとする。

こいつを継承するサブクラスが、以下のとおりとする。

//🐶Dogの実装
class Dog implements AnimalTasks {
  walk() {
    console.log("てくてく歩くわん");
  }

  fly() {
    throw new Error("ぼく飛べませんけど!?💢");
  }

  swim() {
    console.log("頑張って泳ぐわん");
  }
}
//🐱Catの実装
class Cat implements AnimalTasks {
  walk() {
    console.log("にゃ〜ん、のそのそ");
  }

  fly() {
    throw new Error("飛ばないよ!にゃんこよ!?");
  }

  swim() {
    throw new Error("泳げないの!水キライなの!");
  }
}
// 🐦Birdの実装
class Bird implements AnimalTasks {
  walk() {
    console.log("ちょこちょこ歩くちゅん🐦");
  }

  fly() {
    console.log("ばさばさ飛ぶちゅん✨");
  }

  swim() {
    throw new Error("水に浮かないよ!ばちゃばちゃ!");
  }
}

全ての行動持ったインターフェースを継承してしまうと、本来空を飛べないdogが、「飛ぶ」といったり、猫が「泳ぐ」といったような振る舞いを獲得してしまう。

こうなってしまうと、サブクラスで本来あり得ない行動をとることができ、バグの原因につながる。

ではどうすればいい?

インターフェースを細かく分け、それぞれの振る舞いに対してのみ、適用する。

行動はそれぞれインターフェースを分けて置き、それぞれのクラスに個別に継承する。

interface Walkable {
  walk(): void;
}

interface Swimmable {
  swim(): void;
}

interface Flyable {
  fly(): void;
}
class Dog implements Walkable, Swimmable {
  walk() {
    console.log("てくてく歩くわん🐾");
  }
  swim() {
    console.log("ちゃぷちゃぷ泳ぐわん💦");
  }
}
class Cat implements Walkable {
  walk() {
    console.log("のそのそ歩くにゃん🐾");
  }
}
class Bird implements Walkable, Flyable {
  walk() {
    console.log("ちょこちょこ歩くちゅん🐤");
  }
  fly() {
    console.log("ばさばさ飛ぶちゅん✨");
  }
}

これにより、依存関係を最小に抑えることができ、変更に強く、理解しやすい設計になる。

結論

抽象クラスやインターフェース継承先で使わないメソッドがないように分けることで、保守性が高い設計になる。

インターフェースを細かく分類し、依存関係を減らすことで、リスクを減らしていこう。

Discussion

ログインするとコメントできます