**OCP (Open-Closed Principle)**는 SOLID 원칙 중 하나로, 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려있고, 수정에는 닫혀있어야 한다는 원칙입니다.
이 프로젝트는 OCP를 위반하는 나쁜 예제와 OCP를 준수하는 좋은 예제를 통해 개념을 학습할 수 있도록 구성되었습니다.
- ✅ OCP의 기본 개념 이해
- ✅ OCP를 위반하는 코드의 문제점 파악
- ✅ OCP를 준수하는 설계 패턴 학습
- ✅ 전략 패턴을 활용한 확장 가능한 구조 설계
- ✅ instanceof와 switch문 사용의 문제점 이해
📁 src/main/java/com/designpattern/
├── 📁 bad/ # ❌ OCP 위반 예제
│ ├── controller/
│ │ └── BadOrderController.java
│ ├── entity/
│ │ ├── Order.java # @Entity(name = "BadOrder")
│ │ └── OrderItem.java # @Entity(name = "BadOrderItem")
│ ├── repository/
│ │ └── BadOrderRepository.java
│ └── service/
│ ├── BadOrderProcessingService.java
│ ├── BadNotificationService.java
│ └── notification data classes...
│
├── 📁 good/ # ✅ OCP 준수 예제
│ ├── controller/
│ │ └── GoodOrderController.java
│ ├── entity/
│ │ ├── Order.java # @Entity(name = "GoodOrder")
│ │ └── OrderItem.java # @Entity(name = "GoodOrderItem")
│ ├── repository/
│ │ └── GoodOrderRepository.java
│ └── service/
│ ├── GoodOrderProcessingService.java
│ ├── strategy/ # 전략 패턴 구현
│ │ ├── payment/
│ │ ├── notification/
│ │ ├── customertype/
│ │ └── category/
│ └── registry/
│ └── StrategyRegistry.java
│
└── OcpLearningApplication.java # 🚀 메인 애플리케이션
// ❌ 나쁜 예제
public void sendNotification(Object notification, Order order) {
if (notification instanceof EmailNotificationData) {
// 이메일 처리
} else if (notification instanceof SmsNotificationData) {
// SMS 처리
} else if (notification instanceof SlackNotificationData) {
// 슬랙 처리 - 새로 추가시 기존 코드 수정!
}
// 새로운 알림 타입 추가시 이 메서드를 계속 수정해야 함
}// ❌ 나쁜 예제
public void processPayment(String paymentMethod, Order order) {
switch (paymentMethod) {
case "CREDIT_CARD":
// 신용카드 처리
break;
case "PAYPAL":
// PayPal 처리
break;
case "CRYPTO": // 새로 추가된 결제 수단
// 암호화폐 처리 - 기존 switch문 수정!
break;
}
}// ❌ 나쁜 예제 - 하나의 클래스에 모든 결제 로직이 포함됨
public class BadOrderProcessingService {
// 신용카드, 직불카드, PayPal, 은행 이체 등 모든 결제 로직이 여기에...
// 새로운 결제 수단이 추가될 때마다 이 클래스를 수정해야 함
}// ✅ 좋은 예제
public interface PaymentStrategy {
BigDecimal processPayment(Order order);
}
@Component("CREDIT_CARD")
public class CreditCardPaymentStrategy implements PaymentStrategy {
@Override
public BigDecimal processPayment(Order order) {
// 신용카드 처리 로직
}
}
// 새로운 결제 수단 추가시 기존 코드 수정 없이 확장 가능
@Component("CRYPTO")
public class CryptoPaymentStrategy implements PaymentStrategy {
@Override
public BigDecimal processPayment(Order order) {
// 암호화폐 처리 로직
}
}// ✅ 좋은 예제
@Component
public class StrategyRegistry {
// Spring이 자동으로 모든 PaymentStrategy 구현체를 주입
private final Map<String, PaymentStrategy> paymentStrategies;
public PaymentStrategy getPaymentStrategy(String type) {
return paymentStrategies.get(type);
}
}// ✅ 좋은 예제
@Service
public class GoodOrderProcessingService {
private final StrategyRegistry strategyRegistry;
public void processOrder(Order order) {
// 런타임에 적절한 전략을 선택하여 사용
PaymentStrategy strategy = strategyRegistry.getPaymentStrategy(order.getPaymentMethod());
strategy.processPayment(order);
}
}-
새로운 기능 추가시 기존 코드를 수정하지 말 것
- 새로운 클래스나 인터페이스 구현체를 추가하여 확장
-
instanceof, switch문 사용 최소화
- 다형성과 전략 패턴을 활용하여 대체
-
인터페이스와 추상화 활용
- 구체적인 클래스보다는 인터페이스에 의존
-
단일 책임 원칙과 함께 고려
- 각 클래스는 하나의 책임만 가져야 함
| ❌ 피해야 할 패턴 | ✅ 권장하는 패턴 |
|---|---|
instanceof 체크 |
다형성 활용 |
switch/case 문 |
전략 패턴 |
| 거대한 if-else | 인터페이스 구현 |
| 하드코딩된 타입 | 동적 전략 선택 |
| 단일 클래스에 모든 로직 | 역할별 클래스 분리 |
git clone <repository-url>
cd solid-OCP./gradlew build./gradlew bootRun- 🔴 새로운 기능 추가시 기존 코드 수정 필요
- 🔴 코드 복잡도 증가
- 🔴 테스트하기 어려운 구조
- 🔴 유지보수 비용 증가
- 🟢 기존 코드 수정 없이 확장 가능
- 🟢 깔끔하고 이해하기 쉬운 구조
- 🟢 각 기능별로 독립적 테스트 가능
- 🟢 유지보수 용이성 향상
- Language: Java 17+
- Framework: Spring Boot 3.4.4
- ORM: Hibernate 6.6.11
- Database: H2 (In-Memory)
- Build Tool: Gradle
- Template Engine: Thymeleaf
이 프로젝트는 MIT 라이센스 하에 배포됩니다. 자세한 내용은 LICENSE 파일을 참조하세요.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
프로젝트에 대한 질문이나 제안사항이 있으시면 언제든지 연락주세요!
🎯 "확장에는 열려있고, 수정에는 닫혀있어라" - OCP
Made with ❤️ for better software design