策略模式是一种行为型设计模式,它就像是一个可以随时更换的工具箱。想象一下,您是一名厨师,面对不同的食材需要使用不同的切菜工具:
在策略模式中:
让我用Java代码来展示这个厨房的例子:
// 策略接口 - 所有刀具的共同规范
interface CuttingStrategy {
void cut(String food);
}
// 具体策略 - 菜刀
class MeatKnife implements CuttingStrategy {
@Override
public void cut(String food) {
System.out.println("用菜刀切" + food + ",力量大,切得断骨头");
}
}
// 具体策略 - 面刀
class DoughKnife implements CuttingStrategy {
@Override
public void cut(String food) {
System.out.println("用面刀切" + food + ",宽大平滑,切面团不粘刀");
}
}
// 具体策略 - 水果刀
class FruitKnife implements CuttingStrategy {
@Override
public void cut(String food) {
System.out.println("用水果刀切" + food + ",小巧灵活,适合精细操作");
}
}
// 环境类 - 厨师
class Chef {
private CuttingStrategy knife;
// 换刀
public void setKnife(CuttingStrategy knife) {
this.knife = knife;
}
// 切食材
public void cutFood(String food) {
if (knife == null) {
System.out.println("请先选择一把刀!");
return;
}
knife.cut(food);
}
}
// 测试
public class StrategyPatternDemo {
public static void main(String[] args) {
Chef chef = new Chef();
// 切肉
chef.setKnife(new MeatKnife());
chef.cutFood("牛肉");
// 切面团
chef.setKnife(new DoughKnife());
chef.cutFood("面团");
// 切水果
chef.setKnife(new FruitKnife());
chef.cutFood("苹果");
}
}
策略模式的优点:
这就是策略模式的精髓:将变化的部分(各种切东西的方法)封装起来,使它们可以互相替换,同时保持接口的统一,让使用者(厨师)能够根据不同情况选择不同的策略。
某电商平台需要实现一个灵活的促销系统,能够针对不同用户、不同时期应用不同的促销策略:
系统需要能够灵活切换这些促销策略,甚至组合使用,而不需要修改现有代码。
我们将使用策略模式来实现这个促销系统:
PromotionStrategy
,定义计算折扣的方法PromotionStrategyFactory
来管理和获取策略实例PromotionContext
处理策略的应用CompositePromotionStrategy
// 1. 定义促销策略接口
package com.ecommerce.promotion;
import java.math.BigDecimal;
public interface PromotionStrategy {
/**
* 计算促销后的价格
* @param originalPrice 原始价格
* @param order 订单信息
* @return 促销后的价格
*/
BigDecimal calculatePrice(BigDecimal originalPrice, Order order);
/**
* 获取促销描述
* @return 促销活动的描述
*/
String getDescription();
}
// 2. 订单类
package com.ecommerce.promotion;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Order {
private String orderId;
private Long userId;
private String userLevel; // 普通用户、金卡会员、钻石会员等
private List<OrderItem> items;
private Date createTime;
public Order(String orderId, Long userId, String userLevel) {
this.orderId = orderId;
this.userId = userId;
this.userLevel = userLevel;
this.items = new ArrayList<>();
this.createTime = new Date();
}
public void addItem(OrderItem item) {
items.add(item);
}
public BigDecimal calculateTotalPrice() {
BigDecimal total = BigDecimal.ZERO;
for (OrderItem item : items) {
total = total.add(item.getPrice().multiply(new BigDecimal(item.getQuantity())));
}
return total;
}
// Getters and Setters
public String getOrderId() {
return orderId;
}
public Long getUserId() {
return userId;
}
public String getUserLevel() {
return userLevel;
}
public List<OrderItem> getItems() {
return items;
}
public Date getCreateTime() {
return createTime;
}
}
// 3. 订单项类
package com.ecommerce.promotion;
import java.math.BigDecimal;
public class OrderItem {
private String productId;
private String productName;
private BigDecimal price;
private int quantity;
private String category;
public OrderItem(String productId, String productName, BigDecimal price, int quantity, String category) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.quantity = quantity;
this.category = category;
}
// Getters
public String getProductId() {
return productId;
}
public String getProductName() {
return productName;
}
public BigDecimal getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public String getCategory() {
return category;
}
}
// 4. 具体策略类实现
// 4.1 直接折扣策略
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class PercentageDiscountStrategy implements PromotionStrategy {
private BigDecimal discountPercentage;
private String description;
/**
* @param discountPercentage 折扣百分比 (0.8 表示 8折)
*/
public PercentageDiscountStrategy(BigDecimal discountPercentage, String description) {
this.discountPercentage = discountPercentage;
this.description = description;
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
return originalPrice.multiply(discountPercentage).setScale(2, RoundingMode.HALF_UP);
}
@Override
public String getDescription() {
return description;
}
}
// 4.2 满减策略
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class AmountDiscountStrategy implements PromotionStrategy {
private BigDecimal threshold;
private BigDecimal discountAmount;
private String description;
/**
* @param threshold 满减阈值
* @param discountAmount 减免金额
*/
public AmountDiscountStrategy(BigDecimal threshold, BigDecimal discountAmount, String description) {
this.threshold = threshold;
this.discountAmount = discountAmount;
this.description = description;
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
if (originalPrice.compareTo(threshold) >= 0) {
return originalPrice.subtract(discountAmount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
}
return originalPrice;
}
@Override
public String getDescription() {
return description;
}
}
// 4.3 会员折扣策略
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
public class MemberDiscountStrategy implements PromotionStrategy {
private Map<String, BigDecimal> levelDiscountMap;
private String description;
public MemberDiscountStrategy(String description) {
this.levelDiscountMap = new HashMap<>();
this.description = description;
// 初始化各会员等级折扣
levelDiscountMap.put("REGULAR", new BigDecimal("1.0")); // 普通用户不打折
levelDiscountMap.put("SILVER", new BigDecimal("0.95")); // 银卡会员95折
levelDiscountMap.put("GOLD", new BigDecimal("0.9")); // 金卡会员9折
levelDiscountMap.put("DIAMOND", new BigDecimal("0.8")); // 钻石会员8折
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
String userLevel = order.getUserLevel();
BigDecimal discount = levelDiscountMap.getOrDefault(userLevel, new BigDecimal("1.0"));
return originalPrice.multiply(discount).setScale(2, RoundingMode.HALF_UP);
}
@Override
public String getDescription() {
return description;
}
}
// 4.4 限时折扣策略
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
public class TimeDiscountStrategy implements PromotionStrategy {
private Date startTime;
private Date endTime;
private BigDecimal discountPercentage;
private String description;
public TimeDiscountStrategy(Date startTime, Date endTime,
BigDecimal discountPercentage, String description) {
this.startTime = startTime;
this.endTime = endTime;
this.discountPercentage = discountPercentage;
this.description = description;
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
Date orderTime = order.getCreateTime();
if (orderTime.after(startTime) && orderTime.before(endTime)) {
return originalPrice.multiply(discountPercentage).setScale(2, RoundingMode.HALF_UP);
}
return originalPrice;
}
@Override
public String getDescription() {
return description;
}
}
// 4.5 第二件半价策略
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.OrderItem;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
public class SecondHalfPriceStrategy implements PromotionStrategy {
private String description;
public SecondHalfPriceStrategy(String description) {
this.description = description;
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
Map<String, Integer> productCountMap = new HashMap<>();
BigDecimal discount = BigDecimal.ZERO;
// 统计每种商品的数量
for (OrderItem item : order.getItems()) {
String productId = item.getProductId();
productCountMap.put(productId, productCountMap.getOrDefault(productId, 0) + item.getQuantity());
}
// 计算第二件半价的折扣金额
for (OrderItem item : order.getItems()) {
String productId = item.getProductId();
int count = productCountMap.get(productId);
if (count >= 2) {
// 计算可以享受半价的商品数量
int halfPriceCount = count / 2;
// 每件商品半价的折扣金额
BigDecimal itemDiscount = item.getPrice().divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
// 总折扣金额
discount = discount.add(itemDiscount.multiply(new BigDecimal(halfPriceCount)));
}
}
return originalPrice.subtract(discount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
}
@Override
public String getDescription() {
return description;
}
}
// 5. 复合策略 - 支持多个策略组合使用
package com.ecommerce.promotion.strategies;
import com.ecommerce.promotion.Order;
import com.ecommerce.promotion.PromotionStrategy;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class CompositePromotionStrategy implements PromotionStrategy {
private List<PromotionStrategy> strategies;
private String description;
public CompositePromotionStrategy(String description) {
this.strategies = new ArrayList<>();
this.description = description;
}
public void addStrategy(PromotionStrategy strategy) {
strategies.add(strategy);
}
@Override
public BigDecimal calculatePrice(BigDecimal originalPrice, Order order) {
BigDecimal finalPrice = originalPrice;
for (PromotionStrategy strategy : strategies) {
finalPrice = strategy.calculatePrice(finalPrice, order);
}
return finalPrice;
}
@Override
public String getDescription() {
StringBuilder sb = new StringBuilder(description + ": ");
for (int i = 0; i < strategies.size(); i++) {
sb.append(strategies.get(i).getDescription());
if (i < strategies.size() - 1) {
sb.append(" + ");
}
}
return sb.toString();
}
}
// 6. 促销策略工厂
package com.ecommerce.promotion;
import com.ecommerce.promotion.strategies.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> strategies = new HashMap<>();
static {
// 初始化预定义的促销策略
strategies.put("NORMAL", new PercentageDiscountStrategy(new BigDecimal("1.0"), "正常价格"));
strategies.put("DISCOUNT_80", new PercentageDiscountStrategy(new BigDecimal("0.8"), "8折优惠"));
strategies.put("FULL_300_MINUS_50", new AmountDiscountStrategy(new BigDecimal("300"), new BigDecimal("50"), "满300减50"));
strategies.put("MEMBER_DISCOUNT", new MemberDiscountStrategy("会员专属折扣"));
strategies.put("SECOND_HALF", new SecondHalfPriceStrategy("第二件半价"));
// 复合策略示例:会员额外8折 + 满300减50
CompositePromotionStrategy composite = new CompositePromotionStrategy("会员专享活动");
composite.addStrategy(new MemberDiscountStrategy("会员折扣"));
composite.addStrategy(new AmountDiscountStrategy(new BigDecimal("300"), new BigDecimal("50"), "满减"));
strategies.put("MEMBER_PLUS", composite);
}
public static PromotionStrategy getPromotionStrategy(String promotionKey) {
return strategies.getOrDefault(promotionKey, strategies.get("NORMAL"));
}
// 注册新策略
public static void registerStrategy(String promotionKey, PromotionStrategy strategy) {
strategies.put(promotionKey, strategy);
}
// 创建限时折扣策略
public static PromotionStrategy createTimeDiscountStrategy(Date startTime, Date endTime,
BigDecimal discount, String description) {
return new TimeDiscountStrategy(startTime, endTime, discount, description);
}
// 创建自定义折扣策略
public static PromotionStrategy createPercentageDiscountStrategy(BigDecimal discount, String description) {
return new PercentageDiscountStrategy(discount, description);
}
// 创建自定义满减策略
public static PromotionStrategy createAmountDiscountStrategy(BigDecimal threshold,
BigDecimal discountAmount, String description) {
return new AmountDiscountStrategy(threshold, discountAmount, description);
}
}
// 7. 促销上下文 - 负责应用策略
package com.ecommerce.promotion;
import java.math.BigDecimal;
public class PromotionContext {
private PromotionStrategy strategy;
public PromotionContext(PromotionStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(PromotionStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal executeStrategy(Order order) {
BigDecimal originalPrice = order.calculateTotalPrice();
return strategy.calculatePrice(originalPrice, order);
}
public String getPromotionDescription() {
return strategy.getDescription();
}
}
// 8. 客户端使用示例
package com.ecommerce.promotion;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PromotionDemo {
public static void main(String[] args) throws ParseException {
// 创建订单
Order order = new Order("ORD12345", 10001L, "GOLD");
order.addItem(new OrderItem("P001", "笔记本电脑", new BigDecimal("5999"), 1, "电子产品"));
order.addItem(new OrderItem("P002", "无线鼠标", new BigDecimal("99"), 2, "电子配件"));
System.out.println("订单原价: " + order.calculateTotalPrice());
// 使用8折促销策略
PromotionContext context = new PromotionContext(
PromotionStrategyFactory.getPromotionStrategy("DISCOUNT_80")
);
BigDecimal discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
// 切换到满减策略
context.setStrategy(PromotionStrategyFactory.getPromotionStrategy("FULL_300_MINUS_50"));
discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
// 切换到会员折扣策略
context.setStrategy(PromotionStrategyFactory.getPromotionStrategy("MEMBER_DISCOUNT"));
discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
// 使用第二件半价策略
context.setStrategy(PromotionStrategyFactory.getPromotionStrategy("SECOND_HALF"));
discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
// 使用复合促销策略
context.setStrategy(PromotionStrategyFactory.getPromotionStrategy("MEMBER_PLUS"));
discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
// 创建并使用限时折扣策略
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startTime = sdf.parse("2024-03-20 00:00:00");
Date endTime = sdf.parse("2024-03-25 23:59:59");
PromotionStrategy timeStrategy = PromotionStrategyFactory.createTimeDiscountStrategy(
startTime, endTime, new BigDecimal("0.5"), "限时五折"
);
context.setStrategy(timeStrategy);
discountedPrice = context.executeStrategy(order);
System.out.println(context.getPromotionDescription() + " 折后价: " + discountedPrice);
}
}
这个策略模式案例可以应用于:
通过策略模式,我们实现了一个灵活、可扩展的电商促销系统,能够满足多变的业务需求。这种设计避免了使用大量的条件判断语句,提高了代码的可维护性和可测试性,同时也为未来的功能扩展留出了空间。
如果您有任何问题或需要进一步的说明,请随时告诉我。