之前讲到的命令模式是将具体操作分离出来,而这里的状态模式也是相同的思路,将每种状态分离出来,建立状态类。状态模式的描述有很多种,比如状态模式封装基于状态的行为,并将行为委托到当前状态;又比如还有状态模式定义了对象的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新等等。无论状态模式的描述怎么变化,都是讲述了将对象的多种状态独立开来,即一个类代表一种状态。
1.适用性与优缺点
1.适用性:
a.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
b.一个操作中含有庞大的多分支的条件语句,且这些分治依赖于该对象的状态,这个状态通常用一个或多个枚举常量表示,通常,有多个操作包含这一相同的条件结构。状态模式将每一个条件分支放入一个独立类中。
2.优点:
a.状态模式将与特定状态相关的行为局部化,并且将不同状态行为分离出来。
b.所有状态相关的代码都存在某个状态中,所以通过定义新的子类很容易地增加新的状态和转换。
c.状态模式通过各种状态转移,逻辑分布到state的子类之间,来减少相互间的依赖。
3.缺点:
系统中有较多的状态类。
2.示例讲解
我们讲一个天气的例子,我们都知道天气有很多种状态,每种状态都有不同的现象。如果我们放一个类中,则会产生很多逻辑判断,那么这个类过于冗杂,一旦更改,工作量会比较大。所以,我们将状态分离出来,做到每个状态一个类的原则。首先,我们还是遵循针对接口编程,因此建立一个状态接口。
package statepattern;
public interface Weather {
String getWeather();
}
这里举的例子简单点,所以在状态接口中只有一个方法,如果功能比较复杂,可以添加多个方法。以下是对状态接口的实现,有下雨,天晴,下雪等状态:
package statepattern;
public class Rain implements Weather {
@Override
public String getWeather() {
// TODO Auto-generated method stub
return "下雨";
}
}
package statepattern;
public class Sunshine implements Weather {
@Override
public String getWeather() {
// TODO Auto-generated method stub
return "出太阳啦";
}
}
package statepattern;
public class Snow implements Weather {
@Override
public String getWeather() {
// TODO Auto-generated method stub
return "下雪啦";
}
}
以上是每种状态对状态接口的实现,即实现了一个类代表一种天气状态,接下来这个类,则是为了对当前状态的操作,负责实例化具体状态,并且调用相应方法,表示当前状态的现象。
package statepattern;
public class Context {
private Weather weather;
public void setWeather(Weather weather) {
this.weather = weather;
}
public Weather getWeather() {
return this.weather;
}
public String weatherMessage() {
return weather.getWeather();
}
}
以上Context类是一个简单的JavaBean,在运行时创建对象,体现了动态性,如果当前状态改变了,只需要通过Context类的setter函数进行状态动态地更换即可,并且可直接调用相应状态的函数。这样相当于减少了各个状态之间的依赖关系。接下来对示例进行测试:
package statepattern;
public class Test{
public static void main(String[] args) {
Context ctx1 = new Context();
ctx1.setWeather(new Sunshine());
System.out.println(ctx1.weatherMessage());
System.out.println("===============");
ctx1.setWeather(new Rain());
System.out.println(ctx1.weatherMessage());
System.out.println("===============");
ctx1.setWeather(new Snow());
System.out.println(ctx1.weatherMessage());
}
}
运行程序,结果如下:
出太阳啦
===============
下雨
===============
下雪啦
由上面示例可以看出,通过各个具体的状态类以及Context类,实现了将具体类分离出来,减少了类转换的依赖关系。
3.与策略模式的对比
其实状态模式与
策略模式十分相像,状态模式是将每种具体状态分离出来,而策略模式是将每种具体行为分离出来。两者还是有一些不同之处的。
1.状态模式是将一群行为封装在状态的对象中,Context的行为随时可委托到那些状态对象中的一个,当前状态在状态对象集合中游走改变,以反映出Context内部状态,内部决定了状态。而策略模式中对于Context来说,通常只有一个最合适的策略对象,相当于它是具体行为的组合而形成的策略对象。比如天气有好几种状态可以切换,而鸭子的行为方式、叫声是固定的,所以天气用状态模式可以表示多状态转换,而鸭子用策略模式表示行为和叫声只有一种最合适的策略模式。
2.一般策略模式是除继承之外的一种弹性替代方案,通过组合不同的对象来改变行为;而状态模式是将行为包装进状态对象中,可以通过在Context内简单地改变状态对象来改变Context的行为,替换了有许多条件判断的方案。