状态模式 State 行为型 设计模式(二十四)

状态模式 State
 
人有喜怒哀乐,海绵宝宝也会有不一样的时候,也会有不一样的心情~
问题:上图中,若是跟海绵宝宝开玩笑,那种状况最可能被打? 
 
看下面一个示例,演示了java中的多态特性
类A有方法action()
类B继承了A 覆盖了方法action()
类C继承了A 覆盖了方法action()
package state.example;
public class A {
    void action() {
        System.out.println("A.......");
    }
    public static void main(String[] args) {
        A a = new A();
        A b = new B();
        A c = new C();
        a.action();
        b.action();
        c.action();
        }
    }
    
class B extends A {
    @Override
    void action() {
        System.out.println("B.......");
        }
class C extends A {
    @Override
    void action() {
        System.out.println("C.......");
        }
}
image_5c1c71ca_1e79
类型都是A,可是却由于内部的具体的子类类型不同,结果不同
 
海绵宝宝在不一样心情状态下,对同一件事情的处理态度多是不一样的,生气的时候跟他开玩笑极可能会被打。
一样的类型A,因为具体类型状态的不一样,给出的响应是不一样的。
这就是状态, 不少事物都有不一样的状态,不一样的状态可能会有不一样的行为,并且,状态间是能够切换的

意图

容许一个对象在其内部状态改变时改变他的行为。对象看起来彷佛修改了他的类。
别名:状态对象(Objects for States)
 
其实就是 拥有状态属性,在不一样的状态下,呈现出不一样的行为,而且能够灵活的切换状态
状态: 一个对象的行为取决于内部一个或者多个动态变化的属性,这样的属性就叫作状态。
有状态的对象stateful这样的对象就叫作有状态的对象。

结构

不考虑Java语言的多态,如何达到“多态”的效果?
一种很天然的解决方案就是条件分支或者选择语句,好比
int state = 0;

if(0 == state){
              //...
}else if(1 == state){
              //...
}else if(2 == state){
              //...
}else{
              //...
}
int类型的变量state 就是“有状态的对象”,state的具体的值就是“状态”。
状态的切换依赖state变量的赋值,不一样行为的呈现借助于条件分支。
 
显然,若是业务逻辑复杂,将会有 繁琐复杂的分支判断
更有甚者,若是有多个状态共同决定行为,那么岂不是 多个状态的复杂组合了?
并且若是 新增长状态,那么就 须要修改代码,添加一个新的 else if(),扩展性很差,不符合开闭原则。
 
咱们更但愿的是可以达到“多态”的那种调用效果,方法调用与具体的行为解耦。
调用者不须要关注具体的类型,在方法执行时,会具备真实类型的行为。
至关于变形为:
int state = 0;
state.action();
 
看起来,看起来好像,看起来好像action()方法封装了相似下面的判断逻辑 (只是看起来,其实他只有他本身状态下的行为,不须要判断)
if(0 == state){
//...
}else if(1 == state){
//...
}else if(2 == state){
//...
}else{
//...
}
这不就是上面的多态示例么,若是A表示抽象的状态,B和C表示具体的某种状态
若是全部使用状态的地方,都使用静态类型A,就能够根据实际类型达到多态的效果了。
image_5c1c71ca_4747
 
因此说
状态模式的根本在于借助于OOP的多态机制,描述状态,就能够达到不一样行为的效果。
有了不一样类型的状态以后,还能够进一步经过一个中间类对他们进行管理,进而实现灵活的状态切换。
 
因此一种经常使用的状态模式结果以下
image_5c1c71ca_456f
 
抽象状态角色State
接口或者抽象类,定义状态的抽象含义,而且给出状态的行为接口,这个接口被外界经过环境类调用
能够持有Context的引用,经过Context完成状态切换
具体状态ConcreteState
实现了抽象状态的描述,给出本身的行为,每个子类型都表示一种具体的状态
环境类角色Context
又叫作上下文,经过环境类Context对状态进行管理
通常作法是将多种状态定义为他的静态属性,环境类中维护一个State,这是当前状态,常常提供切换状态的方法 

代码示例

抽象的State,定义状态行为
package state;
public interface State {
void handle();
}
具体的状态1 
package state;
public class ConcreateState1 implements State {
@Override
public void handle() {
System.out.println("state 1 do sth");
}
}
具体的状态2
package state;
public class ConcreateState2 implements State {
    @Override
    public void handle() {
        System.out.println("state 2 do sth");
    }
}
环境类,内部封装了两个状态
state为当前状态,初始时设置为状态1了
经过changeState方法进行切换
action()方法为封装调用state的handle方法
package state;
public class Context {
    private final State STATE1 = new ConcreateState1();
    private final State STATE2 = new ConcreateState2();
    private State state = STATE1;
    public void action() {
        state.handle();
    }
    public void changeState(int stateValue) {
        if (1 == stateValue) {
        state = STATE1;
        } else if (2 == stateValue) {
        state = STATE2;
        }
    }
}
image_5c1c71ca_7a31
 
上面的示例代码中,初始状态为状态1 ,action调用状态1,打印 state 1 do sth
状态切换后,调用状态2 
 
状态模式的核心在于引入抽象状态角色State,借助于多态,实现不一样的行为,
而后借助于环境类Context实现State的管理,以灵活切换状态。

状态的具体行为,状态切换的时机等等均可以根据实际状况灵活处理,尤为是什么时候切换状态,谁来负责切换状态。
示例中,在环境类Context中建立了静态的状态对象,你能够根据实际状况动态的建立对象。
若是状态频繁变化,那么事先建立全部状态更合适,若是状态设置后就不多变更,动态建立的形式或许更好。
状态也能够持有环境类的引用,能够得到更多的便利性。

另外的示例

若是仅仅是状态的提取,简化复杂的状态判断逻辑,借助于State角色就能够完成
若是须要状态的切换维护,或者要求状态的顺序等,总之对State的访问须要增长更多的业务逻辑时,那么就能够借助于Context对State进行管理
经过Context封装维护当前状态,也就是Context提供代理方法,代理对当前状态State的直接访问
这样就能够经过Context增长状态切换,顺序限制等更多的处理逻辑
 
好比下面逻辑,假设状态1,2,3,4,5,经过下面的逻辑,就能够控制状态的切换顺序,当前状态推导出下一个状态

void click(){ html

currentState.handle(); java

 

if(state == 1){ 编程

changeState(2); 设计模式

}else if(state == 2){ less

changeState(3); ide

} this

//..... spa

}设计

 
完整代码
package state.order;
public interface State {
void handle();
}
package state.order;
public class ConcreateState1 implements State {
@Override
public void handle() {
System.out.println("state 1 do sth");
}
}
package state.order;
public class ConcreateState2 implements State {
@Override
public void handle() {
System.out.println("state 2 do sth");
}
}
package state.order;
public class ConcreateState3 implements State {
@Override
public void handle() {
System.out.println("state 3 do sth");
}
}
环境Context包括三种状态,初始时当前状态为状态1
每次方法调用,都会按照顺序切换状态
package state.order;
public class Context {
    private final State STATE1 = new ConcreateState1();
    private final State STATE2 = new ConcreateState2();
    private final State STATE3 = new ConcreateState3();
    private State state = STATE1;
    public void action() {
        state.handle();
        if (state == STATE1) {
            setState(STATE2);
        } else if (state == STATE2) {
            setState(STATE3);
        } else if (state == STATE3) {
            setState(STATE1);
        }
    }
    void setState(State state) {
        this.state = state;
    }
}
image_5c1c71ca_4369
从上面的结果能够看得出来,无论你调用多少次方法,他都会完成两个任务,当前状态处理,而后切换状态
 
因此说,状态的切换时机,场景,不一样的方式将会有不少种不一样的变化形式。

总结

状态的本质很简单,就是多态的体现,Java是纯粹的面向对象语言,自然的具备多态的特性
因此,在 Java中,经过类的层次结构,就能够直接造成一个“状态”体系,顶级父类定义状态的访问接口,具体的实现类定义自身的状态行为。
 
不过状态模式是多态的更进一步的抽象和概念提高,而不是单纯的利用多态的特性。
状态模式经过引入环境类Context,能够对状态进行管理,提供对状态切换的支持
经过Context 能够实现对当前状态的“ 代理”,增长更多的处理逻辑
 
若是对象的行为依赖他的状态,状态的改变致使行为的变化,并且,代码中含有大量的与对象有关的条件语句的处理逻辑
就能够考虑使用状态模式
 
状态模式的重点就是将对象的状态切换以及不一样的状态具备不一样的行为与客户端进行了解耦
不一样的状态类型组成了状态的体系结构,Context封装了切换逻辑 ,能够作到状态的切换对客户端透明
将全部的状态切换行为封装到环境中,而不是分散在业务逻辑方法中,不论是维护性仍是扩展性的角度都大大提升。
并且使用起来也会很是灵活,Context也封装代理了当前的状态,可让客户端对状态一无所知,如同一个代理人
客户端再也不须要分状况讨论了,你就告诉Context须要作什么就行了,他本身内部知道状态,也能够切换
 
状态模式必然会致使大量的状态类出现,若是状态不多,你可能就不会用状态模式了
并且运行时的对象个数也会增长不少
 
若是增长新的状态类,对于那些涉及到状态切换的代码必然须要修改,不符合开闭原则
可是对于不修改状态的代码,由于经过抽象角色State,面向抽象编程,因此并不须要修改
修改状态的代码是面向具体类型,面向细节了,因此逃不掉了
 
状态模式,就是多态以及多态的切换
开篇示例中的多态是Java语言层面上的多态,不一样类型呈现不一样的行为。
状态模式是设计思惟业务逻辑上的“多态”,根据状态对象不一样的产生不一样状态下的行为,不是特指OOP中的多态。
状态模式就是将“不一样条件下的行为”进行封装,封装后的对象就是状态对象
 
相关文章
相关标签/搜索