策略模式

策略模式本质

  分离算法,选择实现

 

对设计原理的实现

  从设计原则上来看,策略模式很好地体现了开-闭原则。通过把一系列可变的算法进行封装,并定义出合理的使用结构,使得在系统出现新算法时,额能很容易的把算法加入到已有的系统中,而已有的实现不需要做任何更改。

 

何时选择策略模式

  1. 出现许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个配置一个类的方法,实现算法动态切换。
  2. 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
  3. 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式避免暴露这些跟算法相关的数据结构。
  4. 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。

 

优缺点

  优点:

  1. 定义一系列算法
  2. 避免多重条件语句
  3. 更好的扩展性

  缺点

  1. 客户必须了解每种策略的不同
  2. 怎讲对象数目
  3. 只适合扁平的算法结构(一系列算法的地位是平等的,是可以相互替换的,在运行时刻只有一个算法被使用,限制了算法的使用层级,使用的时候不能嵌套使用)

 

示例:工资支付的实现思路

  功能:工资支付方式的问题。很多企业的工资支付方式是很灵活的,可支付的方式是很多的,比如:人民币现金支付、美元现金支付、银行转账到工资账号、银行转账到工资卡,甚至可能工资转股权等方式。

  要实现这样的功能,策略模式是一个很好的选择,在实现这个功能时,不同的策略算法需要的数据是不一样的,如:现金支付就不需要银行账号,而银行转账就需要账号。这就导致在设计策略接口中的方法时,不太好确定参数的个数,而且,就算现在吧所有参数都列上了,今后扩展呢?难道再来修改策略接口吗?那到底如何实现才能扩展时最方便呢?

  解决方案一:就是把上下文(context)当作参数传递给策略对象。这样,如果要扩展新的策略实现,只需要扩展上下文就可以了,已有的实现不需要做任何更改。

 

  实现代码:

    /**

    * 支付工资的策略接口,公司有多种支付工资的算法

    * 比如:现金,银行卡,美元支付等

    **/

    public interface PaymentStrategy{

      //公司给某人真正支付工资

      //@param ctx 支付工资的上下文,里面包含算法需要的数据

      public void pay(PaymentContext ctx);

 

    }

 

    //人民币现金支付

    public class RMBCash implements PaymentStrategy{

      public void pay(PaymentContext  ctx){

        System.out.println("现金给"+ctx.getUserName()+"人民币现金支付"+ctx.getMoney()+"元");

      }

    }

 

    //美元现金支付

    public class DollarCash implements PaymentStrategy{

      public void pay(PaymentContext  ctx){

        System.out.println("现金给"+ctx.getUserName()+"美元现金支付"+ctx.getMoney()+"元");

      }

    }

 

    //支付工资的上下文,每个人的工资不同,支付方式也不同

    public class PaymentContext{

      private String userName = null;

      private double money = 0.0;

      //支付工资的方式的策略接口

      private PaymentStrategy strategy = null;

      //构造函数,传入被支付工资的人员,应支付的金额和具体的支付策略

      //userName 被支付工资的人员  money 应支付的金额  strategy 具体的支付策略

      public PaymentContext (String userName, double money, PaymentStrategy strategy){

        this.userName = userName;

        this.money = money;

        this.strategy = strategy;

      }

      //省略get方法,只有get方法,让策略算法在实现的时候,根据需要来获取上下文的数据

      //立即支付工资

      public void payNow(){

        //使用客户希望的支付策略来支付工资

        this.strategy.pay(this);

      }

    }

 

    //客户端

    public class Client{

      public static void main(String[] agrs){

      //创建相应的支付策略

      PaymentStrategy strategyRMB = new RMBCash();

      PaymentStrategy strategyDollar = new DollarCash();

      //准备小李的支付工资上下文

      PaymentContext ctx1 = new PaymentContext ("小李",5000,strategyRMB);

      //向小李支付工资

      ctx1.payNow();

      //切换一个人,给petter支付工资

      PaymentContext ctx2 = new PaymentContext ("petter",8000,strategyDollar);

      ctx2.payNow();

    }

    }

    

    扩展示例

    现在我们要加一种支付方式,要求能支付到银行卡

    代码示例:

    //扩展的支付上下文对象

    public class PaymentContext2 extends PaymentContext{

      //银行账号

      private String account = null;

      //构造函数

      public PaymentContext2 (String userName, double money, String account, PaymentStrategy strategy){

        super(userName,money, strategy);

        this.account = account;

      }

      public String getAccount(){return account;}

    }

 

    public class Card implements PaymentStrategy{

      public void pay(PaymentContext ctx){

        PaymentContext2 ctx2 = (PaymentContext2)ctx;

        System.out.println("现在给"+ctx2.getUserName()+"的"+ctx2.getAccount()+"账号支付了"+ctx.getMoney()+"元");

      }

    }

 

    public class Client {

      public static void main(String[] agrs){

        PaymentStrategy strategyCard = new Card();

        PaymenttContext ctx= new PaymentContext2("小王",9000,"00121113",strategyCard);

        ctx.payNow();

      }

    }

 

    另一种扩展方式

    上面那种方式是通过上下文对象来准备新的算法需要的数据,还有一种就是通过策略的构造函数来传入新算法需要的数据,这样实现就不需要扩展上下文了,直接添加一个新的算法实现就可以了。

    代码示例:

    public class Card2 impements PaymentStrategy{

      //账号信息

      private String account = "";

      //构造函数

      public Card2(String account){

        this.account = account;

      }

      public void pay(PaymentContex ctx){

        System.out.println("现在给"+ctx2.getUserName()+"的"+ctx2.getAccount()+"账号支付了"+ctx.getMoney()+"元");

      }

    } 

    两种实现扩展方式的比较:

      对与扩展上下文的方式

        优点:

      1. 这样实现,使所有策略的风格更统一;
      2. 在上下文添加的数据可以视为公共数据,别的相应算法也能用上;

        缺点:

          1. 如果这些数据只是用于特定的算法,这样数据就有点浪费;

          2. 每次添加新的算法都去扩展上下文,容易造成复杂的上下文对象层次;

      对与在策略算法的实现上添加自己需要的数据方式

        优点:

          1. 实现简单

        缺点:

          1. 和其他策略算法的实现方式不一样

          2. 外部使用这个策略算法的时候也不一样,难以以一个统一的方式来动态切换策略算法。

 

posted @ 2018-08-21 18:58  忍着不哭泣  阅读(123)  评论(0编辑  收藏  举报