策略模式的使用场景:
- 针对同一问题的多种处理方式,仅仅是具体行为有差别时;
- 需要安全地封装多种同一类型的操作时;
- 同一抽象类有多个子类,而客户端需要使用 if-else 或者 switch-case 来选择具体子类时。
我们假设电商场景,购物时根据会员等级不同付款时优惠的价格也不同
最早我们没有使用任何策略模式,最原始的代码如下:
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
/**
* 根据会员等级计算费用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("费用低于1000,不参与会员折扣");
return money;
}
if (type == 1) {
// 白银会员优惠50
return money - 50;
} else if (type == 2) {
// 黄金会员8折
return money * 0.8;
} else if (type == 3) {
// 白金会员8折后再优惠20
return (money * 0.8) - 20;
} else if (type == 4) {
// 钻石会员5折
return money * 0.5;
} else {
return money;
}
}
}
这种写法直观,清晰随便一个人都能看懂,但是随着业务原来越多需求越来越复杂,可能在各个会员等级里面还有其它操作,这个时候就会发现代码特别的冗长后期维护起来特别痛苦要不停的往下翻找到对应的位置,显然这不适合后期维护。这个时候策略模式就出来了,把每个会员等级单独抽出来。
首先新建一个计算费用的接口类
public interface MemberStrategy {
/**
* 计算费用
* @param money
* @return
*/
double calMoney(long money);
}
然后分别新建对应的会员等级接口实现类
@Service
public class OrdinaryMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(OrdinaryMemberStrategy.class);
/**
* 普通会员不打折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("普通会员不打折");
return money;
}
}
@Service
public class SilverMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(SilverMemberStrategy.class);
/**
* 白银会员优惠50元
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白银会员优惠50");
return money - 50;
}
}
@Service
public class GoldMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(GoldMemberStrategy.class);
/**
* 黄金会员8折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("黄金会员8折");
return money * 0.8;
}
}
@Service
public class PlatinumMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(PlatinumMemberStrategy.class);
/**
* 白金会员打8折,并且优惠20
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白金会员打8折,并且优惠20");
return (money - 20) * 0.8;
}
}
@Service
public class DiamondMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(DiamondMemberStrategy.class);
/**
* 白钻石会员打5折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("钻石会员打5折");
return money * 0.5;
}
}
然后计算费用接口修改成如下
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
@Autowired
OrdinaryMemberStrategy ordinaryStrategyService;
@Autowired
SilverMemberStrategy silverStrategyService;
@Autowired
GoldMemberStrategy goldStrategyService;
@Autowired
PlatinumMemberStrategy platinumStrategyService;
@Autowired
DiamondMemberStrategy diamondMemberStrategy;
/**
* 根据会员等级计算费用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("费用低于1000,不参与会员折扣");
return money;
}
MemberStrategy memberStrategy;
if (type == 1) {
memberStrategy = silverStrategyService;
} else if (type == 2) {
memberStrategy = goldStrategyService;
} else if (type == 3) {
memberStrategy = platinumStrategyService;
} else if (type == 4) {
memberStrategy = diamondMemberStrategy;
} else {
memberStrategy = ordinaryStrategyService;
}
return memberStrategy.calMoney(money);
}
}
这种方式就化解了之前代码全写在if else逻辑判断里面了,改为对应的会员等级逻辑处理各自有自己的类,后期要修改那个会员等级的逻辑直接去对应的类方法里面修改就好了。
但是这样还是有if else 判断并没有达到完美的要求,这个时候就需要引进工厂模式了。
修改会员策略接口增加一个获取会员等级的接口
public interface MemberStrategy {
/**
* 计算费用
* @param money
* @return
*/
double calMoney(long money);
/**
* 获取会员等级
* @return
*/
int getType();
}
对应的各个会员等级接口实现类也修改下,把会员等级的type写进getType方法里面
@Service
public class OrdinaryMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(OrdinaryMemberStrategy.class);
/**
* 普通会员不打折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("普通会员不打折");
return money;
}
@Override
public int getType() {
return 0;
}
}
@Service
public class SilverMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(SilverMemberStrategy.class);
/**
* 白银会员优惠50元
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白银会员优惠50");
return money - 50;
}
@Override
public int getType() {
return 1;
}
}
@Service
public class GoldMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(GoldMemberStrategy.class);
/**
* 黄金会员8折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("黄金会员8折");
return money * 0.8;
}
@Override
public int getType() {
return 2;
}
}
@Service
public class PlatinumMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(PlatinumMemberStrategy.class);
/**
* 白金会员打8折,并且优惠20
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白金会员打8折,并且优惠20");
return (money - 20) * 0.8;
}
@Override
public int getType() {
return 3;
}
}
@Service
public class DiamondMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(DiamondMemberStrategy.class);
/**
* 白钻石会员打5折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("钻石会员打5折");
return money * 0.5;
}
@Override
public int getType() {
return 4;
}
}
我们在每个会员等级接口实现类里面设置了等级对应的type,接下来我们新增一个会员等级工厂类
public class MemberStrategyFactory {
private Map<Integer, MemberStrategy> map;
public MemberStrategyFactory() {
List<MemberStrategy> list = new ArrayList<>();
list.add(new OrdinaryMemberStrategy());
list.add(new SilverMemberStrategy());
list.add(new GoldMemberStrategy());
list.add(new PlatinumMemberStrategy());
list.add(new DiamondMemberStrategy());
map = list.stream().collect(Collectors.toMap(MemberStrategy::getType, memberStrategy -> memberStrategy));
}
public static class Holder {
public static MemberStrategyFactory instance = new MemberStrategyFactory();
}
public static MemberStrategyFactory getInstance() {
return Holder.instance;
}
public MemberStrategy get(Integer type) {
return map.get(type);
}
}
工厂类里面添加所有的会员等级的策略接口实现,最后我们的计算方法接口修改为
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
/**
* 根据会员等级计算费用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("费用低于1000,不参与会员折扣");
return money;
}
MemberStrategy memberStrategy = MemberStrategyFactory.getInstance().get(type);
if (memberStrategy == null) {
throw new IllegalStateException("会员等级类型不符合");
}
return memberStrategy.calMoney(money);
}
}
使用了工厂策略后就减少了if else了同时也将各个类型的逻辑处理分开了