Strategy Pattern 이란?
객체를 독립적으로 선택하고 사용할 수 있도록 하는 디자인 패턴입니다.
이 패턴은 전략을 인터페이스로 정의하고, 인터페이스의 구현체를 만들어 이를 상황에 맞게 사용하는 방법입니다.
전략 패턴을 사용하면 런타임 시점에 알고리즘을(실 서비스에서 쿠폰이나 할인율 같은 경우) 선택할 수 있으므로 유연하게 동작할 수 있습니다.
런타임 시점에 알고리즘을 선택할 수 있다는 말은
실행 시간에(런타임 시점) 코드에서 사용할 객체를 선택할 수 있다는 것을 의미합니다.
일반적으로 객체는 컴파일 시점에 결정됩니다. 즉, 코드가 컴파일되는 시점에 사용할 객체가 결정되고, 이후에는 변결할 수 없게되는데 전략 패턴을 사용하면 런타임 시점에 다른 객체를 선택하여 사용할 수 있습니다.
전략 패턴의 장점
- 런타임 시점에 알고리즘을 교체할 수 있습니다.
- SOILD 원칙 중 OCP 원칙을 깨지 않으면서 개발을 할 수 있습니다.
Strategy Pattern 예제
Coupon Interface
package strategy;
public interface Coupon {
int calc(ProductEntity product);
}
고정 할인율을 적용할 쿠폰 interface의 구현체
package strategy;
public class FixedDiscountCoupon implements Coupon {
private int discount;
public FixedDiscountCoupon(int discount) {
this.discount = discount;
}
@Override
public int calc(ProductEntity product) {
return Math.max(product.getPrice() - discount, 0);
}
}
백분율 할인율을 적용할 쿠폰 interface의 구현체
package strategy;
public class PercentageCoupon implements Coupon {
private int ratio;
public PercentageCoupon(int ratio) {
this.ratio = ratio;
}
@Override
public int calc(ProductEntity product) {
int discountAmount = (int) (product.getPrice() * (double) ratio / 100);
return Math.max(product.getPrice() - discountAmount, 0);
}
}
쿠폰의 정보를 가져오는 레포지토리
실제 데이터 베이스와 연동하지 않고 mockRepository를 만들어서 진행하였습니다.
package strategy;
import java.util.Optional;
public interface CouponRepository {
Optional<CouponEntity> findById(Long id);
}
package strategy;
import java.util.Optional;
public class MockCouponRepository implements CouponRepository {
@Override
public Optional<CouponEntity> findById(Long id) {
if (Objects.equals(id, 1L)) {
return Optional.of(new CouponEntity(1L, CouponType.FIX));
}
if (Objects.equals(id, 2L)) {
return Optional.of(new CouponEntity(2L, CouponType.PERCENTAGE));
}
return Optional.empty();
}
}
쿠폰 타입
데이터 베이스에 저장된 쿠폰 타입에 따라 위에서 쿠폰 인터페이스를 구현한 구현체를 반환하기 위한 enum class
package strategy;
public enum CouponType {
FIX(new FixedDiscountCoupon(1000)),
PERCENTAGE(new PercentageCoupon(10));
private Coupon coupon;
CouponType(Coupon coupon) {
this.coupon = coupon;
}
public Coupon getCoupon() {
return coupon;
}
}
Coupon Entity
package strategy;
public class CouponEntity {
private Long id;
private CouponType couponType;
public CouponEntity(Long id, CouponType couponType) {
this.id = id;
this.couponType = couponType;
}
public Long getId() {
return id;
}
public CouponType getCouponType() {
return couponType;
}
}
ProductEntity
package strategy;
public class ProductEntity {
private String name;
private int price;
public ProductEntity(String name, int price) {
this.name = name;
this.price = price;
}
public int discount(Coupon coupon) {
return coupon.calc(this);
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
쿠폰을 반환해 줄 CouponService
package strategy;
public class CouponService {
private CouponRepository couponRepository = new MockCouponRepository();
public Coupon getCoupon(long couponId) {
CouponEntity coupon = couponRepository.findById(couponId)
.orElseThrow(() -> new RuntimeException("없는 쿠폰번호 아이디입니다."));
return coupon.getCouponType().getCoupon();
}
}
동작 확인을 위한 Main code
package strategy;
public class Main {
private static CouponService couponService = new CouponService();
public static void main(String[] args) {
ProductEntity synthaProtein = new ProductEntity("신타 6 아이솔레이트", 77800);
Coupon fixedDiscountCoupon = couponService.getCoupon(1L);
System.out.println("신타 6 고정 1000 쿠폰 사용 : " + synthaProtein.discount(fixedDiscountCoupon));
Coupon percentageDiscountCoupon = couponService.getCoupon(2L);
System.out.println("신타 6 10프로 할인 가격 : " + synthaProtein.discount(percentageDiscountCoupon));
}
}
위 코드를 실행하면 고정 할인 쿠폰을 사용 시 1000원이 할인된 가격 76800원이 콘솔 창에 찍히고
백분율 할인율 쿠폰을 사용 시 10프로 할인된 가격인 70200원이 콘솔 창에 찍히는 것을 확인할 수 있습니다.
실 서비스에서는 이렇게 간단하게 쿠폰을 적용할 일이 없겠지만 상품에 적용할 수 있는 쿠폰이 여러 가지 있을 경우 전략 패턴을 사용하여 실행 시점에 동적으로 구현체를 변경하여 다른 할인율을 적용할 수 있게 됩니다. 이 패턴은 실무에서 굉장히 많이 쓰이는 패턴이라서 꼭 알아두면 좋을 거 같다고 생각합니다.
'ComputerScience > Design Pattern' 카테고리의 다른 글
Adaptor Pattern (0) | 2023.03.06 |
---|---|
Facade Pattern (0) | 2023.02.26 |
디자인 패턴 - 디자인 원칙 (0) | 2023.02.14 |
Builder Pattern (0) | 2023.02.01 |
Singleton Pattern (0) | 2023.01.30 |