SOLID 원칙이란?
SOLID
- Single responsibility principle(단일 책임 원칙)
- 한 클래스는 하나의 책임만 가져야 한다.
- 하나의 역할을 한다.
- 클래스가 변경되어야 하는 이유가 하나만 있어야 함을 의미합니다.
- Open/closed principle(개방 폐쇄 원칙)
- 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장을 위해 열려 있어야 하지만 변경을 위해서는 닫혀 있어야 한다.
- 개방폐쇄 원칙 기능은 유지보수를 하는 과정이 쉽게 이루어져야한다.
- 기존의 코드를 변경하지 않고 시스템에 새로운 기능을 추가할 수 있도록 해야 함을 의미합니다.
- Liskov substitution principle(리스코프 치환 원칙)
- Interface segregation principle(인터페이스 분리 원칙)
- 클라이언트가 사용하지 않는 메서드에 의존하지 않도록 해야 함을 의미합니다.
- 인터페이스가 특정 기능에 집중된 작고 간결한 것이어야 함을 의미합니다.
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- Dependency inversion principle(의존관계 역전 원칙)
- 프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.” 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
SRP의 예시를 생각해보면
예시 1: 주문 클래스 하나의 책임을 가지는 클래스를 만들 때 유용한 예시로 주문 클래스를 들 수 있다. 주문 클래스는 주문 정보를 저장하고 관리하는 책임을 가진다. 주문 클래스는 주문 정보를 생성, 수정, 조회 및 삭제하는 기능을 포함할 수 있다. 이러한 기능을 다른 클래스에서 처리하게 되면 코드가 혼란스러워질 수 있으며, 유지 보수가 어려워진다.
예시 2: 로그인 클래스 로그인 기능을 구현할 때, SRP를 따르는 클래스를 만들 수 있다. 로그인 클래스는 사용자 인증 정보를 관리하고 로그인 기능을 처리하는 책임을 가진다. 이 클래스에서는 사용자의 로그인 정보를 검증하고, 로그인이 성공했는지 여부를 반환하는 기능을 구현할 수 있다.
이런한 예시들은 SRP를 따르는 클래스가 어떤 것인지 보여주고, 하나의 책임을 가지는 클래스는 코드의 유지보수와 이해도를 향상시킨다는 것을 보여준다.
O/CP는
위의 SRP의 기반으로 유지보수성을 높이기 위해서 코드를 짤 때 확장성을 높이는 것으로 위에서 설명 했듯이 수정이 아닌 추가를 통해서 기능을 확장할 수 있도록 짜는 것이 중요하다는 것이다.
예를 들어서, 주문과 관련된 프로그램을 짠다고 했을 때, 큰 추상화적 인터페이스로 order라는 인터페이스를 만들고, 그것에서 확장적으로 offlineOrder, onlineOrder, singleOrder, groupOrder 등 상황에 맞는 주문 방법을 확장적으로 코드로 작성하는 방식을 의미한다.
LSP의 경우는 하위 클래스에서 상위 클래스를 대신할 수 있도록 작성하는 것으로,
만약 Repositury를 구성한다고 했을 때, Repository를 처음에는 수정, 삭제가 많이 필요하여 SinglyLinkedList로 짰었는데, 나중에 수정, 삭제보다 탐색이 중요해져서, Repository를 DoubleLinkedList로 바꿔야 한다고 가정했을 때, List<Member> list = new SinglyLinkedList<>(); 에서 단지 List<Member> list = new DoubleLinkedList<>();로만 수정하면 되는 유지보수가 쉬워지는 예시가 있다.
ISP는 특정 기능에 특화된 인터페이스로 코드를 짜야한다는 원리인데, 만약 자동차라는 interface가 있다면, 여기에서 더 특화한 sportscar인터페이스나 truck인터페이스를 만들어서 그것에 맞춰 차의 브랜드 종류에 맞는 객체를 만드는 등의 예시가 있다.
주문과 관련하여 예시 코드를 짜보면, oreder라는 interface를 만들고, 주문을 생성하는 인터페이스 OrderFactory와 주문을 처리하는 OrderProcessor인터페이스를 만들고, 거기에 주문의 종류별로 생성과 처리 클래스를 정의하는 예시로 구현했다.
public interface OrderProcessor {
void processOrder(Order order);
}
public class OnlineOrderProcessor implements OrderProcessor {
@Override
public void processOrder(Order order) {
// 온라인 주문 처리 로직
}
}
public class InStoreOrderProcessor implements OrderProcessor {
@Override
public void processOrder(Order order) {
// 매장 주문 처리 로직
}
}
public class PhoneOrderProcessor implements OrderProcessor {
@Override
public void processOrder(Order order) {
// 전화 주문 처리 로직
}
}
public interface OrderFactory {
Order createOrder();
}
public class OnlineOrderFactory implements OrderFactory {
@Override
public Order createOrder() {
// 온라인 주문 생성 로직
return new OnlineOrder();
}
}
public class InStoreOrderFactory implements OrderFactory {
@Override
public Order createOrder() {
// 매장 주문 생성 로직
return new InStoreOrder();
}
}
public class PhoneOrderFactory implements OrderFactory {
@Override
public Order createOrder() {
// 전화 주문 생성 로직
return new PhoneOrder();
}
}
OrderFactory onlineOrderFactory = new OnlineOrderFactory();
Order onlineOrder = onlineOrderFactory.createOrder();
OrderProcessor onlineOrderProcessor = new OnlineOrderProcessor();
onlineOrderProcessor.processOrder(onlineOrder);
OrderFactory inStoreOrderFactory = new InStoreOrderFactory();
Order inStoreOrder = inStoreOrderFactory.createOrder();
OrderProcessor inStoreOrderProcessor = new InStoreOrderProcessor();
inStoreOrderProcessor.processOrder(inStoreOrder);
public class OnlineOrder implements Order {
private int id;
private Date date;
private double totalAmount;
private List<OrderItem> orderItems;
public OnlineOrder() {
// 생성자 코드
}
@Override
public int getId() {
return id;
}
@Override
public void setId(int id) {
this.id = id;
}
@Override
public Date getDate() {
return date;
}
@Override
public void setDate(Date date) {
this.date = date;
}
@Override
public double getTotalAmount() {
return totalAmount;
}
@Override
public void setTotalAmount(double totalAmount) {
this.totalAmount = totalAmount;
}
@Override
public List<OrderItem> getOrderItems() {
return orderItems;
}
@Override
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
}
@Override
public void removeOrderItem(OrderItem orderItem) {
orderItems.remove(orderItem);
}
@Override
public void submitOrder() {
// 온라인 주문 제출 처리 코드
}
}
public interface Order {
int getId();
void setId(int id);
Date getDate();
void setDate(Date date);
double getTotalAmount();
void setTotalAmount(double totalAmount);
List<OrderItem> getOrderItems();
void addOrderItem(OrderItem orderItem);
void removeOrderItem(OrderItem orderItem);
void submitOrder();
}