状态模式介绍
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来好像是改变了其类。
现对该模式进行分析:该模式是将一个拥有多个状态的对象进行了拆分,将对象和状态的判断、表示进行了分离,比如根据一个人的年龄来判断其所处的阶段,(孩童阶段、青年阶段和年老阶段),如果我们使用通常的方法来实现根据年龄来判断该人所处阶段则需要多个选择分支来判断其所处的阶段;使用状态模式则不同,在状态模式中我们将每一个阶段作为一个具体的阶段类,然后根据不同的年龄使用相应的阶段类即可。
状态模式参与角色
角色 | 功能 |
---|---|
拥有多个状态的角色(也称为环境角色) | 针对不同的状态做出不同的行为,并将与状态相关的操作委托给状态类实现 |
抽象状态角色 | 内部含有所有具体状态角色的公共方法 |
具体状态角色 | 实现了环境角色中每一个状态所对应的行为 |
状态模式UML
案例
根据人的年龄来判断其所处的阶段,阶段划分有:孩童阶段(0-18)、青年阶段(19-40)、中年阶段(41-60)和老年阶段(61-100)。使用状态模式来完成这个功能。
案例分析
假设我们不使用状态模式来实现,我们则只需要使用四个分支结构就可以完成该功能,这是比较简单的,但是在我们使用普通方法实现后如果我们需要增加一个婴儿阶段那么我们需要在该类的源码上进行修改,在实际开发中这是比较危险的,因为对源码的修改可能会导致各种问题,所以使用普通方法将使得代码的可拓展性、可维护性比较差,同时没有实现“单一职责原则”。
话不多说现在我们来分析如何使用状态模式来实现该功能:
- 环境角色:定义一个Person类,该类中拥有年龄属性成员变量、设置年龄的成员方法和一个调用状态类角色的方法。
- 抽象状态角色:定义一个AbstractState类,该类拥有一个handle抽象方法,参数是Person类的引用。
- 具体状态角色:
(1)孩童阶段:定义一个ChildState类,继承了AbstractState,重写了handle方法
(2)青年阶段:定义一个YouthState类,同样继承了AbstractStae。
(3)中年阶段:定义一个MiddleageState类,继承了AbstractStae。
(4)老年阶段:定义一个OldageState类,继承了AbstractStae。
这样我们就实现了使用状态模式完成这个案例的整体规划,下面我将实现代码给大家。
案例实现
环境角色代码
package State.pattern;
/**
* @Introduction 该类是环境角色,将对不同状态的判断和对应状态阶段的行为委托给具体状态类实现
*/
public class Person {
private int age=0; //初始化年龄为0
private AbstractState state;
//设置年龄,在设置年龄之后,根据年龄获取其所处的年龄段
public void setAge(int ages) {
this.age=ages;
state=this.getState();
state.handle(this);
}
//获取年龄
public int getAge() {
return this.age;
}
//调用具体状态类
private AbstractState getState() {
return new ChildState();
}
}
抽象状态角色代码
package State.pattern;
/**、
* @Inroduction 该类是抽象状态角色,内部包含了所有具体状态角色的公共方法
*/
public abstract class AbstractState {
public abstract void handle(Person peron);
}
具体状态角色
(1)孩童阶段
package State.pattern;
/**
* @Introduction 该类是一个具体状态角色,继承了AbstractState类,实现了对应孩童状态的对应行为
*/
public class ChildState extends AbstractState{
//重写handle方法
@Override
public void handle(Person person) {
//对孩童阶段所作出的行为
if(person.getAge()>=0 && person.getAge()<=18)
System.out.println("您当前处于孩童阶段");
else {
AbstractState state=new YouthState();
state.handle(person);
}
}
}
(2)青年阶段
package State.pattern;
/**
* @Introduction 该类是一个具体状态类,继承了AbstractState类,实现了青年阶段的对应行为
*
*/
public class YouthState extends AbstractState{
//重写handle方法
@Override
public void handle(Person person) {
//对孩童阶段所作出的行为
if(person.getAge()>=19 && person.getAge()<=40)
System.out.println("您当前处于青年阶段");
else {
AbstractState state=new MiddleageState();
state.handle(person);
}
}
}
(3)中年阶段
package State.pattern;
/**
* @Introduction 该类是一个具体状态类,继承了AbstractState类,实现了中年阶段的对应行为
*
*/
public class MiddleageState extends AbstractState{
//重写handle方法
@Override
public void handle(Person person) {
//对孩童阶段所作出的行为
if(person.getAge()>=41 && person.getAge()<=60)
System.out.println("您当前处于中年阶段");
else {
AbstractState state=new OldageState();
state.handle(person);
}
}
}
(4)老年阶段
package State.pattern;
/**
* @Introduction 该类是一个具体状态类,继承了AbstractState类,实现了老年阶段的对应行为
*
*/
public class OldageState extends AbstractState{
//重写handle方法
@Override
public void handle(Person person) {
//对孩童阶段所作出的行为
if(person.getAge()>=61 && person.getAge()<=100)
System.out.println("您当前处于老年阶段");
}
}
用户界面模拟代码
package State.pattern;
/**
* @Introduction 该类模拟用户界面
*/
public class Main {
public static void main(String[] args) {
Person person=new Person();
person.setAge(10);
person.setAge(20);
person.setAge(60);
person.setAge(90);
}
}
运行结果
状态模式优缺点以及应用场景
优点:
(1)将一个对象的所有状态对应的行为分别放在不同的具体状态类中完成,实现了单一职责原则。
(2)增加了程序的可拓展性,如果我们需要拓展一个新的状态我们只需要增加一个新的状态类角色,并且将其与其他状态角色相互关联即可。
(3)提高了程序的可维护性
缺点:
(1)没有满足开放-封闭原则,如果我们增加一个新的状态类角色,需要设计到对其他具体状态类角色的修改(需要将新状态类角色与其他具体状态类角色相互联系)
(2)如果一个对象的状态过多会造成程序复杂度提升,代码难以理解(状态之间存在相互调用)
(3)使得系统中类过多
应用场景:
- 当一个对象的行为取决于其对应的状态。
- 一个对象中包含很多分支,而且这些分支的执行取决于一个状态。