java策略模式实战之优惠券

策略模式 (Strategy Pattern)是一种行为型设计模式。它的核心思想是:定义一系列算法,将每个算法封装起来,让它们可以相互替换,使得算法的变化不会影响到使用算法的客户端

进行实战抽象优惠券的发券与核销逻辑,支持满减券、折扣券、兑换券等不同类型,并具备良好的扩展性。


1. 优惠券类型枚举

java

arduino 复制代码
public enum CouponType {
    FULL_REDUCTION,   // 满减券
    DISCOUNT,         // 折扣券
    EXCHANGE          // 兑换券
}

2. 优惠券模板(CouponTemplate)

模板定义了优惠券的基本属性及规则参数。

java

typescript 复制代码
import java.math.BigDecimal;

public class CouponTemplate {
    private Long templateId;
    private String name;
    private CouponType type;
    private BigDecimal thresholdAmount;   // 满减门槛,仅满减券使用
    private BigDecimal reduceAmount;      // 减金额,仅满减券使用
    private BigDecimal discountRate;      // 折扣率(0-1),仅折扣券使用
    private String exchangeGoodsId;       // 可兑换商品ID,仅兑换券使用

    // getter/setter 省略
}

3. 用户券(UserCoupon)

用户领取后生成的实例,包含状态、使用时间等。

java

kotlin 复制代码
import java.time.LocalDateTime;

public class UserCoupon {
    private Long userCouponId;
    private Long userId;
    private Long templateId;
    private CouponType type;
    private CouponStatus status;        // UNUSED, USED, EXPIRED
    private LocalDateTime expireTime;
    private LocalDateTime useTime;

    // 冗余模板规则字段,便于独立判断(也可以关联模板动态获取)
    private BigDecimal thresholdAmount;
    private BigDecimal reduceAmount;
    private BigDecimal discountRate;
    private String exchangeGoodsId;

    // getter/setter 省略
}

enum CouponStatus {
    UNUSED, USED, EXPIRED
}

4. 策略接口

定义发券(发放时做规则校验、填充用户券信息)和核销(使用时做校验、扣减)两个核心行为。

java

arduino 复制代码
public interface CouponStrategy {

    /**
     * 发放校验:根据模板和用户上下文判断是否可发放
     */
    boolean canIssue(CouponTemplate template, UserContext userContext);

    /**
     * 发放动作:生成用户券,填充特定字段
     */
    UserCoupon issue(CouponTemplate template, UserContext userContext);

    /**
     * 核销校验:使用前验证是否满足规则(如满减金额、折扣商品等)
     */
    boolean canUse(UserCoupon userCoupon, OrderContext orderContext);

    /**
     * 核销动作:计算优惠金额,并更新用户券状态
     */
    BigDecimal use(UserCoupon userCoupon, OrderContext orderContext);
}

5. 上下文对象

为了演示,定义简单上下文。

java

kotlin 复制代码
public class UserContext {
    private Long userId;
    private Integer userLevel;
    // 可根据需要扩展
    // getter/setter 省略
}

public class OrderContext {
    private BigDecimal orderAmount;
    private List<OrderItem> items;
    // 省略 getter/setter
}

public class OrderItem {
    private String goodsId;
    private BigDecimal amount;
    // getter/setter 省略
}

6. 具体策略实现

6.1 满减券策略

java

typescript 复制代码
public class FullReductionStrategy implements CouponStrategy {

    @Override
    public boolean canIssue(CouponTemplate template, UserContext userContext) {
        // 简单示例:无额外限制
        return true;
    }

    @Override
    public UserCoupon issue(CouponTemplate template, UserContext userContext) {
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userContext.getUserId());
        userCoupon.setTemplateId(template.getTemplateId());
        userCoupon.setType(template.getType());
        userCoupon.setThresholdAmount(template.getThresholdAmount());
        userCoupon.setReduceAmount(template.getReduceAmount());
        // 设置过期时间等...
        return userCoupon;
    }

    @Override
    public boolean canUse(UserCoupon userCoupon, OrderContext orderContext) {
        // 满减券:订单金额需满足门槛
        return orderContext.getOrderAmount().compareTo(userCoupon.getThresholdAmount()) >= 0;
    }

    @Override
    public BigDecimal use(UserCoupon userCoupon, OrderContext orderContext) {
        // 核销:直接返回减金额
        return userCoupon.getReduceAmount();
    }
}

6.2 折扣券策略

java

typescript 复制代码
public class DiscountStrategy implements CouponStrategy {

    @Override
    public boolean canIssue(CouponTemplate template, UserContext userContext) {
        return true;
    }

    @Override
    public UserCoupon issue(CouponTemplate template, UserContext userContext) {
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userContext.getUserId());
        userCoupon.setTemplateId(template.getTemplateId());
        userCoupon.setType(template.getType());
        userCoupon.setDiscountRate(template.getDiscountRate());
        return userCoupon;
    }

    @Override
    public boolean canUse(UserCoupon userCoupon, OrderContext orderContext) {
        // 折扣券通常不限制门槛,但可以按需扩展
        return true;
    }

    @Override
    public BigDecimal use(UserCoupon userCoupon, OrderContext orderContext) {
        // 折扣:订单金额 * 折扣率
        BigDecimal discount = orderContext.getOrderAmount()
                .multiply(BigDecimal.ONE.subtract(userCoupon.getDiscountRate()));
        return discount;
    }
}

6.3 兑换券策略

java

typescript 复制代码
public class ExchangeStrategy implements CouponStrategy {

    @Override
    public boolean canIssue(CouponTemplate template, UserContext userContext) {
        return true;
    }

    @Override
    public UserCoupon issue(CouponTemplate template, UserContext userContext) {
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(userContext.getUserId());
        userCoupon.setTemplateId(template.getTemplateId());
        userCoupon.setType(template.getType());
        userCoupon.setExchangeGoodsId(template.getExchangeGoodsId());
        return userCoupon;
    }

    @Override
    public boolean canUse(UserCoupon userCoupon, OrderContext orderContext) {
        // 兑换券:检查订单中是否包含对应商品
        return orderContext.getItems().stream()
                .anyMatch(item -> userCoupon.getExchangeGoodsId().equals(item.getGoodsId()));
    }

    @Override
    public BigDecimal use(UserCoupon userCoupon, OrderContext orderContext) {
        // 兑换券:优惠金额等于该商品价格
        return orderContext.getItems().stream()
                .filter(item -> userCoupon.getExchangeGoodsId().equals(item.getGoodsId()))
                .map(OrderItem::getAmount)
                .findFirst()
                .orElse(BigDecimal.ZERO);
    }
}

7. 策略工厂 & 上下文

提供统一的入口,根据券类型获取对应策略。

java

java 复制代码
import java.util.EnumMap;
import java.util.Map;

public class CouponStrategyFactory {
    private static final Map<CouponType, CouponStrategy> STRATEGY_MAP = new EnumMap<>(CouponType.class);

    static {
        STRATEGY_MAP.put(CouponType.FULL_REDUCTION, new FullReductionStrategy());
        STRATEGY_MAP.put(CouponType.DISCOUNT, new DiscountStrategy());
        STRATEGY_MAP.put(CouponType.EXCHANGE, new ExchangeStrategy());
    }

    public static CouponStrategy getStrategy(CouponType type) {
        CouponStrategy strategy = STRATEGY_MAP.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported coupon type: " + type);
        }
        return strategy;
    }
}

8. 使用示例

java

arduino 复制代码
public class CouponService {

    // 发券
    public UserCoupon issueCoupon(CouponTemplate template, UserContext userContext) {
        CouponStrategy strategy = CouponStrategyFactory.getStrategy(template.getType());
        if (!strategy.canIssue(template, userContext)) {
            throw new BusinessException("用户不符合发券条件");
        }
        return strategy.issue(template, userContext);
    }

    // 核销
    public BigDecimal useCoupon(UserCoupon userCoupon, OrderContext orderContext) {
        CouponStrategy strategy = CouponStrategyFactory.getStrategy(userCoupon.getType());
        if (!strategy.canUse(userCoupon, orderContext)) {
            throw new BusinessException("优惠券使用条件不满足");
        }
        BigDecimal discount = strategy.use(userCoupon, orderContext);
        // 更新用户券状态为已使用
        userCoupon.setStatus(CouponStatus.USED);
        // 持久化...
        return discount;
    }
}

设计要点总结

  1. 开闭原则:新增券类型只需添加枚举、实现新策略类,无需修改已有代码。
  2. 单一职责:每种策略只负责一种券的发放与核销逻辑。
  3. 工厂模式:将策略创建集中管理,调用方无需关心具体实现。
  4. 上下文隔离 :通过 UserContextOrderContext 传递动态数据,避免策略耦合业务对象。
相关推荐
一灯架构11 小时前
90%的人答错!一文带你彻底搞懂ArrayList
java·后端
Y40900111 小时前
【多线程】线程安全(1)
java·开发语言·jvm
布局呆星12 小时前
SpringBoot 基础入门
java·spring boot·spring
mldong12 小时前
Python开发者狂喜!200+课时FastAPI全栈实战合集,10大模块持续更新中🔥
后端
风吹迎面入袖凉12 小时前
【Redis】Redisson的可重入锁原理
java·redis
w61001046612 小时前
cka-2026-ConfigMap
java·linux·cka·configmap
GreenTea13 小时前
从 Claw-Code 看 AI 驱动的大型项目开发:2 人 + 10 个自治 Agent 如何产出 48K 行 Rust 代码
前端·人工智能·后端
语戚13 小时前
力扣 968. 监控二叉树 —— 贪心 & 树形 DP 双解法递归 + 非递归全解(Java 实现)
java·算法·leetcode·贪心算法·动态规划·力扣·
quxuexi13 小时前
网络通信安全与可靠传输:从加密到认证,从状态码到可靠传输
java·安全·web