天天看点

略懂设计模式之策略模式

一个阳光明媚的上午,靓仔正在开心的划水摸鱼,耳机里传来音乐“不是吧不是吧,难道单压也算压......”

略懂设计模式之策略模式

产品经理突然出现在身后,拍了拍我的肩膀

产品经理:又在摸鱼,看来工作不饱和啊,正好有个需求你做一下。公司推出了会员制度,分普通会员和超级会员,普通会员购买商品打九折,超级会员购买商品打八折。

靓仔:就这?简单

public Double computePrice(String type, Double price) {
    if ("VIP".equals(type)) {
        // 普通会员9折优惠
        return price * 0.9;
    } else if ("S_VIP".equals(type)) {
        // 超级会员8折优惠
        return price * 0.8;
    } else {
        // 非会员无优惠
        return price;
    }
}
           

几天后。。。

产品经理:我们增加了活动送的一个月体验会员,与普通会员享受同等九折待遇,但是只能享受最高20的优惠金额。

靓仔:明白,不就是“穷逼vip”吗

于是就在原来的代码上改了改

public Double computePrice(String type, Double price) {
    if ("VIP".equals(type)) {
        // 普通会员9折优惠
        return price * 0.9;
    } else if ("S_VIP".equals(type)) {
        // 超级会员8折优惠
        return price * 0.8;
    } else if ("BEGGAR_VIP".equals(type)) {
        // 穷逼vip 9折优惠,最大优惠金额20
        return price > 200 ? (price - 20) : price * 0.9;
    }else {
        // 非会员无优惠
        return price;
    }
}
           

有没有觉得 if-else 特别多,而且一旦再增加会员种类,那么看上去就更繁琐,代码耦合严重,维护起来十分不方便。

怎么办,重构一下代码呗

首先提取出价格计算的接口类

public interface PriceStrategy {
    Double computePrice(Double price);
}
           

然后针对不同的会员类型,实现不同会员价格计算接口,提供算法

// 普通会员
public class VipPriceStrategy implements PriceStrategy{
    @Override
    public Double computePrice(Double price) {
        return price * 0.9;
    }
}

// 超级会员
public class SVipPriceStrategy implements PriceStrategy{
    @Override
    public Double computePrice(Double price) {
        return price * 0.8;
    }
}

// 穷逼会员
public class BeggarVipPriceStrategy implements PriceStrategy{
    @Override
    public Double computePrice(Double price) {
        return price > 200 ? (price - 20) : price * 0.9;
    }
}
           

增加一个上下文角色,封装算法对高层屏蔽,高层模块只用访问Context

public class PriceContext {
    private PriceStrategy priceStrategy;   
    
    public PriceContext(PriceStrategy priceStrategy) {
        this.priceStrategy = priceStrategy;
    }

    public Double computePrice(Double price) {
        return priceStrategy.computePrice(price);
    }
}
           

外部调用,计算价格

public Double getPrice(String type, Double price) {
    PriceStrategy priceStrategy;
    if ("VIP".equals(type)) {
        priceStrategy = new VipPriceStrategy();
    } else if ("S_VIP".equals(type)) {
        priceStrategy = new SVipPriceStrategy;
    } else if ("BEGGAR_VIP".equals(type)) {
        priceStrategy = new BeggarVipPriceStrategy;
    } else {
        return price;
    }
    PriceContext priceContext = new PriceContext(priceStrategy);
    return priceContext.computePrice(price);
}
           

没错,这就是策略模式

  • 环境(Context):持有一个 Strategy 的引用。
  • 抽象策略(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(ConcreteStrategy):包装了相关的算法或行为。
略懂设计模式之策略模式

有朋友可能会问了,这和工厂模式有什么区别吗?

我们再来看下工厂模式。

简单工厂模式:

略懂设计模式之策略模式

看上去简直一摸一样吧。

其实工厂模式和设计模式一直给人一种错觉,总感觉是一样的,没有丝毫的区别。

直到我看到一个网友的解读:

工厂模式中只管生产实例,具体怎么使用工厂实例由调用方决定,策略模式是将生成实例的使用策略放在策略类中配置后才提供调用方使用。工厂模式调用方可以直接调用工厂实例的方法属性等,策略模式不能直接调用实例的方法属性,需要在策略类中封装策略后调用。

一个注重的是实例的生产,一个注重的是策略方法。

好了,这个时候再来看我们的代码,好像越来越复杂了,虽然用策略模式将具体的算法都抽离出来了,但是 if-else 的问题还是没有解决啊

思考一下,我们可不可以结合以下工厂模式,来去掉烦人的 if-else

可以把策略对象初始化到一个 map 进行管理

public interface PriceStrategy {
    Map<String, PriceStrategy> map = new ConcurrentHashMap(){{
        put("VIP", new VipPriceStrategy());
        put("S_VIP", new SVipPriceStrategy());
        put("BEGGAR_VIP", new BeggarVipPriceStrategy());
    }};

    Double computePrice(Double price);
}

public class PriceContext {
    private PriceStrategy priceStrategy;

    public PriceContext(String type) {
        this.priceStrategy = PriceStrategy.map.get(type);
    }

    public Double computePrice(Double price) {
        return priceStrategy.computePrice(price);
    }
}

           

外部调用

public Double computePrice(String type, Double price) {
    PriceContext priceContext = new PriceContext(type);
    return priceContext.computePrice(price);
}
           

舒服了,终于干掉了 if-else

策略模式优缺点

优点:

  • 策略模式遵循开闭原则,实现代码的解耦合,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 算法的使用就和算法本身分开,符合单一职责原则

缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
  • 策略模式可能会造成系统产生很多具体策略类

总结

其实我们在工作中使用设计模式的时候,不需要被条条框框所束缚,设计模式可以有很多变种,也可以结合几种设计模式一起使用,别忘了使用设计模式的初衷是什么,不要为了使用设计模式而使用设计模式。

END

往期推荐

基于 Mysql 实现一个简易版搜索引擎

如何保证接口的幂等性?

你必须了解的分布式事务解决方案

就这?分布式 ID 发号器实战

略懂设计模式之工厂模式

本文来自博客园,作者:靓仔聊编程,转载请注明原文链接:https://www.cnblogs.com/liangzaiit/p/15335150.html