워밍업 클럽 4기 - 백엔드 Day 4 미션: SOLID
1. 코드 리팩토링
public boolean validateOrder(Order order) {
if (order.isEmpty()) {
log.info("주문 항목이 없습니다.");
return false;
}
if (order.hasZeroOrNegativeTotalPrice()) {
log.info("올바르지 않은 총 가격입니다.");
return false;
}
if (order.hasEmptyCustomerInfo()) {
log.info("사용자 정보가 없습니다.");
return false;
}
return true;
}
public class Order {
// items, totalPrice, customerInfo는 있다고 가정
public boolean isEmpty() {
return this.items.size() == 0;
}
public boolean hasZeroOrNegativeTotalPrice() {
return this.totalPrice <= 0;
}
public boolean hasEmptyCustomerInfo() {
return this.customerInfo == null;
}
}
2. SOLID를 나만의 언어로 표현하기
SRP(단일 책임 원칙):
객체의 책임을 하나의 홑문장으로 설명할 수 있어야 한다.
서술어가 2번 나온다는 말은 동작이 2번 나온다는 말이고, 과한 책임을 지고있거나 구체적인 특징만 나열한 경우일 수 있다.(예: 쿼터백은 패스 게임에서는 공을 던지고, 러닝 게임에서는 공을 러닝백에네 넘겨주고, 코치의 작전을 다른 공격 선수들에게 전달한다.)
객체를 설명하는 말의 추상화 정도가 과하면 객체를 적절히 설명할 수 없다.(예: 쿼터백은 미식축구 포지션이다.)
미식축구에서 쿼터백은 공격 전술을 지휘하는 포지션이다.
OCP(개방 폐쇄 원칙): 기능을 변경할 때는 코드를 고치는 게 아니라 코드를 더하는 방식으로 구현해야 한다.
변경해야 할 코드와 그대로 유지되어야 할 코드가 섞여 있으면, 기능을 수정할 때마다 기존 코드를 계속 수정하게 되고, 이로 인해 변경이 다른 코드에까지 영향을 미칠 수 있다.
따라서 변경이 필요한 부분과 보존해야 할 부분을 명확히 분리하고, 새로운 기능은 기존 코드를 건드리지 않고 덧붙이는 방식으로 구현해야 한다.
LSP(리스코프 치환 원칙): 부모 클래스가 자식 클래스에게 물려준 기능은 보존해야한다.
부모 클래스를 자식 클래스로 교체한다는 것은, 부모의 기능을 자식이 할 수 있다는 전제가 깔려있다.
그런데 만약 자식 클래스가 부모 클래스의 기능을 제대로 수행하지 못한다면, 예기치 못한 동작을 하거나 에러가 발생한다.
그렇다면 부모 클래스를 자식 클래스로 교체하는데 추가적인 리소스가 든다.
따라서 자식 클래스는 부모 클래스의 기능을 보존해야한다.
ISP(인터페이스 분리 원칙): 응집도 높은 인터페이스를 설계해야 한다.
인터페이스는 객체의 설계도다. 그리고 인터페이스가 변경된다면, 모든 구현체도 변경되어야 한다.
인터페이스의 응집도가 낮아 여러 책임이 뒤섞여 있으면 일부 기능 변경으로 인해 구현체는 불필요한 변경을 할 수 있다.
따라서 인터페이스는 관련 기능들로 묶인, 응집도 높은 형태로 설계해야 한다.
DIP(의존 역전 원칙): 고수준 모듈과 저수준 모듈이 맺는 하나의 의존 관계를 2개의 의존 관계로 분리하여 결합도를 낮춘다.
고수준 모듈과 저수준 모듈이 직접 연결되어 있으면, 저수준 모듈의 변경 사항은 고수준 모듈까지 전파될 수 있다.
하지만 저수준 모듈을 추상화하면, 아래 같이 2개의 의존 관계로 쪼개진다.
고수준 모듈은 추상화 모듈 의존
저수준 모듈은 추상화 모듈 의존(의존성 역전)
이렇게 2개로 쪼개진 의존 관계에서는, 저수준 모듈의 변경이 고수준 모듈에 영향을 주지 못해 코드의 유지보수성이 향상된다.
%추가% DI(의존성 주입): 어떤 포지션에 어떤 선수를 넣을지 말지는 감독의 책임이다.(스포츠 관점)
댓글을 작성해보세요.