策略(Strategy)模式

Posted qlqwjy

tags:

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

  策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响客户端的情况下发生变化。

  策略模式相当于可插入式的算法。可以使得在保持接口不变的情况下,使具体算法可以互换。

 1.  简介

  策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。也就是说:准备一组算法,并将每一个算法封装起来,使得它们可以互换。

  简略类图如下:

  技术图片

2.  结构

  策略又称作政策(Policy)模式。其结构如下:

技术图片

这个模式涉及到三个角色:

(1)环境(Context)角色:持有一个Strategy的引用

(2)抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或者抽象类实现。此角色给出所有的具体策略类的接口。

(3)具体策略(ConcreteStrategy)类,包装了相关的算法或者行为。

 

 伪代码如下:

package cn.qlq.strategy;

public class Context 

    private Strategy strategy;

    public void contextInterface() 
        strategy.strategyInterface();
    

    public Strategy getStrategy() 
        return strategy;
    

    public void setStrategy(Strategy strategy) 
        this.strategy = strategy;
    

package cn.qlq.strategy;

public interface Strategy 

    void strategyInterface();
package cn.qlq.strategy;

public class ConcreteStrategy implements Strategy 

    @Override
    public void strategyInterface() 
        System.out.println("具体的算法逻辑");
    

 

   一般而言,有意义的策略模式的应用会涉及多于一个的具体策略角色。上面的代码可以作为一个策略模式的骨架。

 

3.  模式的实现

 注意的地方:

(1)所有的具体策略类都有一些公有的行为。这时候就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。这时候抽象策略角色必须用抽象类实现而不能使用接口。

(2)策略模式在每一个时刻都只能使用一个策略对象,但是有的时候一个应用程序同时与几个策略对象相联系。换言之,在应用程序启动时,所有的策略对象就已经被创立出来,而应用程序可以在几个策略对象之间调换。这只有在策略对象不会耗费很多计算机内存资源的情况下才可行,只有在策略对象初始化会花费很长时间的情况下才需要。

 

假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。网站内的所有图书可能有折扣,大致分为:没有折扣、标价上面减去定额2元、按3%比例折扣。

使用策略模式描述的话这些不同的算法都是不同的具体策略角色:NoDiscountStrategy表示算法一;FlatDiscountStrategy表示算法二;PercentageStrategy表示算法三。

类图如下:

技术图片

 

代码如下:

package cn.qlq.strategy;

public abstract class DiscountStrategy 

    /**
     * 单价
     */
    protected float price;

    /**
     * 数量
     */
    protected int numbers;

    abstract float calculateDiscount();

    public DiscountStrategy(float price, int numbers) 
        super();
        this.price = price;
        this.numbers = numbers;
    

    public float getPrice() 
        return price;
    

    public void setPrice(float price) 
        this.price = price;
    

    public int getNumbers() 
        return numbers;
    

    public void setNumbers(int numbers) 
        this.numbers = numbers;
    

  这个是一个抽象类,而抽象类不可能有实例。上面的两个属性以及构造子是为了约束具体策略类应当赋值给单价和数量,然后调用 calculateDiscount 算出折扣。

 

package cn.qlq.strategy;

public class NoDiscountStrategy extends DiscountStrategy 

    public NoDiscountStrategy(float price, int numbers) 
        super(price, numbers);
    

    @Override
    float calculateDiscount() 
        return 0;
    

package cn.qlq.strategy;

public class FlatDiscountStrategy extends DiscountStrategy 

    /**
     * 折扣金额
     */
    private float discountAmount;

    public FlatDiscountStrategy(float price, int numbers, float discountAmount) 
        super(price, numbers);

        this.discountAmount = discountAmount;
    

    @Override
    float calculateDiscount() 
        return discountAmount * numbers;
    

    public float getDiscountAmount() 
        return discountAmount;
    

    public void setDiscountAmount(float discountAmount) 
        this.discountAmount = discountAmount;
    

package cn.qlq.strategy;

public class PercentageStrategy extends DiscountStrategy 

    /**
     * 折扣比例
     */
    private float discountPercent;

    public PercentageStrategy(float price, int numbers, float discountPercent) 
        super(price, numbers);

        this.discountPercent = discountPercent;
    

    @Override
    float calculateDiscount() 
        return discountPercent * price * numbers;
    

    public float getDiscountPercent() 
        return discountPercent;
    

    public void setDiscountPercent(float discountPercent) 
        this.discountPercent = discountPercent;
    

 

何时使用何种策略?

  策略模式并不负责决定使用哪个具体策略,这种决定应该由客户端决定。策略模式仅仅封装算法。而且客户端在使用不同的策略模式的同时需要了解不同策略模式需要的参数。

  测试模式不适合于处理同时嵌套多于一个算法的清形。一般而言,策略模式只适用于在几种算法中选择一种。

  比如在上面基础增加了第四种法则:在所有的折扣算法计算后,总的折扣不能超过1000。这也就意味着客户端需要使用上面算法一、二、三计算出折扣总值,再使用算法四。这就需要再借助于装饰者模式

 

4.  适用场景与优缺点

1.  适用场景

在下面的场景下应当使用策略模式:

(1)一个系统有许多类,它们之间的区别仅在于它们的行为。

(2)一个系统中需要动态地在几种算法中选择一种。

(3)一个算法使用的数据不需要客户端知道

(4)一个对象有多种行为,如果不用恰当的模式就只能使用多重的条件选择语句来实现。此时,用策略模式可以避免难以维护的多重选择,并体现面向对象的思想。

 

2.  优点:

(1)策略模式提供了管理相关算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类中,从而避免重复的代码。

(2)策略模式提供了可以替换继承关系的办法。继承使得动态改变算法或行为变得不可能。

(3)使用策略模式可以避免使用多重条件判断语句。

 

3.  缺点:

(1)客户端必须知道所有的具体策略类,并自行决定使用哪种策略类。

(2)策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端,而将策略类设计成可共享的。这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。

 

5.  操作系统中的策略模式-windows屏幕保护程序的设置

  每一个屏幕保护程序都有不同的参数设置,如下:

 技术图片

  每一种程序提供不同的算法,在屏幕上放映3D文字、图片等。用户需要为每一种程序设定所需的参数。策略模式提供了解决这个问题的方案。当用户选中一个屏幕保护程序后就需要设定程序的参数。如下是3D文字所需要的参数:

技术图片

 

  这是典型的策略模式的应用:每一个屏幕保护程序都有一个自己的参数设定窗口。这个窗口便是具体策略角色,它代表不同的算法;用户可以选择一个算法并设定其参数;所有的参数设定窗口都有自恰的界面设计,让用户相信策略对象后面有一个共同的抽象接口约束它们,它就是抽象策略角色。

 

以上是关于策略(Strategy)模式的主要内容,如果未能解决你的问题,请参考以下文章

策略模式(Strategy)

策略模式(strategy)

策略模式(Strategy Pattern)

设计模式 之 策略(Strategy)模式

图解设计模式-Strategy模式

策略模式(Strategy)