결합도
모듈 간의 의존성 정도를 나타내는 척도로, 낮을수록 좋은 설계입니다.
결합도 단계 (높음 → 낮음)
| 단계 | 이름 | 설명 |
|---|---|---|
| 1 | 내용 결합 | 다른 모듈 내부를 직접 참조/수정 |
| 2 | 공통 결합 | 전역 변수를 공유 |
| 3 | 외부 결합 | 외부 데이터 포맷/프로토콜에 의존 |
| 4 | 제어 결합 | 제어 신호(플래그)를 전달 |
| 5 | 스탬프 결합 | 필요 이상의 데이터 구조를 전달 |
| 6 | 데이터 결합 | 필요한 데이터만 전달 O |
| 7 | 메시지 결합 | 메서드 호출만으로 통신 OO |
높은 결합도 예시
// X 내용 결합 - 다른 클래스 내부에 직접 접근
class OrderService {
void process(Order order) {
// 다른 클래스의 내부 필드를 직접 수정
order.status = "PAID";
order.paymentDate = LocalDate.now();
}
}
// X 공통 결합 - 전역 변수 공유
class Global {
static int userCount = 0; // 여러 클래스가 직접 접근
}
// X 제어 결합 - 플래그로 동작 제어
class FileExporter {
void export(Data data, boolean isCSV) { // 플래그로 분기
if (isCSV) { ... }
else { ... } // 호출자가 내부 동작을 제어
}
}
낮은 결합도 예시
// O 데이터 결합 - 필요한 데이터만 전달
class OrderService {
void process(Long orderId, String status) { // 필요한 값만 전달
orderRepository.updateStatus(orderId, status);
}
}
// O 메시지 결합 - 인터페이스를 통한 통신
interface FileExporter {
void export(Data data);
}
class CsvExporter implements FileExporter { ... }
class ExcelExporter implements FileExporter { ... }
// 호출자는 구현체를 몰라도 됨
class ReportService {
private final FileExporter exporter; // 인터페이스에만 의존
ReportService(FileExporter exporter) {
this.exporter = exporter;
}
}
결합도를 낮추는 방법
// 1. 의존성 주입 (DI)
// X 직접 생성
class OrderService {
EmailSender sender = new EmailSender(); // 강한 결합
}
// O 주입받음
class OrderService {
private final EmailSender sender;
OrderService(EmailSender sender) { // 외부에서 주입
this.sender = sender;
}
}
// 2. 인터페이스 활용
// X 구체 클래스에 의존
class OrderService {
private final KakaoEmailSender sender; // 카카오에 종속
}
// O 인터페이스에 의존
class OrderService {
private final EmailSender sender; // 구현체 교체 자유로움
}
응집도 vs 결합도
| 항목 | 응집도 | 결합도 |
|---|---|---|
| 관심사 | 모듈 내부 | 모듈 간 관계 |
| 좋은 방향 | 높을수록 O | 낮을수록 O |
| 나쁠 때 | 책임이 분산됨 | 변경 시 연쇄 영향 |
결합도가 높으면:
A 수정 → B 수정 → C 수정 → D 수정 ...
연쇄적 변경 발생 X
결합도가 낮으면:
A 수정 → A만 수정하면 됨 O
결합도를 낮추는 핵심은 "구체적인 것이 아닌 추상적인 것에 의존" 하는 것입니다. 이것이 객체지향 원칙 중 DIP(의존 역전 원칙) 의 핵심입니다.