策略模式简介
策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
uml示例图如下:
简单策略实现
数据DTO
@Data
public class Stock {
// 股票交易代码
private String code;
// 现价
private Double price;
// 涨幅
private Double rise;
}
定义策略接口
public interface Strategy {
/**
* 将股票列表排序
*
* @param source 源数据
* @return 排序后的榜单
*/
List<Stock> sort(List<Stock> source);
}
策略实现类
/**
* 高价榜
*/
public class HighPriceRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
}
/**
* 低价榜
*/
public class LowPriceRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
}
/**
* 高涨幅榜
*/
public class HighRiseRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
}
环境角色
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public List<Stock> getRank(List<Stock> source) {
return strategy.sort(source);
}
}
调用类–榜单实例RankServiceImpl
@Service
public class RankServiceImpl {
/**
* dataService.getSource() 提供原始的股票数据
*/
@Resource
private DataService dataService;
/**
* 前端传入榜单类型, 返回排序完的榜单
*
* @param rankType 榜单类型
* @return 榜单数据
*/
public List<Stock> getRank(String rankType) {
// 创建上下文
Context context = new Context();
// 这里选择策略
switch (rankType) {
case "HighPrice":
context.setStrategy(new HighPriceRank());
break;
case "LowPrice":
context.setStrategy(new LowPriceRank());
break;
case "HighRise":
context.setStrategy(new HighRiseRank());
break;
default:
throw new IllegalArgumentException("rankType not found");
}
// 然后执行策略
return context.getRank(dataService.getSource());
}
}
策略枚举
枚举策略类
public enum RankEnum {
// 以下三个为策略实例
HighPrice {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
},
LowPrice {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
},
HighRise {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
};
// 定义策略接口
public abstract List<Stock> sort(List<Stock> source);
}
榜单实例RankServiceImpl
@Service
public class RankServiceImpl {
/**
* dataService.getSource() 提供原始的股票数据
*/
@Resource
private DataService dataService;
/**
* 前端传入榜单类型, 返回排序完的榜单
*
* @param rankType 榜单类型 形似 RankEnum.HighPrice.name()
* @return 榜单数据
*/
public List<Stock> getRank(String rankType) {
// 获取策略,这里如果未匹配会抛 IllegalArgumentException异常
RankEnum rank = RankEnum.valueOf(rankType);
// 然后执行策略
return rank.sort(dataService.getSource());
}
}
在大批量 if-else的情况下,可使用策略枚举来消除替换。
总而言之,使用策略枚举可以很灵活处理各种复杂判断,且可读性与扩展性都比较好,它更像是函数式编程,即传进一个参数,就可以得到对应模式下返回的数值。
若 Java 里业务逻辑中大批量使用 if-else,则是面向过程了,因为业务逻辑里的 if-else 是从上往下一个 if 接一个 if 判断下去的,在各个 if 上打个断点,debug 下去,就明白它其实是面向过程的。
策略工厂
策略类添加了@Service注解,并指定了Service的value属性
/**
* 高价榜
* 注意申明 Service.value = HighPrice,他是我们的key,下同
*/
@Service("HighPrice")
public class HighPriceRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
}
/**
* 低价榜
*/
@Service("LowPrice")
public class LowPriceRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
}
/**
* 高涨幅榜
*/
@Service("HighRise")
public class HighRiseRank implements Strategy {
@Override
public List<Stock> sort(List<Stock> source) {
return source.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
}
利用spring自带beanFactory的优势,实现策略工厂
@Service
public class RankServiceImpl {
/**
* dataService.getSource() 提供原始的股票数据
*/
@Resource
private DataService dataService;
/**
* 利用注解@Resource和@Autowired特性,直接获取所有策略类
* key = @Service的value
*/
@Resource
private Map<String, Strategy> rankMap;
/**
* 前端传入榜单类型, 返回排序完的榜单
*
* @param rankType 榜单类型 和Service注解的value属性一致
* @return 榜单数据
*/
public List<Stock> getRank(String rankType) {
// 判断策略是否存在
if (!rankMap.containsKey(rankType)) {
throw new IllegalArgumentException("rankType not found");
}
// 获得策略实例
Strategy rank = rankMap.get(rankType);
// 执行策略
return rank.sort(dataService.getSource());
}
}
参考
https://cloud.tencent.com/developer/article/1969378
https://xie.infoq.cn/article/9369635b15f3f79981042d218