본문 바로가기

디자인패턴

높은 응집도, 낮은 결합도 예제 코드

728x90

grasp pattern에서 높은 응집도(cohesion)과 낮은 결합도(coupling)인 코드가 좋다고 했는 데, 

잘 와닿지가 않아서 코드를 한번 봐보려고 한다. 

https://ddevgrit.tistory.com/93

 

GRASP PATTERN

General Responsibility Assignment Software Patterns객체지향 디자인의 핵심은 객체에 책임을 부여하는 것이다.책임을 부여하는 원칙에 대해 말하는 패턴이다.총 9가지 패턴이 존재 Information Expert책임을 수

ddevgrit.tistory.com

 

 

chatgpt에게 2개씩 만들어 주고 리팩토링 해보라고 했다. 

 

응집도 관련 코드는 굉장히 이해가 쉬웠다. 

높은 응집도를 가진 코드는 한 클래스에 하나의 책임만 있어야한다. 

 

문제 코드 

class Student {
    public void enrollCourse() {
        // Enroll in course logic
        System.out.println("Enrolling in course...");
    }
    
    public void takeExam() {
        // Take exam logic
        System.out.println("Taking exam...");
    }
    
    public void payFees() {
        // Pay fees logic
        System.out.println("Paying fees...");
    }
}

 

refactoring 코드

 

class CourseEnrollment {
    public void enrollCourse() {
        System.out.println("Enrolling in course...");
    }
}

class Exam {
    public void takeExam() {
        System.out.println("Taking exam...");
    }
}

class FeePayment {
    public void payFees() {
        System.out.println("Paying fees...");
    }
}

 

 

굉장히 쉽다. 

 

 

 

결합도(coupling)

결합도는 chatgpt가 이상한 예제를 줘서 이해하는 데 조금 어려웠다.

chatgpt가 준 높은 coupling을 가진 코드는 아래와 같다 (문제)

 

예제

class Order {
    Product product;
    Inventory inventory;
    Payment payment;
    
    public Order(Product product, Inventory inventory, Payment payment) {
        this.product = product;
        this.inventory = inventory;
        this.payment = payment;
    }
    
    public void placeOrder() {
        if (inventory.checkStock(product)) {
            payment.processPayment(product);
            inventory.updateStock(product);
        }
    }
}

class Product {
    String name;
    double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

class Inventory {
    public boolean checkStock(Product product) {
        // 재고 확인 로직
        return true;
    }
    
    public void updateStock(Product product) {
        // 재고 업데이트 로직
    }
}

class Payment {
    public void processPayment(Product product) {
        // 결제 처리 로직
    }
}

 

low coupling refactoring

interface InventoryService {
    boolean checkStock(Product product);
    void updateStock(Product product);
}

interface PaymentService {
    void processPayment(Customer customer, Product product);
}

class Order {
    private final Product product;
    private final InventoryService inventoryService;
    private final PaymentService paymentService;
    
    public Order(Product product, InventoryService inventoryService, PaymentService paymentService) {
        this.product = product;
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
    }
    
    public void placeOrder() {
        if (inventoryService.checkStock(product)) {
            paymentService.processPayment(product);
            inventoryService.updateStock(product);
        }
    }
}

class Product {
    private final String name;
    private final double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    // Getter methods
    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }
}

class Inventory implements InventoryService {
    @Override
    public boolean checkStock(Product product) {
        // 재고 확인 로직
        return true;
    }
    
    @Override
    public void updateStock(Product product) {
        // 재고 업데이트 로직
    }
}

class Payment implements PaymentService {
    @Override
    public void processPayment(Product product) {
        // 결제 처리 로직
    }
}

public class Main {
    public static void main(String[] args) {
        Product product = new Product("Laptop", 1200.00);
        InventoryService inventoryService = new Inventory();
        PaymentService paymentService = new Payment();
        
        Order order = new Order(product, inventoryService, paymentService);
        order.placeOrder();
    }
}

 

 

coupling이 높은 경우는 한 클래스에서 다른 클래스에 직접 접근을 하는 경우이다. 

이런 경우 다른 클래스의 코드가 변경되면 해당 클래스의 코드도 같이 변경되어야 하는 경우도 존재한다. 

 

인터페이스를 사용하여 결합도를 낮춘다. 

 

인터페이스를 사용하면 

  • 유연성 증가
    • 다른 구현체로 쉽게 교체가 가능
  • 단일 책임 원칙 

등의 이점을 얻을 수 있다. 

 

 

하지만 항상 인터페이스를 쓰면 되는 것은 아니고 

단일 구현체 이거나

인터페이스 사용이 오히려 복잡성을 증가시키거나 성능을 떨어트리면 사용하지 않는 게 낫다.

 

인터페이스 사용전에 아래와 같은 체크리스트를 확인해보면 좋을 것 같다.

 

 

  • 이 클래스는 다른 구현체로 대체될 가능성이 있는가?
  • 이 클래스를 독립적으로 테스트해야 하는가?
  • 이 클래스가 여러 모듈에서 재사용될 가능성이 있는가?

 

 

728x90

'디자인패턴' 카테고리의 다른 글

Edge computing 이란?  (0) 2024.06.24
MVC 패턴 (Model-View-Controller pattern)  (0) 2024.06.20
SOLID 원칙  (0) 2024.06.14
GRASP PATTERN  (0) 2024.06.13
Design Pattern 총정리  (0) 2024.06.11