注意:如果不想浪费时间,请一定要点我。
在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。
对于这种情况,传统的解决方案是考虑到所有的状态,使用条件判断语句做状态判断,然后根据状态做出相应的行为。但是当状态很多时,这种方式会很复杂,而且增加新的状态时,要修改判断语句,违背了开闭原则,不利于扩展。方法过长,有很多的判断分支,意味着责任过大,面向对象的设计是希望做到代码责任的分解,这种方式还违背了单一职责。
比如日常生活中的灯开关,如果当前状态是亮,则开关就会关灯,否则就会开灯。
状态模式主要解决的就是当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到不同状态的一系列类中,可以把复杂的判断逻辑简单化。
定义
状态模式,当一个对象内在状态改变时允许改变其行为,对象看起来就像是修改了它的类。
状态模式的主要优点如下:
(1)将与特定状态相关的行为局部化,并将不同状态的行为分割开来,满足单一职责。
(2)状态模式将各种状态转移逻辑分布到子类之间,减少相互依赖。
(3)由于所有与状态相关的代码都存在于某个具体状态类中,所以通过定义新的子类可以容易的增加新的状态和转换,符合开闭原则
该模式的主要缺点如下:
(1)系统中类和对象的个随着状态的增多而增多。
(2)状态模式的结构和实现比较复杂,如果使用不当会使程序十分混乱。模式的结构和实现
当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为,就可以考虑使用状态模式。
该模式包括以下三种角色:
(1)环境角色:该类维护了一个当前状态,并将与状态相关的行为交给当前状态处理。
(2)抽象状态角色:定义了一个接口,该接口封装了与特定状态相关的行为。
(3)具体状态角色:实现抽象状态所对应的行为。
状态模式的结构图如下所示:
有些人会以为我粘贴的策略模式的类图,的确,策略模式和状态模式的类图是一毛一样的。
但是这两个模式的差别就在于它的意图。
以状态模式而言,我们将与特定状态相关的行为封装到状态对象中。随着某些特定的行为( 比如按灯开关 ),context 内部状态发生改变,context的行为也会发生改变,但是 context 的客户是不知道内部状态对象的改变。
但是对于策略模式而言,客户通常主动指定 context 内部的策略对象,现在,固然策略模式让我们具有弹性,能在运行时改变策略,但是对于某一个 context 对象来说,通常只有一个最适当的策略对象。
一般来说,我们将策略模式想成是除了继承之外的另一种弹性替代方案,如果你通过继承定义了一个类的行为,你将被这个行为困住,甚至修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。
而状态模式想成是不用在 context 中放置许多条件判断的替代方案。通过将行为包装到状态策略中。通过将行为包装到状态对象中,你可以在context内简单的改变状态对象来改变 context的行为
//上下文对象
class DeskLamp{
private Light light = new LightOffState();
public void setState(Light state) {
this.light = state;
}
public void clickSwitch() {
light.onClinck(this);
}
}
//抽象状态
abstract class Light{
public static final Light offState = new LightOffState();
public static final Light onState = new LightOnState();
public abstract void onClinck(DeskLamp dl) ;
}
//具体状态
class LightOffState extends Light{
@Override
public void onClinck(DeskLamp dl) {
System.out.println("灯亮");
dl.setState(onState);
}}
class LightOnState extends Light{
@Override
public void onClinck(DeskLamp dl) {
System.out.println("灯灭");
dl.setState(offState);
}}
}
客户端测试
DeskLamp dl = new DeskLamp();
dl.clickSwitch();
dl.clickSwitch();
dl.clickSwitch();