一、定义

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

二、适用场景

  • 1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
  • 2.一个操作中含有庞大的多分支机构,并且这些分支决定于对象的状态。
 三、角色
  • Context(环境类):环境类拥有各种不同状态的对象,作为外部使用的接口,负责调用状态类接口。
  • State(抽象状态):抽象状态既可以为抽象类,也可以直接定义成接口。主要用于定义状态抽象方法,具体实现由子类负责。
  • ConcreteState(具体状态类):具体状态类为抽象状态的实现者,不同的状态类对应这不同的状态,其内部实现也不相同。环境类中使用不同状态的对象时,能实现不同的处理逻辑。
四、场景展示
类图如下:
场景描述:一个即时通讯客户端连接服务端网络的设计。客户端连接服务器时存在多种状态,最初是通过if-else判断来实现,代码耦合度比较高。使用状态设计模式之后,代码清晰很多,耦合降低。
首先定义接口
package com.ssy.wlj.state;
/**
 * 类说明: 
 * @author happy
 * @since  2019年5月25日
 * 
 */
public interface ConnectState {
    void handleRequest();
}

定义各个状态的实现类:

(1)连接状态

package com.ssy.wlj.state;

/**
 * 类说明: 连接状态
 * 
 * @author happy
 * @since 2019年5月25日
 * 
 */
public class ConnectingState implements ConnectState {

    private ConnectMachine connectMachine;

    public ConnectingState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        waitingConnect();
    }

    private void waitingConnect() {
        // loading and do something
    }

}

(2)连接失败状态

package com.ssy.wlj.state;
/**
 * 类说明: 连接失败状态
 * @author happy
 * @since  2019年5月25日
 * 
 */
public class ConnectFailureState implements ConnectState{

    private ConnectMachine connectMachine;

    public ConnectFailureState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        connectFailure();
    }

    private void connectFailure() {
        //do something
    }

}

(3)连接成功状态

package com.ssy.wlj.state;

/**
 * 类说明: 连接成功状态
 * 
 * @author happy
 * @since 2019年5月25日
 * 
 */
public class ConnectSuccessState implements ConnectState {

    private ConnectMachine connectMachine;

    public ConnectSuccessState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        connectSuccess();
    }

    private void connectSuccess() {
        // do something
    }

}

(4)断开连接状态

package com.ssy.wlj.state;
/**
 * 类说明: 断开连接状态
 * @author happy
 * @since  2019年5月25日
 * 
 */
public class DisconnectState implements ConnectState {

    private ConnectMachine connectMachine;

    public DisconnectState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        disConnect();
    }

    private void disConnect() {
        //do something
    }

}

(5)初始状态

package com.ssy.wlj.state;
/**
 * 类说明: 初始状态
 * @author happy
 * @since  2019年5月25日
 * 
 */
public class InitState implements ConnectState {

    private ConnectMachine connectMachine;

    private boolean connectResult;

    public InitState(ConnectMachine connectMachine) {
        this.connectMachine = connectMachine;
    }

    @Override
    public void handleRequest() {
        doConnect();
        connectMachine.waitingConnect();
    }

    private void doConnect() {
        //connect server
        if (connectResult) {
            connectSuccess();
        } else {
            connectFailure();
        }
    }

    private void connectSuccess() {
        connectMachine.connectSuccess();
    }

    private void connectFailure() {
        connectMachine.connectFailure();
    }

}

通讯设备要持有所有的状态,并在初始化的时候,要设置其开始的状态,然后通讯设备的各个行为,就委托到了各个状态中自己维护,代码如下:

package com.ssy.wlj.state;
/**
 * 类说明: 通信设备
 * @author happy
 * @since  2019年5月25日
 * 
 */
public class ConnectMachine {
    private ConnectState initState;
    private ConnectState connectingState;
    private ConnectState reconnectState;
    private ConnectState disConnectState;
    private ConnectState connectSuccessState;
    private ConnectState connectFailureState;

    public ConnectMachine() {
        initState = new InitState(this);
        connectingState = new ConnectingState(this);
        reconnectState = new ReConnectingState(this);
        disConnectState = new DisconnectState(this);
        connectSuccessState = new ConnectSuccessState(this);
        connectFailureState = new ConnectFailureState(this);
    }

    public void doConnect() {
        initState.handleRequest();
    }

    public void waitingConnect() {
        connectingState.handleRequest();
    }

    public void reConnect() {
        reconnectState.handleRequest();
    }

    public void disConnect() {
        disConnectState.handleRequest();
    }

    public void connectSuccess() {
        connectSuccessState.handleRequest();
    }

    public void connectFailure() {
        connectFailureState.handleRequest();
    }
}

 最后写测试类:

package com.ssy.wlj.state;
/**
 * 类说明: 测试类
 * @author happy
 * @since  2019年5月25日
 * 
 */
public class Client {
    
    public static void main(String[] args) {
        ConnectMachine machine = new ConnectMachine();
        machine.connectFailure();
        machine.reConnect();
        machine.connectSuccess();
    }

}

如果不使用状态模式,所有的逻辑判断全部写在ConnectMachine类里面,而且是通过大量if-else判断实现,代码可读性比较差。改成状态模式之后,实现细节交给具体状态类去实现,ConnectMachine只是提供给外部系统使用的接口,具体实现对于用户来说是不可的。因为只是展示状态模式的设计,使用的是回调的方式,在项目中使用时可以引入Rxjava,响应式编程更加能体会到其中妙处。

 总结

1.状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
理解:这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,这就是说行为会随着内部状态而改变。
“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象

2.状态模式要点

  • (1)客户不会和状态进行交互,全盘了解状态是 context的工作
  • (2)在状态模式中,每个状态通过持有Context的引用,来实现状态转移
  • (3)使用状态模式总是会增加设计中类的数目,这是为了要获得程序可扩展性,弹性的代价,如果你的代码不是一次性的,后期可能会不断加入不同的状态,那么状态模式的设计是绝对值得的。【同时也是一个缺点】
  • (4)状态类可以被多个context实例共享

3.状态模式和策略模式对比

首先让我们来看看它们之间更多的相似之处:

  • 添加新的状态或策略都很容易,而且不需要修改使用它们的Context对象。
  • 它们都让你的代码符合OCP原则(软件对扩展应该是开发的,对修改应该是关闭的)。在状态模式和策略模式中,Context对象对修改是关闭的,添加新的状态或策略,都不需要修改Context。
  • 正如状态模式中的Context会有初始状态一样,策略模式同样有默认策略。
  • 状态模式以不同的状态封装不同的行为,而策略模式以不同的策略封装不同的行为。
  • 它们都依赖子类去实现相关行为

两个模式的差别在于它们的”意图“不同:

状态模式帮助对象管理状态,我们将一群行为封装早状态对象中,context的行为随时可委托到那些状态中的一个.随着时间的流逝,当前状态在状态对象集合中游走改变,以反映context内部状态,因此,context的行为也会跟着改变。当要添加新的状态时,不需要修改原来代码添加新的状态类即可。 而策略模式允许Client选择不同的行为。通过封装一组相关算法,为Client提供运行时的灵活性。Client可以在运行时,选择任一算法,而不改变使用算法的Context。一些流行的策略模式的例子是写那些使用算法的代码,例如加密算法、压缩算法、排序算法。客户通常主动指定context所要组合的策略对象是哪一个.

 
 
 
 
posted on 2019-05-25 14:18  友帅老师  阅读(198)  评论(0)    收藏  举报