프로젝트

일반

사용자정보

GRASP 패턴: Low Coupling (낮은 결합도)

Prof. Jong Min Lee이(가) 약 한달 전에 추가함

GRASP 패턴: Low Coupling (낮은 결합도) - 간단한 설명과 예시

핵심 아이디어: 클래스 간의 의존성을 최소화하여 한 클래스의 변경이 다른 클래스에 미치는 영향을 줄입니다. 서로 강하게 연결된 클래스들은 하나의 변경이 연쇄적인 변경을 유발할 수 있으므로, 의존성을 줄여 시스템을 더 유연하고 이해하기 쉽게 만드는 것이 중요합니다.

간단한 설명:

낮은 결합도는 클래스들이 서로 얼마나 독립적인지를 나타내는 정도입니다. 결합도가 낮은 시스템에서는 한 클래스의 변경이 다른 클래스에 미치는 영향이 적거나 없습니다. 이는 시스템을 더 안정적이고 유지보수하기 쉽게 만들며, 새로운 기능을 추가하거나 기존 기능을 변경할 때 부담을 줄여줍니다. 클래스 간의 의존성은 클래스가 서로를 알고 있거나, 서로의 속성이나 메서드를 사용하는 방식으로 나타납니다.

예시:

알림 기능을 구현한다고 가정해 봅시다. 초기에는 이메일로만 알림을 보내는 기능을 구현했습니다.

class Order {
    private String orderId;
    private double totalAmount;
    private String customerEmail;

    public Order(String orderId, double totalAmount, String customerEmail) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.customerEmail = customerEmail;
    }

    public void processOrder() {
        // 주문 처리 로직 ...
        sendEmailConfirmation();
    }

    private void sendEmailConfirmation() {
        EmailSender emailSender = new EmailSender();
        emailSender.sendEmail(customerEmail, "주문 확인", "주문이 완료되었습니다.");
    }
}

class EmailSender {
    public void sendEmail(String to, String subject, String body) {
        // 이메일 전송 로직 ...
        System.out.println("이메일 발송: To=" + to + ", Subject=" + subject + ", Body=" + body);
    }
}

public class Client {
    public static void main(String[] args) {
        Order order = new Order("ORD123", 55000, "user@example.com");
        order.processOrder();
    }
}

위의 예시에서 Order 클래스는 EmailSender 클래스에 강하게 결합되어 있습니다. Order 클래스는 EmailSender의 구체적인 구현을 알고 있으며, sendEmailConfirmation() 메서드 내에서 EmailSender 객체를 직접 생성하고 사용합니다. 만약 알림 방식을 SMS로 변경해야 하거나, 다른 이메일 전송 방식을 사용해야 한다면 Order 클래스를 직접 수정해야 합니다. 이는 높은 결합도의 문제입니다.

낮은 결합도 패턴을 적용하면 인터페이스를 도입하여 의존성을 줄일 수 있습니다.

interface NotificationSender {
    void sendNotification(String recipient, String subject, String message);
}

class EmailSender implements NotificationSender {
    @Override
    public void sendNotification(String recipient, String subject, String message) {
        // 이메일 전송 로직 ...
        System.out.println("이메일 발송: To=" + recipient + ", Subject=" + subject + ", Body=" + message);
    }
}

class SMSSender implements NotificationSender {
    @Override
    public void sendNotification(String recipient, String subject, String message) {
        // SMS 전송 로직 ...
        System.out.println("SMS 발송: To=" + recipient + ", Subject=" + subject + ", Body=" + message);
    }
}

class Order {
    private String orderId;
    private double totalAmount;
    private String customerContact; // 이메일 또는 전화번호

    private NotificationSender notificationSender; // 인터페이스에 의존

    public Order(String orderId, double totalAmount, String customerContact, NotificationSender sender) {
        this.orderId = orderId;
        this.totalAmount = totalAmount;
        this.customerContact = customerContact;
        this.notificationSender = sender;
    }

    public void processOrder() {
        // 주문 처리 로직 ...
        sendConfirmation();
    }

    private void sendConfirmation() {
        notificationSender.sendNotification(customerContact, "주문 확인", "주문이 완료되었습니다.");
    }
}

public class Client {
    public static void main(String[] args) {
        // 이메일로 알림 발송
        NotificationSender emailSender = new EmailSender();
        Order orderWithEmail = new Order("ORD123", 55000, "user@example.com", emailSender);
        orderWithEmail.processOrder();

        System.out.println("--- 알림 방식 변경 ---");

        // SMS로 알림 발송 (Order 클래스 수정 없음)
        NotificationSender smsSender = new SMSSender();
        Order orderWithSMS = new Order("ORD456", 30000, "010-1234-5678", smsSender);
        orderWithSMS.processOrder();
    }
}

변경된 설계에서는 Order 클래스가 NotificationSender라는 인터페이스에 의존하고, 실제 알림 발송 객체(EmailSender, SMSSender)는 외부에서 주입됩니다.

낮은 결합도의 장점:

  • 유연성: 알림 방식을 변경하거나 새로운 알림 방식을 추가하더라도 Order 클래스를 수정할 필요가 없습니다. 새로운 NotificationSender 구현체를 만들고 Order 객체를 생성할 때 주입해주기만 하면 됩니다.
  • 유지보수 용이성: 각 클래스가 독립적이므로 특정 기능 변경 시 관련된 클래스만 수정하면 됩니다.
  • 재사용성 향상: Order 클래스는 특정 알림 방식에 종속되지 않으므로 다양한 환경에서 재사용될 수 있습니다.
  • 테스트 용이성: 인터페이스를 기반으로 Mock 객체를 만들어 Order 클래스의 로직을 알림 기능 없이 독립적으로 테스트할 수 있습니다.

결론적으로, 낮은 결합도 패턴은 클래스 간의 의존성을 줄여 시스템을 더 유연하고 유지보수하기 쉽게 만드는 중요한 설계 원칙입니다. 인터페이스, 추상 클래스, 의존성 주입 등 다양한 방법을 통해 결합도를 낮출 수 있습니다.