SOLIDPrinciples
1. 📌 단일 책임 원칙 (Single Responsibility Principle)
1.1 핵심 개념
한 클래스는 단 하나의 책임만 가져야 한다
변경의 이유가 하나여야 한다
1.2 실제 예시
// ❌ 잘못된 예시: 여러 책임이 혼재
public class UserService {
public void createUser(User user) { /* 사용자 생성 로직 */ }
public void sendEmail(User user) { /* 이메일 발송 로직 */ }
public void generateReport(User user) { /* 리포트 생성 로직 */ }
}
// ✅ 좋은 예시: 책임 분리
public class UserService {
public void createUser(User user) { /* 사용자 생성 로직 */ }
}
public class EmailService {
public void sendEmail(User user) { /* 이메일 발송 로직 */ }
}
public class ReportService {
public void generateReport(User user) { /* 리포트 생성 로직 */ }
}
2. 🔄 개방-폐쇄 원칙 (Open-Closed Principle)
2.1 핵심 개념
확장에는 열려있고, 수정에는 닫혀있어야 한다
기존 코드 변경 없이 새로운 기능을 추가할 수 있어야 함
2.2 실제 예시
// ❌ 잘못된 예시: 새로운 할인 정책 추가시 코드 수정 필요
public class DiscountCalculator {
public double calculateDiscount(Order order, String type) {
if (type.equals("REGULAR")) {
return order.getPrice() * 0.1;
} else if (type.equals("VIP")) {
return order.getPrice() * 0.2;
}
return 0;
}
}
// ✅ 좋은 예시: 인터페이스를 통한 확장
public interface DiscountPolicy {
double calculateDiscount(Order order);
}
public class RegularDiscountPolicy implements DiscountPolicy {
@Override
public double calculateDiscount(Order order) {
return order.getPrice() * 0.1;
}
}
public class VIPDiscountPolicy implements DiscountPolicy {
@Override
public double calculateDiscount(Order order) {
return order.getPrice() * 0.2;
}
}
3. 🔄 리스코프 치환 원칙 (Liskov Substitution Principle)
3.1 핵심 개념
상위 타입의 객체를 하위 타입의 객체로 치환해도 프로그램의 정확성이 보장되어야 한다
하위 클래스는 상위 클래스의 규약을 지켜야 함
3.2 실제 예시
// ❌ 잘못된 예시: LSP 위반
public class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int getArea() { return width * height; }
}
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // LSP 위반: 예상치 못한 동작
}
}
// ✅ 좋은 예시: 공통 인터페이스 사용
public interface Shape {
int getArea();
}
public class Rectangle implements Shape {
private int width;
private int height;
public int getArea() { return width * height; }
}
public class Square implements Shape {
private int side;
public int getArea() { return side * side; }
}
4. 🔌 인터페이스 분리 원칙 (Interface Segregation Principle)
4.1 핵심 개념
클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다
큰 인터페이스를 작은 단위로 분리
4.2 실제 예시
// ❌ 잘못된 예시: 거대한 인터페이스
public interface Worker {
void work();
void eat();
void sleep();
}
// ✅ 좋은 예시: 인터페이스 분리
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
public class Human implements Workable, Eatable, Sleepable {
@Override public void work() { /* 작업 수행 */ }
@Override public void eat() { /* 식사 */ }
@Override public void sleep() { /* 수면 */ }
}
public class Robot implements Workable {
@Override public void work() { /* 작업 수행 */ }
}
5. 🎯 의존관계 역전 원칙 (Dependency Inversion Principle)
5.1 핵심 개념
고수준 모듈은 저수준 모듈에 의존하지 않아야 한다
둘 다 추상화에 의존해야 함
5.2 실제 예시
// ❌ 잘못된 예시: 구체 클래스에 직접 의존
public class OrderService {
private MySQLOrderRepository orderRepository = new MySQLOrderRepository();
public void createOrder(Order order) {
orderRepository.save(order);
}
}
// ✅ 좋은 예시: 추상화에 의존
public interface OrderRepository {
void save(Order order);
}
public class MySQLOrderRepository implements OrderRepository {
@Override
public void save(Order order) { /* MySQL 저장 로직 */ }
}
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void createOrder(Order order) {
orderRepository.save(order);
}
}
🎓 SOLID 원칙의 실제 적용
장점
🔧 유지보수성 향상
🔄 재사용성 증가
🧪 테스트 용이성
🚀 확장성 개선
주의사항
💭 과도한 추상화 피하기
🎯 비즈니스 요구사항에 맞는 적절한 수준의 설계
🔄 점진적인 리팩토링을 통한 개선
📚 결론
SOLID 원칙은 객체 지향 설계의 근간이 되는 원칙들입니다. 이러한 원칙들을 잘 이해하고 적용하면, 유지보수가 쉽고 확장 가능한 소프트웨어를 만들 수 있습니다. 하지만 각 원칙을 맹목적으로 따르기보다는, 상황에 맞게 적절히 적용하는 것이 중요합니다.
Last updated