コードで見るSOLID原則:依存関係逆転の原則

目次

目次:

依存関係逆転の原則(DIP) ってドユコト?

説明しよう。DIP(Dependency inversion principle)とは。

  • 高位モジュールは低位モジュールに依存せず、抽象的なものに依存する構造にするというものだよ。
  • つまり、DIPはコードモジュールが具体的な実装に依存するのではなく、抽象的なインターフェースに依存するということだよ。

ソフトウェアの複雑性によく効くの?

YES。

  • 高位モジュールと低位モジュールが密に結合しないため、ソフトウェアが複雑化を高い次元で抑制できるよ。
  • つまり、低位のモジュールを変更しても高位のモジュールに影響を与えないので、システムの柔軟性が高まり、保守が容易になるだよ。

テストのし易さによく効くの?

YES。

  • 依存関係を簡単にモック化したり、テストでスタブ化したりできるため、テストのしやすさも向上するよ。
  • これにより、より包括的なテストが可能になり、個々のモジュールを分離してテストすることが容易になるよ。

ソフトウェアを変更しやすくなるの?

YES。

  • DIPはコードがよりモジュール化され柔軟性があるため、システムの変更が容易になるよ。
  • 開発者は、高位のモジュールが壊れることを心配することなく、低位のモジュールに変更を加えることができるよ。
  • これにより、より迅速な開発とイテレーションが可能になるよ。

コードはドウダ?

SOLIDの原則のうち依存性逆転原理(DIP)は、Java、GO、Clojure、Pythonなど様々なプログラミング言語で適用することが可能です。ここでは、各言語でどのようにDIPを適用できるかを簡単に説明します。

依存関係逆転の原則(DIP)をJavaで書くと?

Javaのやり方

  • Javaでは、インターフェースや抽象クラスを使って、高位モジュールが依存する抽象化を定義し、低位モジュールがその抽象化を実装することでDIPを実現することができるよ。
  • 高位モジュールは、低位モジュールの具体的な実装には依存せず、抽象的なものだけに依存するようにするよ。

コードの説明

この実装では、データベースへの接続、データベースからの切断、データベースに対するクエリの実行のためのコントラクトを定義したインターフェース Database を定義しています。このインターフェースは MySQL データベースへの接続、MySQL データベースからの切断、および MySQL データベースでのクエリの実行に関する独自の実装の詳細を持っています。

UserDAO クラスは、具体的な MySQLDatabase の実装ではなく、Database の抽象化に依存しています。コンストラクタのパラメータとして Database インターフェースを受け取り、それを使用してデータベースからユーザーを取得します。この方法では、UserDAO クラスはデータベースの特定の実装に密結合されておらず、Database インターフェースの異なる実装を渡すことで、異なるデータベース実装を使用するように簡単に変更することができます。

コード: Java

import java.sql.ResultSet;
import java.util.List;

public interface Database {
    void connect();
    void disconnect();
    ResultSet executeQuery(String query);
}

/**
 * 低位モジュール
 */
public class MySQLDatabase implements Database {
    private String url;
    private String user;
    private String password;

    public MySQLDatabase(String url, String user, String password) {
        this.url = url;
        this.user = user;
        this.password = password;
    }

    @Override
    public void connect() {
        // 具体的な実装が記述される。
    }

    @Override
    public void disconnect() {
        // 具体的な実装が記述される。
    }

    @Override
    public ResultSet executeQuery(String query) {
        // 具体的な実装が記述される。
        return null;
    }
}

/**
 * 高位モジュール
 */
public class UserDAO {
    private final Database database;

    public UserDAO(Database database) {
        this.database = database;
    }

    public List<User> getUsers() {
        database.connect();
        ResultSet resultSet = database.executeQuery("SELECT * FROM users");

        // ResultSetからList<User>への具体的な実装が記述される。

        database.disconnect();
        return users;
    }
}

依存関係逆転の原則(DIP)をGoで書くと?

Goのやり方

  • GOでは、コンポーネント間の疎結合を可能にするインターフェースの使用により、DIPを実現することができるよ。
  • 高位モジュールはインターフェースに依存し、そのインターフェースは様々な低位モジュールによって実装するよ。

コードの説明

この実装では、データベースへの接続、データベースからの切断、データベースに対するクエリの実行のためのコントラクトを定義したインターフェース Database を定義している。

インターフェースは、MySQL データベースへの接続、MySQL データベースからの切断、および MySQL データベースに対するクエリの実行に関する独自の実装詳細を備えている。

UserDAO 構造体は、具体的な MySQLDatabase の実装ではなく、Database の抽象化に依存している。

NewUserDAO関数はDatabaseインターフェイスをパラメータとして受け取り、提供されたデータベース実装を持つUserDAO構造体の新しいインスタンスを返す。

GetUsers メソッドは、データベースからユーザーを取得するために、データベースの抽象化を使用する。

このように、UserDAO構造体はデータベースの特定の実装に密結合されておらず、Databaseインターフェースの異なる実装を渡すことで、異なるデータベース実装を使用するように容易に変更することができる。

コード: Go

type Database interface {
    Connect()
    Disconnect()
    ExecuteQuery(query string) ResultSet
}

type MySQLDatabase struct {
    // 具体的な実装が記述される。
}

func (db *MySQLDatabase) Connect() {
    // 具体的な実装が記述される。
}

func (db *MySQLDatabase) Disconnect() {
    // 具体的な実装が記述される。
}

func (db *MySQLDatabase) ExecuteQuery(query string) ResultSet {
    // 具体的な実装が記述される。
    return nil
}

type UserDAO struct {
    database Database
}

func NewUserDAO(database Database) *UserDAO {
    return &UserDAO{database: database}
}

func (dao *UserDAO) GetUsers() []User {
    dao.database.Connect()
    resultSet := dao.database.ExecuteQuery("SELECT * FROM users")

    // ResultSetを[]User格納

    dao.database.Disconnect()
    return users
}

依存関係逆転の原則(DIP)をClojureで書くと?

Clojureのやり方

Clojureでは、プロトコルを使用することでDIPを実現できる。プロトコルは、様々なコンポーネントによって実装可能な抽象的なインターフェースを定義する方法を提供するもの。

高位のコンポーネントは、具体的な実装ではなく、プロトコルに依存することができ、システムをより柔軟にすることができるぜよ。

コードの説明

この実装では、データベースへの接続、データベースからの切断、およびデータベースに対するクエリの実行に関するコントラクトを定義するプロトコル Database を定義した。

また、このプロトコルの具体的な実装として、MySQL データベースへの接続、MySQL データベースからの切断、MySQL データベースに対するクエリの実行に関する独自の実装詳細を持つ MySQLDatabase を定義している。

user-dao 関数は、具体的な MySQLDatabase の実装ではなく、抽象化された Database に依存する。

これは Database プロトコルのデータベースパラメータを受け取り、提供されたデータベース抽象化を使用してデータベースからユーザーを取得するために使用できるクロージャを返す。

このように、user-dao 関数はデータベースの特定の実装に密結合されておらず、Database プロトコルの異なる実装を渡すことで、異なるデータベース実装を使用するように容易に変更することができる。

コード: Clojure

(defprotocol Database
  (connect [this])
  (disconnect [this])
  (execute-query [this query]))

(defrecord MySQLDatabase []
  Database
  (connect [_])
  (disconnect [_])
  (execute-query [_ query] (query-database query)))

(defn user-dao [database]
  (fn []
    ;; ユーザーを取得するためにデータベースの抽象化を使用。
  ))

依存関係逆転の原則(DIP)をPythonで書くと?

Python のやり方

Pythonの場合、DIPは抽象的な基底クラスを使用することで実現できる。

高位のコンポーネントは、具体的な実装ではなく、この抽象的な基底クラスに依存することができ、システムをより柔軟なものにすることができる。

コードの説明

この実装では、データベースへの接続、データベースからの切断、データベースに対するクエリの実行に関するコントラクトを定義する抽象基底クラス Database を定義している。

また、この抽象基底クラスの具体的な実装として MySQLDatabase を定義した。このクラスは MySQL データベースへの接続、MySQL データベースからの切断、および MySQL データベースに対するクエリの実行に関する独自の実装の詳細を持っている。

UserDAO クラスは、具体的な MySQLDatabase の実装ではなく、Database の抽象化に依存している。

コンストラクタで Database クラスのデータベースパラメータを受け取り、提供されたデータベース抽象化を使用してデータベースからユーザーを取得するためにそれを使用する。

この方法では、UserDAO クラスはデータベースの特定の実装に密結合されておらず、Database クラスの異なる実装を渡すことで、異なるデータベース実装を使用するように簡単に変更することができる。

コード: Python

from abc import ABC, abstractmethod

class Database(ABC):
    @abstractmethod
    def connect(self):
        pass

    @abstractmethod
    def disconnect(self):
        pass

    @abstractmethod
    def execute_query(self, query: str)  ResultSet:
        pass

class MySQLDatabase(Database):
    # 具体的な実装が記述される。

class UserDAO:
    def __init__(self, database: Database):
        self.database = database

    def get_users(self):
        # ユーザーを取得するためにデータベースの抽象化を使用します。


よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次