设计模式第二话—策略模式

Posted 小仇哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式第二话—策略模式相关的知识,希望对你有一定的参考价值。

一、实际问题:
某电商系统逢年过节都需做线上促销活动,但每次促销活动的方式不一样,所以需要开发一个功能,只要输入原价再选择活动类型后,就能计算出折扣以后的价钱。
简单常规的做法就是:

public static void main(String[]args)
    Scanner scan = new Scanner(System.in));
    String input = scan.nextLine();
    double price = scan.nextLine();
    switch (input)
        case "中秋节":
        price = price * 0.95;
        break;
        case "国庆节":
        if(price>=100)
            price = price - 50;
            break;
        default
            price = price;
    
    println("打折后价钱:"+price);

这样写存在的问题:
若需增加新的促销活动,就要修改这段代码,需新增switch-case分支,非常麻烦且打破了OCP,即“开放-封闭”的原则。
开闭原则:
对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
那有没有什么办法使得我们的报价管理即可扩展、可维护,又可以方便的响应变化呢?当然有解决方案啦,就是我们下面要讲的策略模式。

二、策略模式定义
策略(Strategy)模式:定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。它也被成为政策模式,是一种行为型模式。

图1 策略模式通用类图
Strategy接口(抽象策略角色)
策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。

ConcreteStrategy具体策略角色
实现抽象策略中的操作,该类含有具体的算法。

Context封装角色
即上下文角色,起到承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。

三、策略模式的源代码
//策略接口

public interface IStrategy 
    //定义的抽象算法方法 来约束具体的算法实现方法
    public void doSomething();




// 具体的策略实现
public class ConcreteStrategy1 implements IStrategy 
    //具体的算法实现
    @Override
    public void doSomething() 
        System.out.println("this is ConcreteStrategy1 method...");
    



// 具体的策略实现
public class ConcreteStrategy2 implements IStrategy 
    //具体的算法实现
    @Override
    public void doSomething() 
        System.out.println("this is ConcreteStrategy2 method...");
    



/**
 * 策略上下文
 */
public class Context 
    //持有一个策略实现的引用
    private IStrategy strategy;
    //使用构造器注入具体的策略类
    public StrategyContext(IStrategy strategy) 
        this.strategy = strategy;
    

    public void contextMethod()
        //调用策略实现的方法
        strategy.doSomething();
    


//外部客户端
public class Client 
    public static void main(String[] args) 
        //1.声明具体策略实现
        IStrategy strategy = new ConcreteStrategy1();
        //2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
        Context ctx = new Context(strategy);
        //3.调用上下文对象的方法来完成对具体策略实现的回调
        ctx.contextMethod();
    

四、解决问题
针对我们上面促销活动的例子:我们可以应用策略模式对其进行改造,不同类型的活动有不同的折扣,我们可以将不同类型的活动的报价规则都封装为一个独立的算法,然后抽象出这些报价算法的公共接口

公共报价策略接口:
//报价策略接口
public interface IStrategy 
    //获取折后价的价格
    double getPrice(double price);


中秋节活动策略实现:
//中秋节活动的报价策略实现类
public class ZqStrategy implements IStrategy 
    @Override
    double getPrice(double price) 
        price = 0.95*price;
        return price;
    


国庆活动策略实现:
//国庆活动的报价策略实现类
public class GqStrategy implements IStrategy 
    @Override
    double getPrice(double price) 
        if(price>=100)
            price = price - 50;
        return price;
    


无活动策略实现:
//无活动的报价策略实现类
public class wStrategy implements IStrategy 
    @Override
    double getPrice(double price) 
        return price;
    


策略上下文:
//报价上下文角色
public class Context 
    //持有一个具体的报价策略
    private IStrategy strategy;
    //注入报价策略
    public Context(IStrategy strategy)
        this.strategy = strategy;
    
    //回调具体活动报价策略的方法
    ouble getPrice(double price)
        return strategy.getPrice(price);
    


外部客户端:
//外部客户端
public class Client 
    public static void main(String[] args) 
        //1.创建中秋活动的报价策略
        IQuoteStrategy zqStrategy = new ZqStrategy();
        //2.创建报价上下文对象,并设置具体的报价策略
        Context context = new Context(zqStrategy);
        //3.调用报价上下文的方法
        double price = context.getPrice(100);
        System.out.println("折扣价为:" +price);
    

这个时候,电商系统新推出双十一大促,可以享受折扣5折优惠,那该怎么办呢?这个很容易,只要新增一个报价策略的实现,然后外部客户端调用的时候,创建这个新增的报价策略实现,并设置到策略上下文就可以了,对原来已经实现的代码没有任何的改动。

五、策略模式优缺点
优点:提供了对开闭原则的完美支持,算法可以自由切换,避免使用多重分支条件判断;扩展性良好;复用性好,不同环境类可以方便复用这些策略类。
缺点:策略类数量多,来一种策略就要写一个类;无法在客户端同时使用多个策略类 --> 客户端每次只能使用一个策略类;所有策略类都需要对外暴露(致命缺陷,客户端需要知道所有的策略类,并自行决定使用哪一个策略–>只适用于客户端了解所有策略算法的情况)。

六、使用场景
多个类只有在算法或行为上稍有不同的场景
算法需要自由切换的场景,即如果一个系统要动态地在几种算法之间选择其中一种时
需要屏蔽算法规则的场景,即希望客户知道复杂的与算法有关的数据结构,可以将其封装到策略中

七、参考资料
设计模式之禅

以上是关于设计模式第二话—策略模式的主要内容,如果未能解决你的问题,请参考以下文章

策略模式

设计模式第16篇:策略设计模式

python设计模式第九天策略模式

设计模式@第25章:策略模式

第一梦 策略模式

第23章 行为型模式—策略模式