Skip to content

hasune/solid-ocp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🏗️ SOLID OCP (Open-Closed Principle) Learning Project

Java Spring Boot Hibernate License Build

📚 프로젝트 개요

**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  # 🚀 메인 애플리케이션

❌ 나쁜 예제: OCP 위반 사례

🚨 주요 문제점

1. instanceof 남용

// ❌ 나쁜 예제
public void sendNotification(Object notification, Order order) {
    if (notification instanceof EmailNotificationData) {
        // 이메일 처리
    } else if (notification instanceof SmsNotificationData) {
        // SMS 처리
    } else if (notification instanceof SlackNotificationData) {
        // 슬랙 처리 - 새로 추가시 기존 코드 수정!
    }
    // 새로운 알림 타입 추가시 이 메서드를 계속 수정해야 함
}

2. Switch 문 남용

// ❌ 나쁜 예제
public void processPayment(String paymentMethod, Order order) {
    switch (paymentMethod) {
        case "CREDIT_CARD":
            // 신용카드 처리
            break;
        case "PAYPAL":
            // PayPal 처리
            break;
        case "CRYPTO":  // 새로 추가된 결제 수단
            // 암호화폐 처리 - 기존 switch문 수정!
            break;
    }
}

3. 단일 클래스에 모든 로직 집중

// ❌ 나쁜 예제 - 하나의 클래스에 모든 결제 로직이 포함됨
public class BadOrderProcessingService {
    // 신용카드, 직불카드, PayPal, 은행 이체 등 모든 결제 로직이 여기에...
    // 새로운 결제 수단이 추가될 때마다 이 클래스를 수정해야 함
}

✅ 좋은 예제: OCP 준수 사례

🎉 주요 해결책

1. 전략 패턴 활용

// ✅ 좋은 예제
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) {
        // 암호화폐 처리 로직
    }
}

2. Strategy Registry 패턴

// ✅ 좋은 예제
@Component
public class StrategyRegistry {
    // Spring이 자동으로 모든 PaymentStrategy 구현체를 주입
    private final Map<String, PaymentStrategy> paymentStrategies;
    
    public PaymentStrategy getPaymentStrategy(String type) {
        return paymentStrategies.get(type);
    }
}

3. 확장 가능한 서비스 구조

// ✅ 좋은 예제
@Service
public class GoodOrderProcessingService {
    private final StrategyRegistry strategyRegistry;
    
    public void processOrder(Order order) {
        // 런타임에 적절한 전략을 선택하여 사용
        PaymentStrategy strategy = strategyRegistry.getPaymentStrategy(order.getPaymentMethod());
        strategy.processPayment(order);
    }
}

🔍 OCP 준수를 위한 핵심 포인트

✨ 반드시 지켜야 할 원칙

  1. 새로운 기능 추가시 기존 코드를 수정하지 말 것

    • 새로운 클래스나 인터페이스 구현체를 추가하여 확장
  2. instanceof, switch문 사용 최소화

    • 다형성과 전략 패턴을 활용하여 대체
  3. 인터페이스와 추상화 활용

    • 구체적인 클래스보다는 인터페이스에 의존
  4. 단일 책임 원칙과 함께 고려

    • 각 클래스는 하나의 책임만 가져야 함

⚠️ 주의해야 할 안티패턴

❌ 피해야 할 패턴 ✅ 권장하는 패턴
instanceof 체크 다형성 활용
switch/case 전략 패턴
거대한 if-else 인터페이스 구현
하드코딩된 타입 동적 전략 선택
단일 클래스에 모든 로직 역할별 클래스 분리

🚀 실행 방법

1. 프로젝트 클론

git clone <repository-url>
cd solid-OCP

2. 의존성 설치

./gradlew build

3. 애플리케이션 실행

./gradlew bootRun

4. 엔드포인트 접근

📊 학습 효과

Before (Bad Example)

  • 🔴 새로운 기능 추가시 기존 코드 수정 필요
  • 🔴 코드 복잡도 증가
  • 🔴 테스트하기 어려운 구조
  • 🔴 유지보수 비용 증가

After (Good Example)

  • 🟢 기존 코드 수정 없이 확장 가능
  • 🟢 깔끔하고 이해하기 쉬운 구조
  • 🟢 각 기능별로 독립적 테스트 가능
  • 🟢 유지보수 용이성 향상

🛠️ 기술 스택

  • 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 파일을 참조하세요.

🤝 기여하기

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📞 문의

프로젝트에 대한 질문이나 제안사항이 있으시면 언제든지 연락주세요!


🎯 "확장에는 열려있고, 수정에는 닫혀있어라" - OCP

Made with ❤️ for better software design

About

SOLID - OCP (Open/Closed Principle, 개방/폐쇄 원칙)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors