设计模式:策略模式详解与实战
详细讲解了设计模式中的策略模式。首先介绍了策略模式的定义、UML 类图及三个核心角色(抽象策略、具体策略、环境)。接着通过电商会员折扣的业务场景,展示了从基础实现到简单工厂再到注解反射优化的完整演进过程,有效解决了复杂的条件判断问题。此外,文章还分析了 Android 源码中属性动画插值器和估值器的策略模式应用,对比了策略模式与状态模式的区别,并总结了该模式的优缺点。内容涵盖 Java 代码实战,旨在帮助开发者理解如何运用策略模式提升代码的可维护性和扩展性。

详细讲解了设计模式中的策略模式。首先介绍了策略模式的定义、UML 类图及三个核心角色(抽象策略、具体策略、环境)。接着通过电商会员折扣的业务场景,展示了从基础实现到简单工厂再到注解反射优化的完整演进过程,有效解决了复杂的条件判断问题。此外,文章还分析了 Android 源码中属性动画插值器和估值器的策略模式应用,对比了策略模式与状态模式的区别,并总结了该模式的优缺点。内容涵盖 Java 代码实战,旨在帮助开发者理解如何运用策略模式提升代码的可维护性和扩展性。

在软件开发过程中,我们经常会遇到大量的 if-else 或者 switch-case 语句。当条件嵌套过多时,代码会变得臃肿不堪,维护成本显著增加。策略模式(Strategy Pattern)正是为了解决这一问题而诞生的行为型设计模式。本文将深入探讨策略模式的定义、实现方式、优化方案以及在 Android 源码中的实际应用。
策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。这使得算法独立于使用它的客户而独立变化。
public interface Strategy {
/**
* 策略方法
*/
void strategyInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
// 相关的业务逻辑 A
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void strategyInterface() {
// 相关的业务逻辑 B
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void contextInterface() {
strategy.strategyInterface();
}
}
假设某电商平台推出了多种会员等级,分别为普通用户、VIP、超级 VIP 以及金牌会员。针对不同类别的用户,购买虚拟商品有不同的打折方式。此外,用户每消费一定金额就会增加一个级别。我们可以使用策略模式来处理这种动态变化的计费逻辑。
public interface CalPrice {
Double calPrice(Double originalPrice);
}
// 普通用户:原价
public class OriginalPrice implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice;
}
}
// VIP:九折
public class Vip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.9;
}
}
// 超级 VIP:八折
public class SuperVip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.8;
}
}
// 金牌会员:七折
public class GoldVip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.7;
}
}
public class Player {
private double totalAmount = 0; // 消费总额
private double amount = 0; // 单次消费金额
private CalPrice calPrice = new OriginalPrice(); // 初始策略
// 购买皮肤,更新总额并可能升级
public void buy(double amount) {
this.amount = amount;
totalAmount += amount;
// 根据总额动态调整策略
if (totalAmount > 30000) {
calPrice = new GoldVip();
} else if (totalAmount > 20000) {
calPrice = new SuperVip();
} else if (totalAmount > 10000) {
calPrice = new Vip();
}
}
// 计算最终支付金额
public Double calLastAmount() {
return calPrice.calPrice(amount);
}
public double getTotalAmount {
totalAmount;
}
}
虽然上述代码解决了策略切换问题,但 Player 类中包含了大量判断逻辑。我们可以引入简单工厂模式将策略创建逻辑分离出来。
public class CalPriceFactory {
private CalPriceFactory() {}
public static CalPrice createCalPrice(Player player) {
double total = player.getTotalAmount();
if (total > 30000) {
return new GoldVip();
} else if (total > 20000) {
return new SuperVip();
} else if (total > 10000) {
return new Vip();
} else {
return new OriginalPrice();
}
}
}
缺点:如果增加新的会员等级,工厂类需要修改代码,违反了开闭原则。
为了解决工厂类频繁修改的问题,可以使用注解配合反射来自动注册和匹配策略。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PriceRegion {
int max() default Integer.MAX_VALUE;
int min() default Integer.MIN_VALUE;
}
@PriceRegion(max = 10000)
public class OriginalPrice implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice;
}
}
@PriceRegion(min = 10000, max = 20000)
public class Vip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.9;
}
}
@PriceRegion(min = 20000, max = 30000)
public class SuperVip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.8;
}
}
@PriceRegion(min = 30000)
public class GoldVip implements CalPrice {
@Override
public Double calPrice(Double originalPrice) {
return originalPrice * 0.7;
}
}
public class SmartCalPriceFactory {
private List<Class<? extends CalPrice>> calPriceList;
public SmartCalPriceFactory() {
init();
}
private void init() {
calPriceList = new ArrayList<>();
// 实际项目中应扫描指定包路径下的类
// 此处简化演示,手动添加或模拟扫描
calPriceList.add(OriginalPrice.class);
calPriceList.add(Vip.class);
calPriceList.add(SuperVip.class);
calPriceList.add(GoldVip.class);
}
public CalPrice createCalPrice(Player player) {
for (Class<? extends CalPrice> clazz : calPriceList) {
PriceRegion region = clazz.getAnnotation(PriceRegion.class);
if (region != null) {
double total = player.getTotalAmount();
if (total >= region.min() && total <= region.max()) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("策略实例化失败", e);
}
}
}
}
throw new RuntimeException("未找到匹配的策略");
}
}
Android 框架中广泛使用了策略模式,最典型的例子是属性动画中的插值器。
TimeInterpolator 接口定义了插值算法,不同的实现类决定了动画的速度变化效果。
public interface TimeInterpolator {
float getInterpolation(float input);
}
常见的实现包括 AccelerateDecelerateInterpolator(先加速后减速)、LinearInterpolator(线性)等。
TypeEvaluator 接口用于计算属性值的变化。
public interface TypeEvaluator<T> {
T evaluate(float fraction, T startValue, T endValue);
}
例如 ArgbEvaluator 用于颜色值的插值计算,它分别对 Alpha、Red、Green、Blue 四个通道进行线性插值。
两者结构相似,但意图不同:
策略模式是一种强大的设计模式,能够有效降低代码耦合度,提高系统的可扩展性。通过本文的学习,我们掌握了策略模式的基本结构、优化技巧以及在 Android 开发中的实际应用。在实际开发中,建议结合工厂模式和反射机制,构建更加灵活和健壮的架构。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online