개발/자바

Java Enum을 활용한 전략 패턴(Strategy Pattern) 구현

피터JK 2025. 2. 20. 09:48
728x90

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**에 새로운 결제 수단을 추가하는 것이 매우 쉬워집니다! 

728x90