Java Enum을 활용한 전략 패턴(Strategy Pattern) 구현
전략 패턴(Strategy Pattern)은 알고리즘 군(Strategy)을 정의하고, 이를 개별 클래스로 캡슐화한 후, 필요할 때 교체할 수 있도록 만드는 패턴입니다.
Enum을 활용하면 불필요한 클래스를 줄이고, 코드의 가독성과 유지보수성을 높일 수 있습니다.
1️⃣ 기본적인 전략 패턴
전략 패턴은 일반적으로 다음과 같은 구조를 가집니다.
- Strategy 인터페이스 → 전략의 공통 인터페이스
- ConcreteStrategy 구현체 → 각각의 전략을 개별 클래스로 구현
- Context → 전략을 실행하는 역할
✅ 클래스 기반 전략 패턴 예제
// 전략 인터페이스
interface PaymentStrategy {
void pay(int amount);
}
// 개별 전략 (Concrete Strategy)
class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
}
class PayPalPayment implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal.");
}
}
// Context (전략을 사용하는 곳)
class PaymentContext {
private PaymentStrategy strategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(int amount) {
strategy.pay(amount);
}
}
// 실행
public class Main {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
context.setPaymentStrategy(new CreditCardPayment());
context.executePayment(1000);
context.setPaymentStrategy(new PayPalPayment());
context.executePayment(500);
}
}
📌 실행 결과
Paid 1000 using Credit Card.
Paid 500 using PayPal.
➡️ 위 코드의 문제점
- 전략 클래스(CreditCardPayment, PayPalPayment)가 많아질수록 관리가 어려움
- setPaymentStrategy()로 객체를 계속 변경해야 함
- 전략을 간단히 정의하고 싶다면 Enum을 활용할 수 있음
2️⃣ Enum을 활용한 전략 패턴 구현
클래스 기반 구현을 Enum으로 대체하면 코드가 간결해지고, 불필요한 객체 생성을 방지할 수 있습니다.
✅ Enum 기반 전략 패턴
// 전략 인터페이스
interface PaymentStrategy {
void pay(int amount);
}
// Enum으로 전략 정의
enum PaymentMethod implements PaymentStrategy {
CREDIT_CARD {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
},
PAYPAL {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal.");
}
},
BITCOIN {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Bitcoin.");
}
}
}
// 실행 클래스
public class Main {
public static void main(String[] args) {
PaymentMethod payment = PaymentMethod.CREDIT_CARD;
payment.pay(1000); // "Paid 1000 using Credit Card."
payment = PaymentMethod.PAYPAL;
payment.pay(500); // "Paid 500 using PayPal."
payment = PaymentMethod.BITCOIN;
payment.pay(300); // "Paid 300 using Bitcoin."
}
}
📌 실행 결과
Paid 1000 using Credit Card.
Paid 500 using PayPal.
Paid 300 using Bitcoin.
3️⃣ Enum을 활용한 전략 패턴의 장점
✅ 불필요한 클래스 제거 → 개별 클래스를 만들 필요 없이 enum 내에서 전략을 정의할 수 있음
✅ 싱글턴 방식 유지 → Enum은 자바에서 싱글턴으로 보장되므로 별도의 객체 생성을 방지할 수 있음
✅ 코드 간결성 증가 → 하나의 파일에서 모든 전략을 정의할 수 있음
✅ Switch 문 제거 가능 → Enum의 다형성을 활용하여 if-else나 switch 없이 직접 실행 가능
4️⃣ Enum 전략 패턴 + Context 적용
실제 프로젝트에서는 전략을 실행하는 Context 클래스를 만들 수도 있습니다.
✅ Enum + Context 적용
// 전략 인터페이스
interface DiscountStrategy {
double applyDiscount(double price);
}
// Enum으로 할인 전략 정의
enum DiscountType implements DiscountStrategy {
NONE {
@Override
public double applyDiscount(double price) {
return price; // 할인 없음
}
},
SEASONAL {
@Override
public double applyDiscount(double price) {
return price * 0.9; // 10% 할인
}
},
BLACK_FRIDAY {
@Override
public double applyDiscount(double price) {
return price * 0.7; // 30% 할인
}
}
}
// Context (전략 실행)
class PriceCalculator {
private DiscountStrategy discountStrategy;
public PriceCalculator(DiscountType discountType) {
this.discountStrategy = discountType; // Enum 활용
}
public double calculateFinalPrice(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 실행 클래스
public class Main {
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator(DiscountType.SEASONAL);
System.out.println("Final Price: " + calculator.calculateFinalPrice(100.0));
calculator = new PriceCalculator(DiscountType.BLACK_FRIDAY);
System.out.println("Final Price: " + calculator.calculateFinalPrice(100.0));
}
}
📌 실행 결과
Final Price: 90.0
Final Price: 70.0
➡️ DiscountType.SEASONAL을 사용하면 10% 할인, BLACK_FRIDAY를 사용하면 30% 할인이 적용됩니다.
🔥 정리
| 방식 | 클래스 기반 전략 패턴 | Enum 기반 전략 패턴 |
| 구현 방식 | 개별 클래스로 전략 구현 | enum 내부에서 전략 구현 |
| 코드 양 | 비교적 많음 | 간결함 |
| 객체 생성 | 필요함 (객체 생성 필요) | 불필요 (Enum은 싱글턴) |
| 유지보수 | 전략 추가 시 클래스 추가 필요 | Enum 상수만 추가하면 됨 |
| 다형성 활용 | 가능 | 가능 |
✅ Enum을 활용하면 불필요한 객체 생성을 줄이고, 코드가 깔끔해짐
✅ 전략을 쉽게 확장하고 유지보수할 수 있음
✅ Switch 문이나 if-else 없이 깔끔하게 처리 가능 🚀
📦 패키지 구성 (Package Structure)
Enum을 활용한 전략 패턴을 구현할 때, 패키지를 적절히 분리하면 코드의 유지보수성과 가독성이 더욱 향상됩니다.
아래와 같은 패키지 구조를 추천합니다.
com.example.strategy
│── Main.java // 실행 클래스
│
├── context
│ ├── PriceCalculator.java // Context 역할 (전략 실행)
│
├── strategy
│ ├── DiscountStrategy.java // 전략 인터페이스
│ ├── DiscountType.java // Enum 기반 전략 구현
│
└── payment
├── PaymentStrategy.java // 결제 전략 인터페이스
├── PaymentMethod.java // Enum 기반 결제 전략
📌 패키지별 상세 설명
✅ com.example.strategy
- Main.java: 전략 패턴을 테스트하는 실행 클래스
✅ com.example.strategy.context
- PriceCalculator.java: 할인 전략을 실행하는 역할 (Context)
✅ com.example.strategy.strategy
- DiscountStrategy.java: 할인 전략을 위한 인터페이스
- DiscountType.java: Enum을 활용한 할인 전략 정의
✅ com.example.strategy.payment
- PaymentStrategy.java: 결제 전략 인터페이스
- PaymentMethod.java: Enum을 활용한 결제 전략 정의
📌 실제 패키지별 코드
1️⃣ com.example.strategy.Main.java (실행 클래스)
package com.example.strategy;
import com.example.strategy.context.PriceCalculator;
import com.example.strategy.strategy.DiscountType;
import com.example.strategy.payment.PaymentMethod;
public class Main {
public static void main(String[] args) {
// 할인 전략 테스트
PriceCalculator calculator = new PriceCalculator(DiscountType.SEASONAL);
System.out.println("Final Price: " + calculator.calculateFinalPrice(100.0));
calculator = new PriceCalculator(DiscountType.BLACK_FRIDAY);
System.out.println("Final Price: " + calculator.calculateFinalPrice(100.0));
// 결제 전략 테스트
PaymentMethod payment = PaymentMethod.CREDIT_CARD;
payment.pay(1000);
payment = PaymentMethod.PAYPAL;
payment.pay(500);
}
}
2️⃣ com.example.strategy.context.PriceCalculator.java (Context 클래스)
package com.example.strategy.context;
import com.example.strategy.strategy.DiscountStrategy;
import com.example.strategy.strategy.DiscountType;
public class PriceCalculator {
private DiscountStrategy discountStrategy;
public PriceCalculator(DiscountType discountType) {
this.discountStrategy = discountType; // Enum 활용
}
public double calculateFinalPrice(double price) {
return discountStrategy.applyDiscount(price);
}
}
3️⃣ com.example.strategy.strategy.DiscountStrategy.java (할인 전략 인터페이스)
package com.example.strategy.strategy;
public interface DiscountStrategy {
double applyDiscount(double price);
}
4️⃣ com.example.strategy.strategy.DiscountType.java (Enum을 활용한 할인 전략)
package com.example.strategy.strategy;
public enum DiscountType implements DiscountStrategy {
NONE {
@Override
public double applyDiscount(double price) {
return price; // 할인 없음
}
},
SEASONAL {
@Override
public double applyDiscount(double price) {
return price * 0.9; // 10% 할인
}
},
BLACK_FRIDAY {
@Override
public double applyDiscount(double price) {
return price * 0.7; // 30% 할인
}
}
}
5️⃣ com.example.strategy.payment.PaymentStrategy.java (결제 전략 인터페이스)
package com.example.strategy.payment;
public interface PaymentStrategy {
void pay(int amount);
}
6️⃣ com.example.strategy.payment.PaymentMethod.java (Enum을 활용한 결제 전략)
package com.example.strategy.payment;
public enum PaymentMethod implements PaymentStrategy {
CREDIT_CARD {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
},
PAYPAL {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal.");
}
},
BITCOIN {
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Bitcoin.");
}
}
}
🎯 패키지 구성의 장점
✅ 모듈화된 설계 → 전략(할인, 결제)과 컨텍스트(계산기)를 분리하여 유지보수성 향상
✅ Enum 활용으로 가독성 증가 → 개별 클래스를 만들 필요 없이 Enum 내에서 전략을 정의
✅ 새로운 전략 추가 용이 → 새로운 할인 정책, 결제 수단을 쉽게 추가 가능
이제 **DiscountType**에 새로운 할인 전략을 추가하거나, **PaymentMethod**에 새로운 결제 수단을 추가하는 것이 매우 쉬워집니다!
'개발 > 자바' 카테고리의 다른 글
| 컬렉션 사용의 필요성 (0) | 2025.02.20 |
|---|---|
| 오버로딩(Overloading)과 오버라이딩(Overriding)의 차이점 (0) | 2025.02.20 |
| Java Enum에서 인터페이스 구현하기 (0) | 2025.02.20 |
| 쇼핑몰 주문 정보를 추상 클래스를 이용한 구현 예제 (0) | 2025.02.19 |
| OCP(Open-Closed Principle), SRP(Single Responsibility Principle) (0) | 2025.02.19 |