【SOLID原則】依存性逆転の原則とは何か

2023/02/28に公開約3,100字

依存性逆転の原則とは

依存性逆転の原則(DIP: Dependency Inversion Principle
SOLIDのDにあたる部分ですね。

高水準のモジュールは、低水準のモジュールに依存すべきではなく、両方のモジュールは、抽象化に依存すべきであるという原則です。

つまり、依存関係を逆転させ、低レベルのモジュールが高レベルのモジュールに依存するようにすることが重要です。

高レベルのモジュールは、その詳細な実装に依存することなく、抽象化されたインターフェースを使用して低レベルのモジュールとやり取りすることができます。

このようにすることで下位モジュールの変更が上位モジュールに影響をあたることを防ぐことができます。

悪いコード(原則に反してい

以下のコードでは、ArticleControllerがArticleService、そしてArticleServiceがArticleRdbRepositoryに依存しています。

これは依存性逆転の原則に違反しています。

class Article {}

class ArticleController {
    private articleService = new ArticleService();

    create(article: Article): Article {
        return this.articleService.create(article);
    }

    findById(id: string): Article {
        return this.articleService.findById(id);
    }
}

class ArticleService {
    private articleRepository = new ArticleRdbRepository();

    create(article: Article): Article {
        return this.articleRepository.create(article);
    }

    findById(id: string): Article {
        return this.articleRepository.findById(id);
    }
}

class ArticleRdbRepository {
    create(article: Article): Article {
        console.log("記事を登録");
        return article;
    }

    findById(id: string): Article {
        console.log(`${id}の記事を検索`);
        return new Article();
    }
}

上述したように原則に異版しているため、以下の問題を孕んでいます

  1. 拡張性の低下
    依存性逆転の原則に違反したコードでは、新しい機能を追加する場合に影響を受ける範囲が広くなります。たとえば、ArticleControllerArticleServiceに依存している場合、新しいサービスを追加する場合には、すべてのArticleControllerを変更する必要があります。
  2. テストの困難さ
    依存性逆転の原則に違反したコードでは、モジュールを単体でテストすることが困難になる場合があります。たとえば、ArticleControllerを単体でテストする場合、ArticleServiceArticleRdbRepositoryも含めてテストする必要があります。

良いコード(原則に則している)

以下の方法を用いることで抽象化した概念に対して依存するので、上位モジュールが下位モジュールに依存することがなくなる

  1. インターフェースを使用する
    上位モジュールは、抽象化されたインターフェースを使用して下位モジュールとやり取りするべきです。これにより、下位モジュールの具体的な実装に依存することなく、上位モジュールを拡張することができます。

  2. 依存関係注入を使用する
    依存関係注入(DI: Dependency Injection)を使用することで、上位モジュールが下位モジュールに依存することを回避することができます。依存関係注入では、上位モジュールが下位モジュールを作成するのではなく、上位モジュールに下位モジュールのインスタンスを注入することにより、依存関係を解決します。

class Article {}

interface IArticleService {
  create(article: Article): Article;
  findById(id: string): Article;
}

class ArticleController {
  constructor(private articleService: IArticleService) {
    this.articleService = articleService;
  }

  create(article: Article): Article {
    return this.articleService.create(article);
  }

  findById(id: string): Article {
    return this.articleService.findById(id);
  }
}

interface IArticleRepository {
  create(article: Article): Article;
  findById(id: string): Article;
}

class ArticleService implements IArticleService {
  constructor(private articleRepository: IArticleRepository) {
    this.articleRepository = articleRepository;
  }

  create(article: Article): Article {
    return this.articleRepository.create(article);
  }

  findById(id: string): Article {
    return this.articleRepository.findById(id);
  }
}

class ArticleRdbRepository implements IArticleRepository {
    create(article: Article): Article {
        console.log("記事を登録");
        return article;
    }

    findById(id: string): Article {
        console.log(`${id}の記事を検索`);
        return new Article();
    }
}

Discussion

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