zoukankan      html  css  js  c++  java
  • 设计模式大类--行为模式(中)

    四、Chain of Responsibility(责任链)
    描述:一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request. 也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去
    好处:降低了类之间的耦合性

    例子:
    抽象处理者角色
    public abstract class Handler {

    /**
    * 持有后继的责任对象
    */
    protected Handler successor;
    /**
    * 示意处理请求的方法,虽然这个示意方法是没有传入参数的
    * 但实际是可以传入参数的,根据具体需要来选择是否传递参数
    */
    public abstract void handleRequest();
    /**
    * 取值方法
    */
    public Handler getSuccessor() {
    return successor;
    }
    /**
    * 赋值方法,设置后继的责任对象
    */
    public void setSuccessor(Handler successor) {
    this.successor = successor;
    }

    }

      具体处理者角色
    public class ConcreteHandler extends Handler {
    /**
    * 处理方法,调用此方法处理请求
    */
    @Override
    public void handleRequest() {
    /**
    * 判断是否有后继的责任对象
    * 如果有,就转发请求给后继的责任对象
    * 如果没有,则处理请求
    */
    if(getSuccessor() != null)
    {
    System.out.println("放过请求");
    getSuccessor().handleRequest();
    }else
    {
    System.out.println("处理请求");
    }
    }

    }

      客户端类
    public class Client {

    public static void main(String[] args) {
    //组装责任链
    Handler handler1 = new ConcreteHandler();
    Handler handler2 = new ConcreteHandler();
    handler1.setSuccessor(handler2);
    //提交请求
    handler1.handleRequest();
    }

    }
      可以看出,客户端创建了两个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,而第二个处理者对象没有下家。然后客户端将请求传递给第一个处理者对象。
      由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理。因此,第一个处理者对象接到请求后,会将请求传递给第二个处理者对象。由于第二个处理者对象没有下家,于是自行处理请求。

    五、Command(命令模式)
    描述:对命令进行封装,将发出命令的责任和执行命令的责任分割开
    好处:面向接口编程、能实现Undo功能。

    例子:
    典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":

    public interface Command {
      public abstract void execute ( );
    }


    具体不同命令/请求代码是实现接口Command,下面有三个具体命令

    public class Engineer implements Command {

      public void execute( ) {
        //do Engineer's command
      }
    }

    public class Programmer implements Command {

      public void execute( ) {
        //do programmer's command
      }
    }

    public class Politician implements Command {

      public void execute( ) {
        //do Politician's command
      }
    }

    按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去:

    public class producer{
      public static List produceRequests() {
        List queue = new ArrayList();
        queue.add( new DomesticEngineer() );
        queue.add( new Politician() );
        queue.add( new Programmer() );
        return queue;
      }

    }

    这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面如何调用Command模式:

    public class TestCommand {
      public static void main(String[] args) {
        
        List queue = Producer.produceRequests();
        for (Iterator it = queue.iterator(); it.hasNext(); )
            //取出List中东东,其他特征都不能确定,只能保证一个特征是100%正确,
            // 他们至少是接口Command的"儿子".所以强制转换类型为接口Command

            ((Command)it.next()).execute();
      

      }
    }

    由此可见,调用者基本只和接口打交道,不合具体实现交互,这也体现了一个原则,面向接口编程,这样,以后增加第四个具体命令时,就不必修改调用者TestCommand中的代码了.

    六、State(状态模式)
    描述:不同的状态,不同的行为;或者说,每个状态有着相应的行为
    优点:封装转换过程,也就是转换规则、枚举可能的状态,因此,需要事先确定状态种类。

    例子:
    请看下例:

    public class Context{

      private Color state=null;

      public void push(){

        //如果当前red状态 就切换到blue
        if (state==Color.red) state=Color.blue;

        //如果当前blue状态 就切换到green
        else if (state==Color.blue) state=Color.green;

        //如果当前black状态 就切换到red
        else if (state==Color.black) state=Color.red;

        //如果当前green状态 就切换到black
        else if (state==Color.green) state=Color.black;
        
        Sample sample=new Sample(state);
        sample.operate();
      }

      public void pull(){

        //与push状态切换正好相反

        if (state==Color.green) state=Color.blue;
        else if (state==Color.black) state=Color.green;
        else if (state==Color.blue) state=Color.red;
        else if (state==Color.red) state=Color.black;

        Sample2 sample2=new Sample2(state);
        sample2.operate();
      }

    }

    在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它.

    另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂.

    例如: 银行帐户, 经常会在Open 状态和Close状态间转换.

    例如: 经典的TcpConnection, Tcp的状态有创建 侦听 关闭三个,并且反复转换,其创建 侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State

    例如:信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit,每个状态对应的行为应该是比较大的.适合使用State

    例如:在工具箱挑选不同工具,可以看成在不同工具中切换,适合使用State.如 具体绘图程序,用户可以选择不同工具绘制方框 直线 曲线,这种状态切换可以使用State.

    如何使用
    State需要两种类型实体参与:

    1.state manager 状态管理器 ,就是开关 ,如上面例子的Context实际就是一个state manager, 在state manager中有对状态的切换动作.
    2.用抽象类或接口实现的父类,,不同状态就是继承这个父类的不同子类.

    以上面的Context为例.我们要修改它,建立两个类型的实体.
    第一步: 首先建立一个父类:

    public abstract class State{

      public abstract void handlepush(Context c);
      public abstract void handlepull(Context c);
      public abstract void getcolor();

    }

    父类中的方法要对应state manager中的开关行为,在state manager中 本例就是Context中,有两个开关动作push推和pull拉.那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull(); 同时还需要一个获取push或pull结果的方法getcolor()

    下面是具体子类的实现:

    public class BlueState extends State{

      public void handlepush(Context c){
         //根据push方法"如果是blue状态的切换到green" ;
         c.setState(new GreenState());

      }
      public void handlepull(Context c){

         //根据pull方法"如果是blue状态的切换到red" ;
        c.setState(new RedState());

      }

      public abstract void getcolor(){ return (Color.blue)}

    }


    同样 其他状态的子类实现如blue一样.

    第二步: 要重新改写State manager 也就是本例的Context:

    public class Context{

      private Sate state=null; //我们将原来的 Color state 改成了新建的State state;

      //setState是用来改变state的状态 使用setState实现状态的切换
      pulic void setState(State state){

        this.state=state;

      }

      public void push(){

        //状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心
        state.handlepush(this);
        
        //因为sample要使用state中的一个切换结果,使用getColor()
        Sample sample=new Sample(state.getColor());
        sample.operate();

      }

      public void pull(){

        state.handlepull(this);
        
        Sample2 sample2=new Sample2(state.getColor());
        sample2.operate();

      }

    }


    至此,我们也就实现了State的refactorying过程

  • 相关阅读:
    手写web框架之加载配置项目
    JAVA中注解的实现原理
    使用Mock 测试 controller层
    如何写resultful接口
    RSA加密、解密、签名、验签的原理及方法
    AES256位加密
    聊聊分布式事务,再说说解决方案
    分布式锁简单入门以及三种实现方式介绍
    redis总结(面试中容易遇到的)
    字符串匹配的KMP算法
  • 原文地址:https://www.cnblogs.com/aiguozhe/p/3763495.html
Copyright © 2011-2022 走看看