본문 바로가기
개발/Architecture & Design Pattern

C++ - 의존 역전 원칙 (DIP, Dependency Inversion Principle)

by 피로물든딸기 2024. 2. 12.
반응형

C, C++ 전체 링크

Architecture & Design Pattern 전체 링크

 

참고

- 팩토리 메서드 패턴

 

의존 역전 원칙 (DIP, Dependency Inversion Principle)

- 구체화에 의존하지 말고 추상화에 의존해야 한다.

- 상위 모듈은 하위 모듈에 의존해서는 안 되며, 모든 모듈은 추상화된 것에 의존해야 한다.

- 이 원칙으로 하위 모듈의 변경이 상위 모듈에 영향을 주지 않고, 상위 모듈이 하위 모듈에 종속되지 않도록 한다.

- 예를 들어, DB 연결과 같은 하위 모듈에 대한 의존성을 해결하기 위해 인터페이스를 제공하고,

  상위 모듈은 해당 인터페이스를 구현하게 하여 실제 코드가 변경되더라도 상위 모듈에 영향을 미치지 않게 된다.


먼저 아래의 의존 역전 원칙 위반 사례를 보자.

#include <iostream>

using namespace std;

// 하위 모듈
class Database
{
public:
	void connect() { cout << "Connecting to database..." << endl; }
	// 그 외 기능...
};

// 상위 모듈
class Application
{
private:
	Database db;

public:
	void processData()
	{
		db.connect(); // 상위 Application 모듈이 하위 DB 모듈에 직접 의존
		cout << "Processing data..." << endl;
	}
};

int main()
{
	Application* app = new Application();
	app->processData();

	return 0;
}

 

코드에서 Application 클래스는 Database 클래스에 직접 의존하고 있다.

processData() 메서드가 db.connect()를 호출하여 데이터베이스에 연결하기 때문이다.

class Application
{
	...
	void processData()
	{
		db.connect(); // 상위 Application 모듈이 하위 DB 모듈에 직접 의존
		cout << "Processing data..." << endl;
	}
};

의존 역전 원칙 적용

 

Application이 Database에 직접 의존하지 않도록 의존성을 끊고 추상화된 인터페이스에 의존하도록 수정해 보자.

 

Database 클래스를 인터페이스로 만들어 추상화한다.

class Database
{
public:
	virtual ~Database() {}
	virtual void connect() = 0;
	// 그 외 기능...
};

 

그리고 Application 클래스는 Database 클래스의 구현이 아니라 인터페이스에만 의존하도록 변경하였다.

Application 클래스의 생성자를 통해 Database 인터페이스를 주입받아 사용하므로, 의존성을 역전시켰다.

class Application
{
private:
	Database* db;

public:
	// 생성자를 통해 의존성 주입
	Application(Database* db) : db(db) {}

	void processData()
	{
		db->connect(); // 상위 모듈이 하위 모듈에 의존하지만, 추상화된 인터페이스에 의존
		cout << "Processing data..." << endl;
	}
};

 

Application은 Database에 의존하지만 실제로 사용하는 하위 모듈은 MySQLDatabase다.

MySQLDatabaseDatabase 인터페이스를 통해 구체적으로 구현된 서브 클래스다.

class MySQLDatabase : public Database
{
public:
	void connect() override { cout << "Connecting to MySQL database..." << endl; }
	// 그 외 기능...
};

 

이제 Application 클래스는 Database 클래스의 변경에 영향을 받지 않으며,

MySQL 외에 다른 데이터베이스 구현체도 쉽게 교체할 수 있다.

 

전체 코드는 다음과 같다.

#include <iostream>

using namespace std;

// 하위 모듈에 대한 인터페이스
class Database 
{
public:
	virtual ~Database() {}
	virtual void connect() = 0;
	// 그 외 기능...
};

// 하위 모듈을 구현하는 서브 클래스
class MySQLDatabase : public Database 
{
public:
	void connect() override { cout << "Connecting to MySQL database..." << endl; }
	// 그 외 기능...
};

// 상위 모듈
class Application 
{
private:
	Database* db;

public:
	// 생성자를 통해 의존성 주입
	Application(Database* db) : db(db) {}

	void processData()
	{
		db->connect(); // 상위 모듈이 하위 모듈에 의존하지만, 추상화된 인터페이스에 의존
		cout << "Processing data..." << endl;
	}
};

int main() 
{
	Database* mysqlDb = new MySQLDatabase();
	Application* app = new Application(mysqlDb);
	app->processData();

	delete mysqlDb;

	return 0;
}
반응형

댓글