Java设计模式(四)策略模式

来源:互联网 发布:溺水 该去救人吗 知乎 编辑:程序博客网 时间:2024/05/16 01:15
本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188

 

          Strategy是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类。

定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换策略模式让算法独立于使用它的客户而独立变化。(原文:The Strategy Pattern defines a family ofalgorithms,encapsulates each one,and makes them interchangeable. Strategy letsthe algorithm vary independently from clients that use it.)

角色:

Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。

ConcreteStrategy:具体的策略实现,也就是具体的算法实现。

Context:上下文,负责和具体的策略类交互,通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。

策略模式的结构示意图如图1所示:


应用场景:

  1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

  2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。

  3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

优点:

   1、 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
 2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
    3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合

    4.提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。

缺点:
 1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量
    2、 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。(这本身没有解除客户端需要选择判断的压力,而策略 模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化的减轻了客户端的压力。)

举一个我看过的策略模式比较经典的例子:报价管理

对不同的客户要报不同的价格,向客户报价是非常复杂的,因此在一些CRM(客户关系管理)的系统中,会有一个单独的报价管理模块,来处理复杂的报价功能。

为了演示的简洁性,假定现在需要实现一个简化的报价管理,实现如下的功能:

(1)对普通客户或者是新客户报全价

(2)对老客户报的价格,统一折扣5%

(3)对大客户报的价格,统一折扣10%

(1)先看策略接口,示例代码如下: 

  1. /** 
  2.  
  3.  * 策略,定义计算报价算法的接口 
  4.  
  5.  */  
  6.   
  7. public interface Strategy {  
  8.   
  9.     /** 
  10.  
  11.      * 计算应报的价格 
  12.  
  13.      * @param goodsPrice 商品销售原价 
  14.  
  15.      * @return 计算出来的,应该给客户报的价格 
  16.  
  17.      */  
  18.   
  19.     public double calcPrice(double goodsPrice);  
  20.   
  21. }  
/**     * 策略,定义计算报价算法的接口     */    public interface Strategy {        /**         * 计算应报的价格         * @param goodsPrice 商品销售原价         * @return 计算出来的,应该给客户报的价格         */        public double calcPrice(double goodsPrice);    }


(2)接下来看看具体的算法实现,不同的算法,实现也不一样,先看为新客户或者是普通客户计算应报的价格的实现,示例代码如下: 

  1. /** 
  2.  
  3.  * 具体算法实现,为新客户或者是普通客户计算应报的价格 
  4.  
  5.  */  
  6.   
  7. public class NormalCustomerStrategy implements Strategy{  
  8.   
  9.     public double calcPrice(double goodsPrice) {  
  10.   
  11.        System.out.println("对于新客户或者是普通客户,没有折扣");  
  12.   
  13.        return goodsPrice;  
  14.   
  15.     }  
  16.   
  17. }  
/**     * 具体算法实现,为新客户或者是普通客户计算应报的价格     */    public class NormalCustomerStrategy implements Strategy{        public double calcPrice(double goodsPrice) {           System.out.println("对于新客户或者是普通客户,没有折扣");           return goodsPrice;        }    }


再看看为老客户计算应报的价格的实现,示例代码如下: 

  1. /** 
  2.  
  3.  * 具体算法实现,为老客户计算应报的价格 
  4.  
  5.  */  
  6.   
  7. public class OldCustomerStrategy implements Strategy{  
  8.   
  9.     public double calcPrice(double goodsPrice) {  
  10.   
  11.        System.out.println("对于老客户,统一折扣5%");  
  12.   
  13.        return goodsPrice*(1-0.05);  
  14.   
  15.     }  
  16.   
  17. }  
/**     * 具体算法实现,为老客户计算应报的价格     */    public class OldCustomerStrategy implements Strategy{        public double calcPrice(double goodsPrice) {           System.out.println("对于老客户,统一折扣5%");           return goodsPrice*(1-0.05);        }    }


再看看为大客户计算应报的价格的实现,示例代码如下:  

  1. /** 
  2.  
  3.  * 具体算法实现,为大客户计算应报的价格 
  4.  
  5.  */  
  6.   
  7. public class LargeCustomerStrategy implements Strategy{  
  8.   
  9.     public double calcPrice(double goodsPrice) {  
  10.   
  11.        System.out.println("对于大客户,统一折扣10%");  
  12.   
  13.        return goodsPrice*(1-0.1);  
  14.   
  15.     }  
  16.   
  17. }  
/**     * 具体算法实现,为大客户计算应报的价格     */    public class LargeCustomerStrategy implements Strategy{        public double calcPrice(double goodsPrice) {           System.out.println("对于大客户,统一折扣10%");           return goodsPrice*(1-0.1);        }    }


(3)接下来看看上下文的实现,也就是原来的价格类,它的变化比较大,主要有:

  • 原来那些私有的,用来做不同计算的方法,已经去掉了,独立出去做成了算法类
  • 原来报价方法里面,对具体计算方式的判断,去掉了,让客户端来完成选择具体算法的功能
  • 新添加持有一个具体的算法实现,通过构造方法传入
  • 原来报价方法的实现,变化成了转调具体算法来实现

示例代码如下: 

  1. /** 
  2.  
  3.  * 价格管理,主要完成计算向客户所报价格的功能 
  4.  
  5.  */  
  6.   
  7. public class Price {  
  8.   
  9.     /** 
  10.  
  11.      * 持有一个具体的策略对象 
  12.  
  13.      */  
  14.   
  15.     private Strategy strategy = null;  
  16.   
  17.     /** 
  18.  
  19.      * 构造方法,传入一个具体的策略对象 
  20.  
  21.      * @param aStrategy 具体的策略对象 
  22.  
  23.      */  
  24.   
  25.     public Price(Strategy aStrategy){  
  26.   
  27.        this.strategy = aStrategy;  
  28.   
  29.     }    
  30.   
  31.     /** 
  32.  
  33.      * 报价,计算对客户的报价 
  34.  
  35.      * @param goodsPrice 商品销售原价 
  36.  
  37.      * @return 计算出来的,应该给客户报的价格 
  38.  
  39.      */  
  40.   
  41.     public double quote(double goodsPrice){  
  42.   
  43.        return this.strategy.calcPrice(goodsPrice);  
  44.   
  45.     }  
  46.   
  47. }  
/**     * 价格管理,主要完成计算向客户所报价格的功能     */    public class Price {        /**         * 持有一个具体的策略对象         */        private Strategy strategy = null;        /**         * 构造方法,传入一个具体的策略对象         * @param aStrategy 具体的策略对象         */        public Price(Strategy aStrategy){           this.strategy = aStrategy;        }          /**         * 报价,计算对客户的报价         * @param goodsPrice 商品销售原价         * @return 计算出来的,应该给客户报的价格         */        public double quote(double goodsPrice){           return this.strategy.calcPrice(goodsPrice);        }    }


(4)写个客户端来测试运行一下,好加深体会,示例代码如下:  

  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.        //1:选择并创建需要使用的策略对象  
  6.   
  7.        Strategy strategy = new LargeCustomerStrategy ();  
  8.   
  9.        //2:创建上下文  
  10.   
  11.        Price ctx = new Price(strategy);  
  12.   
  13.        //3:计算报价  
  14.   
  15.        double quote = ctx.quote(1000);  
  16.   
  17.        System.out.println("向客户报价:"+quote);  
  18.   
  19.     }  
  20.   
  21. }