设计模式
- 策略模式:DiscountStrategy接口以及其多个具体实现类构成了策略模式的典型实现。策略模式定义了一系列算法,将每个算法封装起来,并使它们可以互相替换,让算法的变化独立于使用算法的客户端。
- 依赖注入模式:通过Spring的@Autowired注解,实现了依赖注入,即将实现了DiscountStrategy接口的具体策略类注入到DiscountContext中,降低了组件之间的耦合度。
- 工厂模式:虽然没有显式的工厂类,但DiscountContext类中动态管理策略类对象实例的特性可以看作是一种简单的工厂模式,根据具体条件生成对应的对象。
- 观察者模式:Spring的组件扫描机制类似于观察者模式,Spring在启动的时候会扫描指定的包路径,当发现标记为@Component的类时,会将其注册为Spring的Bean并进行管理。
示例
场景示例:订单折扣应用
开发一个订单系统,不同类型的客户可以享受不同程度的折扣。在传统的if-else设计中:
if (customerType == CustomerType.NEW) {
// 新客户折扣计算逻辑
double discount = calculateNewCustomerDiscount(orderTotal);
// 其他业务逻辑...
} else if (customerType == CustomerType.RETURNING) {
// 老客户折扣计算逻辑
double discount = calculateReturningCustomerDiscount(orderTotal);
// 其他业务逻辑...
} else if (customerType == CustomerType.VIP) {
// VIP客户折扣计算逻辑
double discount = calculateVIPCustomerDiscount(orderTotal);
// 其他业务逻辑...
}
存在问题
-
如果需要新增一种客户类型,就需要修改主逻辑中的if-else语句,会导致代码膨胀和可维护性下降。
-
折扣计算逻辑与订单处理逻辑混在一起,使代码难以理解和维护。
优化示例:
- 定义DiscountStrategy接口:首先定义一个DiscountStrategy接口,所有策略对象需要实现这个接口。
public interface DiscountStrategy {
double applyDiscount(double amount);
}
- 实现具体的DiscountStrategy策略类:编写多个实现了DiscountStrategy接口的具体策略类。
@Component
public class StrategyA implements DiscountStrategy {
@Override
public double applyDiscount(double amount) {
// 实现具体的折扣策略
}
}
@Component
public class StrategyB implements DiscountStrategy {
@Override
public double applyDiscount(double amount) {
// 实现不同的折扣策略
}
}
- DiscountContext类:在DiscountContext类中使用@Autowired注解注入所有实现了DiscountStrategy接口的类。
@Component
@Scope("prototype")
public class DiscountContext {
private Map<String, DiscountStrategy> strategies = new HashMap<>();
@Autowired
public void setStrategies(List<DiscountStrategy > strategyList) {
for (DiscountStrategy strategy : strategyList) {
strategies.put(strategy.getClass().getSimpleName(), strategy);
}
}
public double applyDiscount(String type, double amount) {
DiscountStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("Invalid discount type");
}
return strategy.applyDiscount(amount);
}
// 其他DiscountContext的方法
}
- 通过将DiscountContext的作用域设置为原型模式(@Scope(“prototype”)),Spring 将为每个注入它的bean创建一个新的实例,避免了不同线程共享同一个实例的问题。
反射(已废弃)
使用反射的情况下,让context可以根据type来生成对应的策略对象,并且返回一个仅仅包含当前策略对象的context,使用context来进行操作,也就是map中不存储策略对象,而是存储策略对象的.class,然后用反射生成对应的对象。(没有使用自动注入还需要手动注入。如果想要自动注入改一下即可)
import java.util.HashMap;
import java.util.Map;
public class DiscountContext {
private Map<String, Class<? extends DiscountStrategy>> strategyClasses = new HashMap<>();
public DiscountContext createContextWithStrategy(String type) throws InstantiationException, IllegalAccessException {
DiscountContext newContext = new DiscountContext();
Class<? extends DiscountStrategy> strategyClass = getStrategyClass(type);
if (strategyClass == null) {
throw new IllegalArgumentException("Invalid discount type");
}
DiscountStrategy strategyInstance = strategyClass.newInstance();
newContext.addStrategyClass(type, strategyClass);
return newContext;
}
public void addStrategyClass(String type, Class<? extends DiscountStrategy> strategyClass) {
strategyClasses.put(type, strategyClass);
}
public Class<? extends DiscountStrategy> getStrategyClass(String type) {
return strategyClasses.get(type);
}
}
反射方案优劣:
- 灵活性:
- 优势: 使用反射可以根据类名动态创建对象,无需提前实例化对象,使得系统更加灵活,并能够适应未来的扩展和变化。
- 劣势: 反射运行时开销较大,性能相对较低。
- 解耦性
- 优势:反射方案允许每个线程生成自己的对象实例,避免了共享对象带来的线程安全问题和并发性问题。
- ** 劣势**:反射抹平了静态类型检查,代码可读性和可维护性可能会降低。
- 并发优势
- 优势:反射方案允许每个线程生成自己的对象实例,避免了共享对象带来的线程安全问题和并发性问题。
- 劣势:反射可能会引入新的并发风险,如类加载、实例化等操作可能存在竞争条件。
思考补充
我理解这策略模式应该使用的场景是 当前这个属性的增加会新增一套对应的业务逻辑,开发需要取编写这样一套业务逻辑(也就是新的策略)而像那种标志性质的字段,本身增加并不会影响现有的业务逻辑,则不需要使用策略模式。(记录想法用的,想的不对,欢迎指导!)