“状态变化”模式
在组件构建过程中,某些对象的状态经常面临变化,通常要使用许多 if else 语句来判断对象的状态到底如何,然后才进行相对应的处理,这就使得代码不够优雅,如何对这些变化进行有效的管理?同时又维护高层模块的稳定?状态模式为这一问题提供了一种解决方案。
典型的模式:State,Memento
模式定义
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了它的类。
2018年软件设计师下半年下午题正好有个例子可以帮助理解。
会员的等级根据其里程数来变化
其中,
- FrequentFly(Context上下文)定义客户感兴趣的接口;维护一个CState实例,这个实例定义当前的状态。
- CState(状态)定义一个接口以封装与Context的一个特定状态相关的行为(travel)。
- CSliver,CGold,CBasic(具体状态子类),每个实现与Context的一个状态相关的行为,即travel()。
先创建出 FrequentFlyContext 与 CState
package com.zlfan.state;
public abstract class CState {
public int flyMiles; //里程数
public abstract double travel(double miles,FrequentFlyerContext context);
}
package com.zlfan.state;
public class FrequentFlyerContext {
CState state;
double flyMiles;
public FrequentFlyerContext(){
state = new CNoCustomer();
flyMiles = 0;
}
public void setState(CState state){
this.state = state;
}
public void travel(double miles){
double bonusMiles = state.travel(miles, this);
flyMiles = flyMiles + bonusMiles;
}
}
CState的4个子类,普卡会员,金卡会员,银卡会员,非会员
package com.zlfan.state;
/*
* 普卡会员
*/
public class CBasic extends CState{
@Override
public double travel(double miles, FrequentFlyerContext context) {
if(context.flyMiles>=25000 && context.flyMiles<50000)
context.setState(new CSliver());
if(context.flyMiles>=50000)
context.setState(new CGold());
return miles;
}
}
package com.zlfan.state;
/*
* 金卡会员
*/
public class CGold extends CState{
@Override
public double travel(double miles, FrequentFlyerContext context) {
if(context.flyMiles>=25000 && context.flyMiles<50000)
context.setState(new CSliver());
if(context.flyMiles<=25000)
context.setState(new CBasic());
return miles +0.5*miles;
}
}
package com.zlfan.state;
/*
* 银卡会员
*/
public class CSliver extends CState{
@Override
public double travel(double miles, FrequentFlyerContext context) {
if(context.flyMiles>50000)
context.setState(new CGold());
if(context.flyMiles<=25000)
context.setState(new CBasic());
return miles + 0.25*miles;
}
}
package com.zlfan.state;
/*
* 非会员用户
*/
public class CNoCustomer extends CState{
@Override
public double travel(double miles, FrequentFlyerContext context) {
System.out.println("您的里程数不会积累");
return miles;
}
}
对象如果改变状态,只要在FrequentFlyerContext类中设置当前的状态,就可以处理当前状态对应的操作travel(),无须再去判断状态情况,即上面的用户四个状态,直接设置就好了;如果不采用状态设计模式,我们就要为每一种状态(非会员,普卡,金卡,银卡)都要进行判断,这就是前面说的,代码有许多 if else 语句,不够优雅。
适用性
1.一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这是的开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。
优缺点:
优点:结构清晰,符合开闭原则
缺点:使用大量的状态类
总结
- State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象切换状态时,切换相应对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
- 为不同的状态引入不同的对象使得状态转变变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的。
- 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。