1. 결합도 ( Coupling )란?
결합도는 소프트웨어 모듈 ( 클래스, 함수, 컴포넌트 등 ) 간의 상호 의존성을 나타내는 지표입니다.
결합도가 낮을 수록 ( Loose Coupling ) 유지보수와 확장성이 뛰어나고, 결합도가 높을 수록 ( Tight Coupling ) 변경이 어려워 집니다. 즉, 낮은 결합도를 가지고, 높은 응집도를 가지는 소프트웨어가 좋은 소프트 웨어입니다.
- 좋은 소프트 웨어
- 낮은 결합도 ( Low Coupling )
- 높은 응집도 ( High Cohesion )
2. 결합도의 종류 ( 낮은 결합도 → 높은 결합도 )
결합도 종류 | 설명 | 예제 |
자료 결합 ( Data Coupling ) | 모듈 간에 단순한 데이터 값(변수)만 주고받는 경우 | sum(a, b) 같은 함수 호출 |
스탬프 결합(Stamp Coupling) | 구조체나 객체 같은 복합 데이터 타입을 주고받는 경우 | process(struct Data d) |
제어 결합(Control Coupling) | 한 모듈이 다른 모듈의 흐름을 직접 제어하는 경우 | process(mode) (mode에 따라 동작이 달라짐) |
외부 결합(External Coupling) | 외부 환경(파일, DB, 네트워크 등)에 의존하는 경우 | DB에서 데이터를 읽어야 실행되는 함수 |
공통 결합(Common Coupling) | 전역 변수를 공유하는 경우 | global int count; |
내용 결합(Content Coupling) | 한 모듈이 다른 모듈의 내부 구현에 직접 접근하는 경우 | mod1()이 mod2() 내부 변수 직접 수정 |
3. 결합도별 예제 코드
1. 자료 결합 ( Data Coupling )
- 단순히 매개변수 ( primitive data )만 주고받음
- 서로 영향을 최소화할 수 있음
class Calculator {
public int add(int a, int b) { // 데이터만 주고받음
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
int result = calc.add(10, 20);
System.out.println("결과: " + result);
}
}
2. 스탬프 결합 ( Stamp Coupling )
- 구조체나 객체를 주고 받음
- 일부 데이터만 필요한데도 전체를 전달해야해서 불필요한 의존성이 생길 수 있음
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
class UserService {
public void printUserInfo(User user) {
System.out.println("사용자 이름: " + user.name);
}
}
public class Main {
public static void main(String[] args) {
User user = new User("홍길동", 25);
UserService service = new UserService();
service.printUserInfo(user); // 전체 객체를 전달하지만 일부 데이터만 사용
}
}
3. 제어 결합 ( Control Coupling )
- 모듈이 다른 모듈의 동작을 제어하는 값을 전달함
- 코드 변경 시 영향을 받기 쉬움
class ReportGenerator {
public void generateReport(boolean isDetailed) {
if (isDetailed) {
System.out.println("상세 보고서 생성...");
} else {
System.out.println("간략 보고서 생성...");
}
}
}
public class Main {
public static void main(String[] args) {
ReportGenerator generator = new ReportGenerator();
generator.generateReport(true); // 플래그 값으로 메서드 내부 동작을 제어
}
}
개선 방법
제어 변수를 주지 말고, 각각의 기능을 별도 함수로 분리하는 것이 좋습니다.
class ReportGenerator {
public void generateDetailReport() {
System.out.println("상세 보고서 생성...");
}
public void generateShortRepot() {
System.out.println("간략 보고서 생성...");
}
}
public class Main {
public static void main(String[] args) {
ReportGenerator generator = new ReportGenerator();
generator.generateDetailReport();
}
}
4. 외부 결합 ( External Coupling )
- 외부 파일, DB 등 환경에 의존함
- 외부 시스템이 변경되면 코드도 수정해야 함
import java.io.*;
class FileManager {
public void saveToFile(String data) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("data.txt"))) {
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
public String readFromFile() {
StringBuilder data = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
data.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return data.toString();
}
}
public class ExternalCouplingExample {
public static void main(String[] args) {
FileManager fileManager = new FileManager();
fileManager.saveToFile("결합도를 낮추자!");
System.out.println(fileManager.readFromFile());
}
}
개선 방안
파일을 직접 열지 말고, 인터페이스의 생성으로 결합도를 줄일 수 있습니다.
import java.io.*;
// 인터페이스 도입 (결합도 낮추기)
interface DataStorage {
void save(String data);
String load();
}
// 파일 저장 방식 구현 (FileStorage)
class FileStorage implements DataStorage {
private String filePath;
public FileStorage(String filePath) {
this.filePath = filePath;
}
@Override
public void save(String data) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String load() {
StringBuilder data = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
data.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return data.toString();
}
}
// FileManager는 DataStorage 인터페이스를 사용 (유연성 증가)
class FileManager {
private DataStorage storage;
public FileManager(DataStorage storage) {
this.storage = storage;
}
public void saveData(String data) {
storage.save(data);
}
public String readData() {
return storage.load();
}
}
public class ExternalCouplingRefactored {
public static void main(String[] args) {
// 파일 저장 방식이 아닌 다른 저장 방식도 쉽게 교체 가능!
DataStorage fileStorage = new FileStorage("data.txt");
FileManager fileManager = new FileManager(fileStorage);
fileManager.saveData("결합도를 줄이자!");
System.out.println(fileManager.readData());
}
}
5. 공통 결합 ( Common Coupling )
- 여러 모듈이 전역 변수(global)를 공유함
- 하나의 모듈에서 전역 변수를 변경하면 다른 모듈도 영향을 받음
class Config {
public static String databaseUrl = "jdbc:mysql://localhost:3306/mydb"; // 전역 변수 사용
}
class DatabaseService {
public void connect() {
System.out.println("DB 연결: " + Config.databaseUrl);
}
}
public class Main {
public static void main(String[] args) {
DatabaseService dbService = new DatabaseService();
dbService.connect();
}
}
개선 방법
전역 변수 사용을 피하고, 함수 매개 변수로 전달하는 것이 좋습니다.
class DatabaseService {
public void connect(String databaseUrl) {
System.out.println("DB 연결: " + databaseUrl);
}
}
public class Main {
public static void main(String[] args) {
String databaseUrl = "jdbc:mysql://localhost:3306/mydb";
DatabaseService dbService = new DatabaseService();
dbService.connect(databaseUrl);
}
}
6. 내용 결합 ( Content Coupling )
class BankAccount {
public int balance = 1000;
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.balance = 5000; // private 필드 직접 조작
System.out.println("잔액: " + account.balance);
}
}
개선 방법
접근을 막고, getter / setter를 제공하는 방식으로 수정해야 합니다.
class BankAccount {
private int balance = 1000;
public int getBalance() {
return balance;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
// account.balance = 5000; // private 필드 직접 조작
System.out.println("잔액: " + account.getBalance());
}
}
4. 종은 코드의 결합도 줄이는 방법
- 낮은 결합도를 유지하는 방법 :
- 전역 변수 사용을 최소화 ( 공통 결합 )
- 모듈 간 인터페이스 단순화 ( 필요한 데이터만 전달 )
- 의존성 주입 ( DI, Dependency Injection )을 활용
- 외부 시스템 ( DB, 파일 등 )과의 의존성을 최소화
- 한 모듈이 다른 모듈의 내부 구현을 알지 못하도록 캡슐화
5. 결론
- 결합도가 낮을수록 유지보수가 쉬운 코드
- 자료 결합 ( Data Coupling )을 유지하는 것이 가장 이상적
- 공통 결합 ( Common Coupling )과 내용 결합 ( Content Coupling )을 피하기
'CS 공부' 카테고리의 다른 글
CS) 정처기 대비 요약 정리 (0) | 2025.02.15 |
---|---|
CS) 소프트웨어 프로젝트 관리에서 3P와 애자일에서의 적용 (0) | 2025.02.14 |
CS ) UML 다이어그램과 디자인 패턴 정리 (0) | 2025.01.14 |
CS) 조인의 종류와 원리 (0) | 2025.01.13 |
CS) 인덱스 (0) | 2025.01.09 |