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이 높은 경우는 한 클래스에서 다른 클래스에 직접 접근을 하는 경우이다.
이런 경우 다른 클래스의 코드가 변경되면 해당 클래스의 코드도 같이 변경되어야 하는 경우도 존재한다.
인터페이스를 사용하여 결합도를 낮춘다.
인터페이스를 사용하면
- 유연성 증가
- 다른 구현체로 쉽게 교체가 가능
- 단일 책임 원칙
등의 이점을 얻을 수 있다.
하지만 항상 인터페이스를 쓰면 되는 것은 아니고
단일 구현체 이거나
인터페이스 사용이 오히려 복잡성을 증가시키거나 성능을 떨어트리면 사용하지 않는 게 낫다.
인터페이스 사용전에 아래와 같은 체크리스트를 확인해보면 좋을 것 같다.
- 이 클래스는 다른 구현체로 대체될 가능성이 있는가?
- 이 클래스를 독립적으로 테스트해야 하는가?
- 이 클래스가 여러 모듈에서 재사용될 가능성이 있는가?
'디자인패턴' 카테고리의 다른 글
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 |