본문 바로가기

CS 공부

CS) 결합도 ( Coupling )

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. 종은 코드의 결합도 줄이는 방법

  • 낮은 결합도를 유지하는 방법 :
    1. 전역 변수 사용을 최소화 ( 공통 결합 )
    2. 모듈 간 인터페이스 단순화 ( 필요한 데이터만 전달 )
    3. 의존성 주입 ( DI, Dependency Injection )을 활용
    4. 외부 시스템 ( DB, 파일 등 )과의 의존성을 최소화
    5. 한 모듈이 다른 모듈의 내부 구현을 알지 못하도록 캡슐화

 

5. 결론

 

  • 결합도가 낮을수록 유지보수가 쉬운 코드
  • 자료 결합 ( Data Coupling )을 유지하는 것이 가장 이상적
  • 공통 결합 ( Common Coupling )과 내용 결합 ( Content Coupling )을 피하기