6-Strategy: choosing the algorithm at run-time

阅读更多

Strategy : choosing the algorithm at run-time

Strategy also adds a “Context” which can be a surrogate class that controls the selection and use of the particular strategy object—just like State ! Here’s what it looks like:

//: strategy:StrategyPattern.java

package strategy;

import com.bruceeckel.util.*; // Arrays2.toString()

import junit.framework.*;

 

// The strategy interface:

interface FindMinima {

  // Line is a sequence of points:

  double[] algorithm(double[] line);

}

 

// The various strategies:

class LeastSquares implements FindMinima {

  public double[] algorithm(double[] line) {

    return new double[] { 1.1, 2.2 }; // Dummy

  }

}

 

class NewtonsMethod implements FindMinima {

  public double[] algorithm(double[] line) {

    return new double[] { 3.3, 4.4 }; // Dummy

  }

}

 

class Bisection implements FindMinima {

  public double[] algorithm(double[] line) {

    return new double[] { 5.5, 6.6 }; // Dummy

  }

}

 

class ConjugateGradient implements FindMinima {

  public double[] algorithm(double[] line) {

    return new double[] { 3.3, 4.4 }; // Dummy

  }

}

 

// The "Context" controls the strategy:

class MinimaSolver {

  private FindMinima strategy;

  public MinimaSolver(FindMinima strat) {

    strategy = strat;

  }

  double[] minima(double[] line) {

    return strategy.algorithm(line);

  }

  void changeAlgorithm(FindMinima newAlgorithm) {

    strategy = newAlgorithm;

  }

}

 

public class StrategyPattern extends TestCase  {

  MinimaSolver solver =

    new MinimaSolver(new LeastSquares());

  double[] line = {

    1.0, 2.0, 1.0, 2.0, -1.0,

    3.0, 4.0, 5.0, 4.0 };

  public void test() {

    System.out.println(

      Arrays2.toString(solver.minima(line)));

    solver.changeAlgorithm(new Bisection());

    System.out.println(

      Arrays2.toString(solver.minima(line)));

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(StrategyPattern.class);

  }

} ///:~

 

//何止是相像,简直就是一样,从教程中的利用接口+组合来完成的过程,这个实现过程和State是一模一样!

//那么该如何理解这些模式之间的区别呢?很多实现的示例是通过继承(http://www.jdon.com/designpatterns/designpattern_Strategy.htm )来实现的,这个比较让人迷惑...但是,有一点//是可以肯定

//的:重要的是设计思想,只要是这个掌握了,具体使用哪个模式是可以转化重构!

 

倒是这些资料很有提示性:

http://archive.cnblogs.com/a/1420362/

 

http://zhongkang.iteye.com/blog/152403

 

http://blog.163.com/chenxuezhen_1/blog/static/9167375120100134622841/

 

具体的总结,得后续完结!(2011-12-29)

//重新整理了一下其中一个参考内容,感觉这个例子还是比较好的!

Strategy Pattern/State Pattern

(1)Strategy Pattern

(i)定义:定义了算法族,把变化的各个部分封装起来,使得算法可以相互替换并独立于使用该算法的客户。
(ii)结构图:见 4-State (Strategy和State的结构图是一样的)

    分析:Strategy模式的接口封装了Context(应用类)中变化的部分,然后在Context中引用了封装的接口,把变化的部分操作委托给Strategy来实现,这样就可以在具体使用中,指定对应的类来实现封装类的不同实现。

(iii)意图:封装变化以便类型在运行时根据需要选择合适的算法。这样提高了代码的复用度,也使程序更改更方便。
(iv)用到的OO原则:封装变化;对扩展开放,对修改关闭;针对接口编程而不是实现编程;多用组合少用继承。
(v)具体样例:
   假设我们要完成公司员工这个类的操作,算法如下:
package com.xforward.test.strategy;

class Employees{
  String height;
  String weight;
  String diploma;
  String role;
  short hours;
  int bonus;
  int earning;
 
  Employees(String role,short hours){
      this.role = role;
      this.hours = hours;
  }
 
  Employees(String role,int bonus){
      this.role = role;
      this.bonus = bonus;
  }
 
  Employees(String role,int bonus,int earning){
      this.role = role;
      this.bonus = bonus;
      this.earning = earning;
  }

  public String Description(){
      String desc="";
      desc += "Heighht:"+height+";";
      desc += "Weighht:"+weight+";";
      desc += "Diploma:"+diploma+";";
      return desc;
  }

  public double getWage(){
      double wage = 0;
     
      if("temp".equals(role)){
         wage = 10*hours;
      }
      else if("accountant".equals(role)){
        wage = 2000+ bonus;
      }
     else if("manager".equals(role)){
         wage = 5000+bonus+ earning*0.05;
      }
      //还有其他的角色....
      return wage;
      }
 
  public static void main(String[] args){
      short s = 10;
      Employees e = new Employees("temp",s);
      System.out.println("Wage :"+e.getWage());
  }

这里,我们假设Employees这个类有两个函数,一个用于个人情况描述的,一个用于计算工资的。个人描述是不变的,而工资的算法则因人而异。这样如果 员工的种类改变了,增加或减少都要对类里面的函数进行修改。这种情况下我们就可以用Strategy Pattern来处理了。

首先把变化的部分也就是GetWage这个函数抽象出来成为一个接口或者抽象类:
package com.xforward.test.strategy;

interface Wage{
 double getWage();
}
然后根据具体需要加入Wage的子类实现getWage():
package com.xforward.test.strategy;

public class TempWage implements Wage {
    short hours;
   
    TempWage(short hours){
        this.hours  = hours;
    }
   
    public double getWage() {
        return 10*hours;
    }

}

package com.xforward.test.strategy;

public class AccountantWage implements Wage {
    int bonus;
   
    AccountantWage(int bonus){
        this.bonus  = bonus;
    }
   
    public double getWage() {
        return 2000+bonus;
    }

}

package com.xforward.test.strategy;

public class ManagerWage implements Wage {
    int bonus;
    int earning;
   
    ManagerWage(int bonus,int earning){
        this.bonus  = bonus;
        this.earning = earning;
    }
   
    public double getWage() {
        return 5000+bonus+earning*0.5;
    }

}


这样以后就可以在Employees中声明一个Wage接口,把工资的计算委托给Wage接口中的函数来实现,Employees就变成:
package com.xforward.test.strategy;

class Employees2{
  String height;
  String weight;
  String diploma;
  String role;

  Wage wage;

  Employees2(Wage wage){
      this.wage = wage;
  }

  public String Description(){
      String desc="";
      desc += "Heighht:"+height+";";
      desc += "Weighht:"+weight+";";
      desc += "Diploma:"+diploma+";";
      return desc;
  }

  public double getWage(){
      //double wage = 0;
     
      return wage.getWage();
      }
 
  public static void main(String[] args){
      short s = 12;
      Wage wage = new TempWage(s);
      double d = new Employees2(wage).getWage();
      System.out.println("Wage = "+d);
     
  }
}
例子中用策略模式的好处是:如果以后有新的工资计算法,我们只需要加入一个新的Wage子类,而不需要对Employees这个类做任何的改动。这样做还 可以复用代码,如果另外一个类有类似的计算和某个工资计算一样,我们也可以直接用Wage这个接口,而不需要加入新的类或接口。

(2) State Pattern

(i)  定义:允许对象在内部状态改变的时候改变其行为,使得对象看起来好像改变了它的类。

(ii)  结构图:见 4-State (Strategy和State的结构图是一样的)

(iii)  意图:把与对象状态相关的行为放入状态组成的类中,使对象状态的行为能够随着状态的改变而该变。

(iv)  用到的OO原则:和Strategy Pattern一样。

(v)  具体样例:

这里我们用TCP连接来举例,TCP连接有三个状态:Open,Lisening, Close,初步实现如下:

package com.xforward.test.state;

enum StateFlag{
      OPEN,CLOSED,LISTENING
}

public class TCPConnection {

      StateFlag state = StateFlag.CLOSED;
     
      public String Open(){
          if(state == StateFlag.OPEN){
              return "TCP has been opened";
          }else if(state == StateFlag.CLOSED){
              state = StateFlag.OPEN;
              return "open successfully";
          }else if(state == StateFlag.LISTENING){
              return "TCP is listening";
          }
          return null;
      }
      public String Close(){
          if(state == StateFlag.OPEN){
              state =StateFlag.CLOSED;
              return "Closed Successfully";
          }else if(state == StateFlag.CLOSED){
              return "TCP has been closed";
          }else if(state == StateFlag.LISTENING){
              state =StateFlag.CLOSED;                
              return "Closed Successfully";
          }
          return null;
      }
     public String Listening(){
      if(state == StateFlag.OPEN){
          state =StateFlag.LISTENING;
          return "Listen Successfully";
      }else if(state == StateFlag.CLOSED){
          return "TCP has been closed";
      }else if(state == StateFlag.LISTENING){
          return "TCP is listening";
      }
      return null;
     }
    
}

这样,如果我们每加一个State的话,要为每个函数都加一条实现语句,不仅麻烦还很容易出现bug.于是我们就想到了State Pattern。(这个例子需要后续完善一下,尤其是测试部分,不是很自然)

实现一个State接口:

package com.xforward.test.state;

public interface State {
     String open();
     String close();
     String listening();
}

实现各个具体状态类:

package com.xforward.test.state;

public class OpenState implements State {
    TCPConnection2 tcp;//用于记录状态
   
    OpenState(TCPConnection2 tcp){
        this.tcp = tcp;
    }
   
    public String open() {
        return "TCP has been opened";
    }
   
    public String close() {
        tcp.state1 = tcp.closeState;
        return "Closed Successfully";
    }

    public String listening() {
        tcp.state1 = tcp.listenState;
        return "Listen Successfully";
    }
   
}

package com.xforward.test.state;

public class ListenState implements State {
    TCPConnection2 tcp;//用于记录状态
   
    ListenState(TCPConnection2 tcp){
        this.tcp = tcp;
    }
   
    public String open() {
        return "TCP is listening";
    }
   
    public String close() {
        tcp.state1 = tcp.closeState;
        return "Closed Successfully";
    }

    public String listening() {
        return "TCP is listening";
    }
   
}

package com.xforward.test.state;

public class CloseState implements State {
    TCPConnection2 tcp;//用于记录状态
   
    CloseState(TCPConnection2 tcp){
        this.tcp = tcp;
    }
   
    public String open() {
        tcp.state1 = tcp.openState;
        return "Open Successfully";
    }
   
    public String close() {
        return "TCP has been Closed";
    }

    public String listening() {
        tcp.state1 = tcp.listenState;
        return "Listen Successfully";
    }
   
}

 

这样改了之后TCPConnection就可以写成:

package com.xforward.test.state;

public class TCPConnection2 {
      OpenState openState = new OpenState(this);
      CloseState closeState = new CloseState(this);
      ListenState listenState = new ListenState(this);
     
      State state1 = closeState ;
     
      public static void main(String[] args){
          TCPConnection2 t = new TCPConnection2();
          t.state1.close();
          t.state1.open();
          t.state1.listening();
      }
     
}

使用State Pattern后如果加入新的状态,我们只需要加入新状态的一个State子类就可以了。

 

(3)Strategy Pattern和State Pattern 异同点:

(i)相同点:两种模式的结构图一样,做法都是用接口或者抽象类来封装变化的方法。一般来说在类中有许多判断语句的时候就可以考虑使用这两种模式。(当然模式使用 时基于变化的,如果判断语句的条件是定死的,那么不用设计模式也没有问题)

(ii) 不同点:意图不一样:State Pattern中很显著的特点就是有状态和行为,并且行为随着状态的改变而改变,相当于判断语句中的条件是一种种状态。State Pattern中实现的状态子类中是有耦合的。而Strategy Pattern,我个人认为判断语句中的条件是类别,不同的类型实现的操作不一样,这样就可以把操作抽象出来。抽象出来的操作子类之间是解耦的。

 

 

 

你可能感兴趣的:(Strategy,策略模式)