一、为什么讲策略模式 策略模式,应该是工作中比较常用的设计模式,调用方自己选择用哪一种策略完成对数据的操作,也就是一个类的行为或其算法可以在运行时更改 我个人的理解是将一些除了过程不同其他都一样的函数封装成策略,然后调用方自己去选择想让数据执行什么过程策略。常见的例子为根据用户分类推荐不同的排行榜(用户关注点不一样,推荐榜单就不一样) 和单例模式一样,随着时间发展,我不再推荐经典策略模式,更推荐简单策略用枚举策略模式,复杂地用工厂策略模式。下面引入一个例子,我们的需求是:对一份股票数据列表,给出低价榜、高价榜、涨幅榜。这其中只有排序条件的区别,比较适合作为策略模式的例子二、经典策略模式 数据DTODatapublicclassStock{股票交易代码privateStringcode;现价privateDoubleprice;涨幅privateDoublerise;} 抽象得到的策略接口publicinterfaceStrategy{将股票列表排序paramsource源数据return排序后的榜单ListStocksort(ListStocksource);} 实现我们的策略类高价榜publicclassHighPriceRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice)。reversed())。collect(Collectors。toList());}}低价榜publicclassLowPriceRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice))。collect(Collectors。toList());}}高涨幅榜publicclassHighRiseRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getRise)。reversed())。collect(Collectors。toList());}} 经典的Context类,publicclassContext{privateStrategystrategy;publicvoidsetStrategy(Strategystrategy){this。strategystrategy;}publicListStockgetRank(ListStocksource){returnstrategy。sort(source);}} 于是我们顺礼成章地得到调用类榜单实例RankServiceImplServicepublicclassRankServiceImpl{dataService。getSource()提供原始的股票数据ResourceprivateDataServicedataService;前端传入榜单类型,返回排序完的榜单paramrankType榜单类型return榜单数据publicListStockgetRank(StringrankType){创建上下文ContextcontextnewContext();这里选择策略switch(rankType){caseHighPrice:context。setStrategy(newHighPriceRank());break;caseLowPrice:context。setStrategy(newLowPriceRank());break;caseHighRise:context。setStrategy(newHighRiseRank());break;default:thrownewIllegalArgumentException(rankTypenotfound);}然后执行策略returncontext。getRank(dataService。getSource());}} 我们可以看到经典方法,创建了一个接口、三个策略类,还是比较啰嗦的。调用类的实现也待商榷,新增一个策略类还要修改榜单实例(可以用抽象工厂解决,但是复杂度又上升了)。加之我们有更好的选择,所以此处不再推荐经典策略模式三、基于枚举的策略模式 这里对这种简单的策略,推荐用枚举进行优化。枚举的本质是创建了一些静态类的集合。 我下面直接给出例子,大家可以直观感受一下 枚举策略类publicenumRankEnum{以下三个为策略实例HighPrice{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice)。reversed())。collect(Collectors。toList());}},LowPrice{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice))。collect(Collectors。toList());}},HighRise{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getRise)。reversed())。collect(Collectors。toList());}};这里定义了策略接口publicabstractListStocksort(ListStocksource);} 对应的调用类也得以优化,榜单实例RankServiceImplServicepublicclassRankServiceImpl{dataService。getSource()提供原始的股票数据ResourceprivateDataServicedataService;前端传入榜单类型,返回排序完的榜单paramrankType榜单类型形似RankEnum。HighPrice。name()return榜单数据publicListStockgetRank(StringrankType){获取策略,这里如果未匹配会抛IllegalArgumentException异常RankEnumrankRankEnum。valueOf(rankType);然后执行策略returnrank。sort(dataService。getSource());}} 可以看到,如果策略简单的话,基于枚举的策略模式优雅许多,调用方也做到了0修改,但正确地使用枚举策略模式需要额外考虑以下几点。枚举的策略类是公用且静态,这意味着这个策略过程不能引入非静态的部分,扩展性受限策略模式的目标之一,是优秀的扩展性和可维护性,最好能新增或修改某一策略类时,对其他类是无改动的。而枚举策略如果过多或者过程复杂,维护是比较困难的,可维护性受限四、基于工厂的策略模式 为了解决良好的扩展性和可维护性,我更推荐以下利用spring自带beanFactory的优势,实现一个基于工厂的策略模式。 策略类改动只是添加了Service注解,并指定了Service的value属性高价榜注意申明Service。valueHighPrice,他是我们的key,下同Service(HighPrice)publicclassHighPriceRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice)。reversed())。collect(Collectors。toList());}}低价榜Service(LowPrice)publicclassLowPriceRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getPrice))。collect(Collectors。toList());}}高涨幅榜Service(HighRise)publicclassHighRiseRankimplementsStrategy{OverridepublicListStocksort(ListStocksource){returnsource。stream()。sorted(Comparator。comparing(Stock::getRise)。reversed())。collect(Collectors。toList());}} 调用类修改较大,接入借助spring工厂特性,完成策略类ServicepublicclassRankServiceImpl{dataService。getSource()提供原始的股票数据ResourceprivateDataServicedataService;利用注解Resource和Autowired特性,直接获取所有策略类keyService的valueResourceprivateMapString,StrategyrankMap;前端传入榜单类型,返回排序完的榜单paramrankType榜单类型和Service注解的value属性一致return榜单数据publicListStockgetRank(StringrankType){判断策略是否存在if(!rankMap。containsKey(rankType)){thrownewIllegalArgumentException(rankTypenotfound);}获得策略实例StrategyrankrankMap。get(rankType);执行策略returnrank。sort(dataService。getSource());}} 若读者使用的不是Spring,也可以找找对应框架的工厂模式实现,或者自己实现一个抽象工厂 工厂策略模式会比枚举策略模式啰嗦,但也更加灵活、易扩展性和易维护。故简单策略推荐枚举策略模式,复杂策略才推荐工厂策略模式 本文来自https:www。cnblogs。comdechinphypdictclass。html