什么是策略模式
顾名思义,针对不同的情况而提供的不同解决方案的集合,即为策略。那么此模式则是为了针对不同情况提供不同的算法,根据情况使用不同的算法进行计算。
生活中的策略模式
-
诸葛亮的锦囊妙计
-
旅行前选择出行方式(汽车,飞机,货车)
-
购买电脑时商家提供的不同的配置方案
举个栗子
项目一期
公司开发了一个商城APP,由于考虑项目初期使用人员不多,
项目前期只提供三个级别会员,分别是:
- 普通会员,
- 金牌会员,
- 钻石会员
不同级别的会员在购买商品后可以提供不同的打折方案,方案如下:
- 普通会员:原价的9.9折
- 金牌会员:原价的9.5折
- 钻石会员:原价的9折
我们的开发人员小王在拿到需求后直接上手就干,并且很快就开发完此功能,代码如下:
会员对象
@Getter
@Setter
public class Member {
private MemberLevelEnum level;
private String name;
}
会员等级枚举
public enum MemberLevelEnum {
//普通会员
GENERAL,
//金牌会员
GOLD,
//钻石会员
DIAMONDS
}
价格打折方案
public class PriceCalculation {
public static Double calculation(Double price, Member member) throws Exception {
if(MemberLevelEnum.GENERAL.equals(member.getLevel()) ){
price *= 0.95;
}else if(MemberLevelEnum.GOLD.equals(member.getLevel())){
price *= 0.9;
}else if(MemberLevelEnum.DIAMONDS.equals(member.getLevel())){
price *= 0.8;
}else{
throw new Exception("无此会员级别");
}
return price;
}
}
}
项目二期
随着一期产品的成功,会员数量大幅上升,为了更加细化用户级别,提供更好的服务,获取更加优质的顾客,决定增加更高的用户级别,提供更高的折扣方案,方案如下
增加用户级别:超级会员
用户级别折扣:钻石会员的基础上再打9折
开发人员小王在拿到方案后,觉得很轻松,只要为用户等级枚举增加一个等级, 并且在打折方案中增加一个判断即可,说干就干,开发快被完成,代码如下:
会员级别
public enum MemberLevelEnum {
GENERAL,
GOLD,
DIAMONDS,
SUPER
}
打折方案
public class PriceCalculation {
public static Double calculation(Double price, Member member) throws Exception {
if(MemberLevelEnum.GENERAL.equals(member.getLevel()) ){
price *= 0.95;
}else if(MemberLevelEnum.GOLD.equals(member.getLevel())){
price *= 0.9;
}else if(MemberLevelEnum.DIAMONDS.equals(member.getLevel())){
price *= 0.8;
}else if(MemberLevelEnum.SUPER.equals(member.getLevel())){
price = price*0.8*0.9;
}else{
throw new Exception("无此会员级别");
}
return price;
}
}
打折方案升级版
可是在此时小王突然想到,如果钻石会员的折扣变了,那岂不是需要改两个地方的算法?如果是一个不知道业务的新人修改那岂不是要出大事,于是小王将折扣算法提取成方法,开发出了打折方案的升级版,代码如下
/**
* 打折
* [@param](https://my.oschina.net/u/2303379) price
* 商品合计价格
* [@param](https://my.oschina.net/u/2303379) member
* 会员实例
* [@return](https://my.oschina.net/u/556800) 打折后价格
* [@throws](https://my.oschina.net/throws) Exception
*/
public static Double calculation(Double price, Member member) throws Exception {
if(MemberLevelEnum.GENERAL.equals(member.getLevel()) ){
return calculateGeneral(price);
}else if(MemberLevelEnum.GOLD.equals(member.getLevel())){
return calculateGold(price);
}else if(MemberLevelEnum.DIAMONDS.equals(member.getLevel())){
return calculateDiamonds(price);
}else if(MemberLevelEnum.SUPER.equals(member.getLevel())){
return calculateSuper(price);
}else{
throw new Exception("无此会员级别");
}
}
/**
* 普通会员打折方案
* [@param](https://my.oschina.net/u/2303379) price
* 价格
* @return 折后价格
*/
private static Double calculateGeneral(Double price){
return price*0.99;
}
/**
* 金牌会员打折方案
* @param price
* 价格
* @return 折后价格
*/
private static Double calculateGold(Double price){
return price*0.95;
}
/**
* 钻石会员打折方案
* @param price
* 价格
* @return 折后价格
*/
private static Double calculateDiamonds(Double price){
return price*0.9;
}
/**
* 超级会员打折方案
* @param price
* 价格
* @return 折后价格
*/
private static Double calculateSuper(Double price){
return calculateDiamonds(price)*0.9;
}
}
最终项目优化
项目的老司机小李在做代码的审核时,发现此处代码存在问题,如果某个方法出错,则会导致整个会员打折功能无法使用,从而造成项目的事故。所以老司机为此做出了代码的优化方案,方案如下:
- 将项目中的会员打折方案提取,并抽象。
- 不同的会员级别分别实现此抽象类,使用对应会员级别的算法,来实现同一个折算方案
具体代码如下:
抽象策略
public interface PriceCalculationStrategy {
Double calculate(Double price);
}
各具体实现策略
普通会员打折实现类
/**
* 普通会员打折实现类
* Created by asus on 2018/4/12.
*/
public class GeneralPriceCalculationStrategy implements PriceCalculationStrategy{
@Override
public Double calculate(Double price){
return price*0.95;
}
}
金牌会员打折实现类
/**
* 金牌会员打折实现类
* Created by asus on 2018/4/12.
*/
public class GoldPriceCalculationStrategy implements PriceCalculationStrategy{
@Override
public Double calculate(Double price){
return price*0.9;
}
}
钻石会员打折实现类
/**
* 钻石会员打折实现类
* Created by asus on 2018/4/12.
*/
public class DiamodsPriceCalculationStrategy implements PriceCalculationStrategy{
@Override
public Double calculate(Double price){
return price*0.8;
}
}
超级会员打折实现类
/**
* 超级会员打折实现类
* Created by asus on 2018/4/12.
*/
public class SuperPriceCalculationStrategy implements PriceCalculationStrategy{
@Override
public Double calculate(Double price){
return new PriceCalculationDiamods().calculate(price)*0.9;
}
}
环境(context)
public class MemberPay {
private PriceCalculationStrategy priceCalculation = null;
/**
* 实例化 传入具体会员折算策略
* @param priceCalculation
*/
public MemberPay(PriceCalculationStrategy priceCalculation) {
this.priceCalculation = priceCalculation;
}
public Double calculate(Double price) {
return priceCalculation.calculate(price);
}
}
优点
- 面向接口编程扩展性好
- 多个算法中间可以自由切换
- 避免使用多重判断
缺点
策略类会增加很多
元素
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。