결합도는 모듈 또는 클래스 간의 의존성을 나타냅니다.
일반적으로 결합도는 낮을수록 좋은 코드입니다.
결합도가 높으면 각 모듈 간 의존성이 강해져, 하나의 모듈이 변경될 때, 다른 모듈도 영향을 받게 됩니다.
결합도 실제 예시
자동차와 엔진의 관계를 코드로 구현해 봅시다.
- 자동차는 초기에 Engine이 장착되어 있습니다.
- 자동차 시동이 걸리면 엔진이 동작한다고 출력합니다.
하지만 이제 엔진의 종류가 다양해졌고, 자동차가 여러 종류의 엔진을 지원해야 합니다.
결합도가 높은 코드
자동차 클래스가 디젤 엔진 클래스를 직접 포함합니다.
이 구조는 아래와 같은 문제점이 있습니다.
- 새로운 전기 엔진을 추가하려면 자동차 클래스도 수정해야합니다
- 변경이 잦은 경우 수정 범위가 커지고 유지 보수가 어려워집니다.
//기능 추가 전 예시코드
#include <iostream>
#include <string>
using namespace std;
// 기존 Engine 클래스
class Engine {
public:
string state;
Engine() : state("off") {}
void start() {
state = "on";
cout << "Engine started" << endl;
}
};
class Car {
public:
Engine engine;
void startCar() {
if (engine.state == "off") {
engine.start();
cout << "Car started" << endl;
}
}
};
//기능 추가 후 예시코드
#include <iostream>
#include <string>
using namespace std;
// 기존 Engine 클래스
class Engine {
public:
string state;
Engine() : state("off") {}
void start() {
state = "on";
cout << "Engine started" << endl;
}
};
// 새로운 ElectricEngine 클래스 (기존 Engine과는 별도)
class ElectricEngine {
public:
string state;
ElectricEngine() : state("off") {}
void start() {
state = "on";
cout << "Electric Engine running silently" << endl;
}
};
// 기존 Car 클래스 수정
class Car {
public:
Engine engine; // Car 클래스는 여전히 Engine 클래스에 강하게 의존
void startCar() {
if (engine.state == "off") {
engine.start();
cout << "Car started" << endl;
}
}
};
결합도가 높기 때문에 자동차 클래스를 직접 수정해야하고, 기능이 추가되어도 수정하기 어렵습니다.
결합도가 낮은 코드
결합도를 낮추기 위해 자동차 클래스가 특정 엔진을 직접 포함하지 않고 인터페이스를 활요하는 방법을 사용할 수 있습니다.
- 자동차 클래스는 엔진 인터페이스에만 의존하므로, 새로운 엔진을 추가해도 자동차 코드를 수정할 필요가 없습니다.
- 확장성이 높아지며, 다양한 엔진을 유연하게 지원할 수 있습니다.
//기능 추가 전 예시코드
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Engine {
public:
virtual void start() = 0;
virtual ~Engine() = default;
};
class DieselEngine : public Engine {
public:
void start() {
cout << "Diesel Engine started" << endl;
}
};
class Car {
private:
unique_ptr<Engine> engine; // 인터페이스에 의존하여 결합도 감소
public:
Car(unique_ptr<Engine> eng) : engine(move(eng)) {}
void startCar() {
engine->start();
cout << "Car started" << endl;
}
};
int main() {
auto engine = make_unique<DieselEngine>();
Car myCar(move(engine));
myCar.startCar();
return 0;
}
//기능 추가 후 예시코드
#include <iostream>
#include <memory>
using namespace std;
// 공통 인터페이스 정의
class Engine {
public:
virtual void start() = 0;
virtual ~Engine() = default;
};
// DieselEngine 구현
class DieselEngine : public Engine {
public:
void start() {
cout << "Diesel Engine started" << endl;
}
};
// 새로운 ElectricEngine 구현
class ElectricEngine : public Engine {
public:
void start() {
cout << "Electric Engine started silently" << endl;
}
};
// Car 클래스는 Engine 인터페이스에만 의존
class Car {
private:
unique_ptr<Engine> engine;
public:
Car(unique_ptr<Engine> eng) : engine(move(eng)) {}
void startCar() {
engine->start();
cout << "Car started" << endl;
}
};
int main() {
// DieselEngine을 사용하는 경우
auto dieselEngine = make_unique<DieselEngine>();
Car dieselCar(move(dieselEngine));
dieselCar.startCar();
// ElectricEngine을 사용하는 경우
auto electricEngine = make_unique<ElectricEngine>();
Car electricCar(move(electricEngine));
electricCar.startCar();
return 0;
}