定义
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
结构图

-
从图上看,Context和State之间是“聚合”关系,但是实际上并没有“群体”和“个体”的意思。Context只保留一个“当前的State”,State是Context的成员变量。只是这个State是根据条件不同,不断地在各个ConcreteSate之间变换。
或者简单地说,Context中并没有一个State的数组。所以,这里用“聚合”关系并不是非常确切,感觉上直接用“关联”更加简单直接。 -
Context只有1个,里面保存了决定状态变换的各种条件变量。判断逻辑在各个ConcreteState中的Handle(Context)方法中。这个Context出现在State的方法参数中。所以State依赖Context。
使用场景
-
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
-
状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。====== 也就是将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。===== 这样做的目的就是为了消除庞大的条件分支语句。
-
状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。
-
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
加班写程序的例子
- 结构图

这里的工作就是
Context
,这里的写程序就是Request()
方法。其实Request()
方法就是直接调用state.Handle(Context)
方法的,所以这里把Request()
和Handle()
命名成一样,(都叫writeProgram()
),是合理的。
- 大神也会犯困(书中代码的问题):
- 从9点顺序测试到22点,输出结果没有问题;
- 如果顺序打乱,比如先测14点,再测10点,结果就不对了。原因是判断条件不充分:比如,下午的条件应该是
if ((work.hour >= 13) && (work.hour < 17))
;仅仅是if (work.hour < 17)
不够的。 - 有断档,比如,先测22点,以后不论怎么测,都是“睡觉时间”。原因是
SleepingState
没有下一步,RestState
也有同样的问题。
- 测试界面:

- Work类(Context);State基类;在这里,Request和Handle都用了同样的名字,都叫writeProgram。
Work类的初始化状态设置非常重要。这是状态机的入口点,不然的话根本启动不了。
class Work {
public State current;
public Integer hour;
public Integer minute;
public Boolean finish;
public Work() {
// 初始为上午工作状态;如果没有初始状态,就无法启动流转
current = new ForenoonState();
}
// Context中的request(),这里和state中的handle()方法用了同样的名字
public void writeProgram() {
current.writeProgram(this);
}
}
abstract class State {
// writeProgram的内容记录在这个字段中
protected String information;
// 写程序的各种状态;这个就是UML图中的handle()方法
public abstract void writeProgram(Work work);
// 用于显示到界面上的方法
public String show() {
return information;
}
}
- 各种具体的State类(),ConcreteState
- 条件要充分,比如上午,就应该是
if ((work.hour >= 9) && (work.hour < 12))
- 基本结构基本上是
if...else...
的样式;要么命中,要么提交下一个节点继续判断。这样周而复始,逻辑闭环。
class ForenoonState extends State {
// 上午 9~12点
@Override
public void writeProgram(Work work) {
if ((work.hour >= 9) && (work.hour < 12)) {
information = "当前时间:" + work.hour + "点" + work.minute + "分"
+ " ===> 上午工作,精神百倍。";
} else {
work.current = new NoonState();
work.writeProgram();
}
}
}
class NoonState extends State {
// 中午 12~13点
@Override
public void writeProgram(Work work) {
if ((work.hour >= 12) && (work.hour < 13)) {
information = "当前时间:" + work.hour + "点" + work.minute + "分"
+ " ===> 饿了,午饭;犯困,午休。";
} else {
work.current = new AfternoonState();
work.writeProgram();
}
}
}
class AfternoonState extends State {
// 下午 13~17点
@Override
public void writeProgram(Work work) {
if ((work.hour >= 13) && (work.hour < 17)) {
information = "当前时间:" + work.hour + "点" + work.minute + "分"
+ " ===> 下午状态还不错,继续努力。";
} else {
work.current = new EveningState();
work.writeProgram();
}
}
}
class EveningState extends State {
// 晚间时间,(有可能的加班时间),17 ~ 21
@Override
public void writeProgram(Work work) {
if ((work.hour >= 17) && (work.hour < 21)) {
if (!work.finish) {
information = "当前时间:" + work.hour + "点" + work.minute + "分"
+ " ===> 加班哦,疲累之极。";
} else {
work.current = new RestState();
work.writeProgram();
}
} else {
work.current = new SleepingState();
work.writeProgram();
}
}
}
class RestState extends State {
// 下班时间;工作完成,晚上5~9点之间
@Override
public void writeProgram(Work work) {
if ((work.hour >= 17) && (work.hour < 21) && work.finish) {
information = "当前时间:" + work.hour + "点" + work.hour + "分"
+ " ===> 下班回家了";
} else {
work.current = new SleepingState();
work.writeProgram();
}
}
}
class SleepingState extends State {
// 21~24~9 晚上9点之后,第二天9点之前为睡眠时间
@Override
public void writeProgram(Work work) {
if ((work.hour >= 21) || (work.hour < 9)) {
information = "当前时间:" + work.hour + "点" + work.hour + "分"
+ " ===> 不行了,睡着了";
} else {
work.current = new ForenoonState();
work.writeProgram();
}
}
}
- 客户端程序:
public class StateActivity extends AppCompatActivity {
public static void launch(Context context) {
if (null != context) {
Intent intent = new Intent();
intent.setClass(context, StateActivity.class);
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}
TimePicker timePicker;
Switch finishSwitch;
TextView stateTextView;
Work testWork;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_state);
setTitle("状态模式");
finishSwitch = findViewById(R.id.switchFinish);
timePicker = findViewById(R.id.timepicker);
stateTextView = findViewById(R.id.textViewState);
testWork = new Work();
// 设置时间选择器状态
timePicker.setIs24HourView(true);
timePicker.setHour(9);
timePicker.setMinute(0);
// 设置点击事件不弹键盘
timePicker.setDescendantFocusability(TimePicker.FOCUS_BLOCK_DESCENDANTS);
}
public void onButtonCheckClick(View view) {
testWork.finish = finishSwitch.isChecked();
testWork.hour = timePicker.getHour();
testWork.minute = timePicker.getMinute();
testWork.writeProgram();
stateTextView.setText(testWork.current.show());
}
}
Demo地址
https://gitee.com/zhangxusong888/Android/tree/master/design_pattern
网友评论