前言
设计模式是指导一个程序猿以更好的姿态处理一些问题,而不再像刚学编程的我们,只会使用if-else分支语句,或是使用硬干的骚操作完成需求。不使用设计模式,一来是代码逻辑会越来越晦涩难懂(到了某天你会发现自己也没办法看清楚所有逻辑),二来是代码维护成本越来越高(你的加班时间会越来越长),三来是可以装bility。基于这么多的好处,小盆友和大家一起讨论和分享下设计模式,让自己不再是一个坐在电脑前敲代码的码农。
讲个故事——“古怪的电梯”
老样子,讲设计模式前,先来个场景(讲不出故事了。。只能尬聊了😩)
设想一下,用程序来表达一个电梯的普通运转流程:关门->运行->停止->开门。这是最为正常的使用流程,乘坐电梯的人进入后,电梯关门,然后电梯运行将人送至目的层数,停止,然后开门。
但是有个问题就是,这只是一般流程,乘坐电梯的人也可以玩弄电梯:开门->关门->再开门->再关门;或是运行->停止->运行->开门等等等等。
总之,乘坐电梯的人操作流程有很多,也就是这四种状态是可以相互转换的。但是,细想一下,也不是很周全,因为正常情况下,开门的状态下是不能运行的,运行状态下不能开门,也就是说,四种状态中,有些状态不能转换。我们就这四种状态的转换画一个表格:
开门open | 运行run | 停止stop | 关门close | |
---|---|---|---|---|
正开着门 | ✖️ | ✖️ | ✖️ | ☑️ |
运行中 | ✖️ | ✖️ | ☑️ | ✖️ |
停止 | ☑️ | ☑️ | ✖️ | ✖️ |
已经关门 | ☑️ | ☑️ | ☑️ | ✖️ |
看到这张表格,想必大家此时心中已经有千万种实现方式,有些小伙伴可能会用到大量的 switch-case 或 if-else的分支语句进行区分状态的转换流程,这里就不给出此类代码(请允许我的懒惰🤔️),只是略微吐槽一下,如果用了大量的分支语句,弊端是显而易见的:
- 扩展性差,当增加一种状态,需要改动的地方多;
- 逻辑耦合,不易维护;
- 不能装逼!!!!
有经验的小伙伴,很快会想到使用状态模式。在这种行为随状态改变而改变的情况,可以考虑使用“状态模式”。先给出状态模式的类图
可以看出,状态主要由三部分组成:
- State:状态的抽象类
- ConcreteState:状态的具体实现类,主要负责实现当前状态的逻辑实现,还有过度到其他状态的逻辑控制(如果不能过度就空实现)
- Context:环境角色,负责管理状态
接下来,我们对上面的场景进行映射
可以看到,映射的类图其实没有多大的变动,主要的状态转变逻辑会在各自的具体状态实现类中,例如:OpenState转变为CloseState,是由OpenState来维护(将Context的当前状态进行切换),并且打开的逻辑是由OpenState来实现。而Context用于管理调用各自具体的状态。可能现在你有些凌乱,我们可以先进入编码时刻,结合具体代码来理解这段话。
编码时刻
先看看抽象状态类,主要是规范所有具体状态,而且持有一个状态管理
public abstract class LiftState {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
public abstract void open();
public abstract void close();
public abstract void run();
public abstract void stop();
}
接下来是四个具体的状态
public class OpenState extends LiftState {
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void close() {
super.context.setLiftState(Context.CLOSE_STATE);
super.context.close();
}
@Override
public void run() { //空实现,因为当前状态无法转到run }
@Override
public void stop() { //空实现,因为当前状态无法转到stop }
}
public class RunState extends LiftState {
@Override
public void open() { //空实现,因为当前状态无法转到open}
@Override
public void close() { //空实现,因为当前状态无法转到close }
@Override
public void run() {
System.out.println("电梯上下运行...");
}
@Override
public void stop() {
super.context.setLiftState(Context.STOP_STATE);
super.context.stop();
}
}
public class StopState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.OPEN_STATE);
super.context.open();
}
@Override
public void close() { //空实现,因为当前状态无法转到close }
@Override
public void run() {
super.context.setLiftState(Context.RUN_STATE);
super.context.run();
}
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
public class CloseState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.OPEN_STATE);
super.context.open();
}
@Override
public void close() {
System.out.println("电梯门关闭...");
}
@Override
public void run() {
super.context.setLiftState(Context.RUN_STATE);
super.context.run();
}
@Override
public void stop() {
super.context.setLiftState(Context.STOP_STATE);
super.context.stop();
}
}
状态管理类
public class Context {
public final static OpenState OPEN_STATE = new OpenState();
public final static CloseState CLOSE_STATE = new CloseState();
public final static RunState RUN_STATE = new RunState();
public final static StopState STOP_STATE = new StopState();
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
this.liftState.setContext(this);
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
最后是调用代码:
public class Main {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(Context.CLOSE_STATE);
context.open();
context.close();
context.run();
context.stop();
}
}
运行结果
总结
在这里使用状态模式的好处是,让各自的状态逻辑分成不同的类,维护起来清晰。而且扩展方便,只需要添加一个继承LiftState的类,在Context中增加这个状态即可使用。但是,这里也就暴露了一个状态模式的缺点,当状态较多时,具体的状态类会非常多。所以建议状态最好不超过5个,但只是建议,毕竟程序是死的,人是活的。希望这篇文章,能给你一些启发,减少一些些分支语句,让代码优雅起来。
网友评论