제어 결합
모듈화 설계: 응집도와 결합도 (제어 결합 해결 방안)
소프트웨어공학 과목에서 모듈화 설계를 공부하며 **응집도(Cohesion)**와 **결합도(Coupling)**를 배우고 있다. 특히 결합도 중 **제어 결합(Control Coupling)**이 어떤 문제를 일으키는지, 그리고 어떻게 해결할 수 있는지에 대해 정리해 본다.
📌 제어 결합 (Control Coupling)의 문제점
제어 결합은 한 모듈이 다른 모듈에 **제어 플래그(Control Flag)**를 전달하여, 받는 모듈의 동작 흐름을 결정하는 방식이다. 아래 예시 코드를 통해 문제점을 살펴보자.
👎 제어 결합 예시 코드
// 인증_처리 모듈
function 인증_처리(사용자_ID, 비밀번호) {
if (사용자_인증_성공(사용자_ID, 비밀번호)) {
사용자_등급 = 사용자_정보_가져오기(사용자_ID).등급;
// 사용자_등급이 제어 플래그 역할을 한다.
로그인_수행(사용자_ID, 사용자_등급);
} else {
로그인_실패_처리();
}
}
// 로그인_수행 모듈
function 로그인_수행(사용자_ID, 등급) { // '등급'이 제어 플래그
if (등급 == "관리자") {
관리자_로그인_로직();
} else if (등급 == "일반_사용자") {
일반_사용자_로그인_로직();
} else {
// ... 기타 등급 처리
}
로그인_성공_처리(사용자_ID);
}
❗️ 제어 결합의 문제점
위 코드에서 사용자_등급
이라는 제어 플래그를 로그인_수행
모듈에 전달하고 있다. 이 방식은 다음과 같은 문제를 야기한다.
- 테스트의 어려움: 새로운 등급이 추가될 때마다
로그인_수행
모듈 내부의if-else if
블록이 변경되어야 하고, 이에 따라 관련 테스트 케이스도 업데이트되어야 한다. 이는 유지보수 비용을 증가시킨다. - 높은 의존성:
로그인_수행
모듈은등급
이라는 플래그의 의미와 종류를 알고 있어야 한다. 즉,인증_처리
모듈과로그인_수행
모듈 간에등급
플래그에 대한 암묵적인 합의가 필요하며, 한쪽이 변경되면 다른 쪽도 영향을 받는다. 이는 모듈 간의 결합도를 높인다.
✅ 제어 결합 해결: 전략 패턴(Strategy Pattern) 활용
이러한 제어 결합의 문제를 해결하기 위해, 플래그를 직접 매개변수로 전달하는 대신 **다형적인 객체(Polymorphic Object)**를 전달하는 방법을 사용한다. 이는 디자인 패턴 중 **전략 패턴(Strategy Pattern)**과 유사하다. 로그인의 구체적인 방식을 캡슐화하여 추상화된 인터페이스를 통해 상호작용하도록 변경할 수 있다.
👍 제어 결합 해결 예시 코드 (전략 패턴 활용)
// 로그인 전략 인터페이스/클래스 (추상화된 로그인 행위 정의)
interface 로그인_전략 {
void 로그인();
}
// 관리자 로그인 전략 구현체
class 관리자_로그인_전략 implements 로그인_전략 {
void 로그인() {
// 실제 관리자 로그인 처리 로직
관리자_로그인_로직();
}
}
// 일반 사용자 로그인 전략 구현체
class 일반_사용자_로그인_전략 implements 로그인_전략 {
void 로그인() {
// 실제 일반 사용자 로그인 처리 로직
일반_사용자_로그인_로직();
}
}
// 로그인_수행 모듈 (전략 패턴의 Context 역할)
function 로그인_수행(사용자_ID, 로그인_전략_객체) {
// 제어 플래그 대신 객체의 공통 메서드를 호출
로그인_전략_객체.로그인();
로그인_성공_처리(사용자_ID);
}
// 인증_처리 모듈 (전략 객체 생성 및 전달)
function 인증_처리(사용자_ID, 비밀번호) {
if (사용자_인증_성공(사용자_ID, 비밀번호)) {
사용자_등급 = 사용자_정보_가져오기(사용자_ID).등급;
로그인_전략_객체 = null; // 초기화
// 사용자 등급에 따라 적절한 전략 객체 생성
if (사용자_등급 == "관리자") {
로그인_전략_객체 = new 관리자_로그인_전략();
} else if (사용자_등급 == "일반_사용자") {
로그인_전략_객체 = new 일반_사용자_로그인_전략();
}
// ... 기타 등급 처리 및 전략 객체 생성 로직 추가 가능
로그인_수행(사용자_ID, 로그인_전략_객체); // 추상화된 객체 전달
} else {
로그인_실패_처리();
}
}
✨ 개선점
이 방식은 등급
플래그에 직접 의존하지 않고, 로그인_전략_객체
라는 추상화된 객체를 통해 공통적인 로그인()
함수를 호출한다.
로그인_수행
모듈의 독립성 증대: 이제로그인_수행
모듈은 더 이상등급
플래그의 종류나 각 등급별 로그인 로직을 알 필요가 없다. 단지로그인_전략
인터페이스를 구현하는 객체가로그인()
메서드를 가지고 있다는 사실만 알면 된다. 이는로그인_수행
모듈의 결합도를 낮추고 응집도를 높인다.- 확장성 용이: 새로운 사용자 등급이 추가되더라도, 새로운
로그인_전략
구현체만 추가하고인증_처리
모듈에서 해당 전략 객체를 생성하여 넘겨주면 된다.로그인_수행
모듈은 전혀 수정할 필요가 없으므로, 유지보수 및 확장이 훨씬 용이해진다. - 테스트 용이성: 각
로그인_전략
구현체를 개별적으로 테스트할 수 있으며,로그인_수행
모듈은 다양한 전략 객체를 주입하여 테스트할 수 있다.
이러한 방식으로 설계하면, 변화에 유연하게 대응할 수 있는 견고한 소프트웨어를 만들 수 있다. 소프트웨어공학의 모듈화 원칙이 실제 코드에 어떻게 적용되는지 명확히 이해할 수 있는 좋은 예시였다.