策略模式-业务场景和实战优化

781 阅读3分钟

策略模式

业务场景

加入我们有这样的业务场景:系统根据会员等级来返回不同的折扣。大多数同学会写出以下代码:

if("普通会员".equals(level)){
    return "9折";
}else if("白银会员".equals(level)){
    return "8折";
}else if("黄金会员".equals(level)){
    return "7折";
}else(){
    return "无折扣";
}

这个代码可能会存在哪些问题呢

  • 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低
  • 如果产品要增加一种会员等级,那只能在原有代码上修改

说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及单一原则

  • 开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码。
  • 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。

如果你的代码就是这样:有多个if...else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。

策略模式定义

策略模式定义了算法组,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。这个策略模式的定义是不是有点抽象呢?那我们来看点通俗易懂的比喻:

假设你跟不同性格类型的小姐姐约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去逛街买买买最合适。当然,目的都是为了得到小姐姐的芳心,请看电影、吃小吃、逛街就是不同的策略。

策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。

策略模式使用

策略模式实现的必要步骤如下:

  • 一个接口或者抽象类,里面两个方法(一个方法匹配操作类型,一个可替换的逻辑实现方法)
  • 不同策略的差异化实现(就是说,不同策略的实现类)
  • 使用策略模式

一个接口,两个方法

public interface Strategy {
    
    //获取会员类型
    MemberTypeEnum getMemberType();
    
    //具体折扣后的金额
    double compute(double money);
}

不同策略的差异化实现


// 普通会员策略
public class OrdinaryStrategy implements Strategy {

    @Override
    public MemberTypeEnum getMemberType() {
        return MemberTypeEnum.Ordinary;
    }

    @Override
    public double compute(double money) {
        System.out.println("普通会员 不打折");
        return money;
    }
}

// 白银会员策略
public class SilverStrategy implements Strategy {

    @Override
    public MemberTypeEnum getMemberType() {
        return MemberTypeEnum.Silver;
    }

    @Override
    public double compute(double money) {

        System.out.println("白银会员 优惠50元");
        return money - 50;
    }
}

// 黄金会员策略
public class GoldStrategy implements Strategy{

    @Override
    public MemberTypeEnum getMemberType() {
        return MemberTypeEnum.Gold;
    }

    @Override
    public double compute(double money) {
        System.out.println("黄金会员 8折");
        return money * 0.8;
    }
}

使用策略模式

如何使用呢?我们借助spring的生命周期,使用ApplicationContextAware接口,把对用的策略,初始化到map里面。然后对外提供compute方法即可:

@Component
public class StrategyService implements ApplicationContextAware{
  
    private Map<MemberTypeEnum, Strategy> strategyMap = new ConcurrentHashMap<>();
    

    public void compute(MemberTypeEnum memberTypeEnum, double money) {
        Strategy strategy = strategyMap.get(memberTypeEnum);
        if (strategy != null) {
            strategy.compute(money);
        }
    }


    //把不同策略放到map
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Strategy> tmepMap = applicationContext.getBeansOfType(Strategy.class);
        tmepMap.values().forEach(strategyService -> strategyMap.put(strategyService.getMemberType(), strategyService));
    }
}

这里的Map其实使用的是一种伪工厂模式。

我曾一度 以为 策略模式不过如此。以为代码优化到这已经可以了。

但是还有一个恐怖的事情,if-else 依然存在 :)

我尝试翻阅了许多书籍,查看如何消除 策略模式中的 if-else

书中大部分的方法是,使用简单工厂 + 策略模式。把 if - else 切换为 switch 创建一个工厂方法而已。

但是这远远没有达到我想要的效果,略彻底消除if else参考我的另一篇文章:

使用注解方式+策略+工厂模式彻底消除if else