💡

"いい"コードとは 4(SOLIDの原則 D 編)

に公開

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

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

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

Dependency Inversion Principle (依存関係逆転の原則)

wikipedeaでは、

  1. 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも抽象(例としてインターフェース)に依存するべきである。
  2. 抽象は詳細に依存してはならない。詳細(具象的な実装内容)が抽象に依存するべきである。

となっている。

簡単に言うと...

上位モジュール(=偉い側)は、下位モジュール(=具体的なやつ)に依存してはいけないという原則である。
よく言う、自分より抽象度が高いものに依存しようといった設計である。

さっさと具体例に進もう。

class Dog {
  bark() {
    console.log("わんわん!🐶");
  }
}

class PetHouse {
  private dog: Dog;

  constructor(dog: Dog) {
    this.dog = dog;
  }

  makeSound() {
    this.dog.bark();
  }
}

これだと、petHouseはDog クラスに完全に依存していると、catがpetHouseを使えないことになってしまう。

依存関係を“逆転”

抽象(インターフェース)を作る!

interface Pet {
  makeSound(): void;
}
class Dog implements Pet {
  makeSound() {
    console.log("わんわん!🐶");
  }
}
class Cat implements Pet {
  makeSound() {
    console.log("にゃーにゃー!🐱");
  }
}
class PetHouse {
  private pet: Pet;

  constructor(pet: Pet) {
    this.pet = pet;
  }

  makeSound() {
    this.pet.makeSound();
  }
}

Petインターフェースに依存関係を向けることで、dogとcatがpetHouseを使えるようになる。

const dog = new Dog();             // Dog型のインスタンス
const cat = new Cat();             // Cat型のインスタンス

const dogHouse = new PetHouse(dog);  // 🐶→Petとして受け取る
dogHouse.makeSound();               // 🔊「わんわん!🐶」

const catHouse = new PetHouse(cat);  // 🐱→Petとして受け取る
catHouse.makeSound();               // 🔊「にゃーにゃー!🐱」

犬猫の違いに振り回されるな!ペットという抽象に頼れ!

つまりは、
ペットハウスは“ペット”ってことだけ分かってればよくて、中にいるのが犬か猫かなんて気にしないで動けるということである。

あえて抽象度が高いpetをわざわざ入れることにより、そこに依存関係を向かわせることで、結果的に抽象度を上げた実装が可能になる。

※例として挙げた、makeSoundだが、PetHouseで定義したmakeSoundがそれぞれの犬や猫が呼び出すことができ、クラスが違うオブジェクトでも
処理を同じようによびだすことができることをポリモーフィズムという。

Discussion