我们每个人都乘过电梯,电梯的动作:开门、关门、运行、停止。
现在我们用程序来实现一个电梯的动作,先看类图设计,如图所示
现在看一下代码
public interface ILift {
//开启电梯
public void open();
//关闭电梯
public void close();
//能运行,能上能下
public void run();
//电梯还要能停下来i
public void stop();
}
public class Lift implements ILift {
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void close() {
System.out.println("电梯门关闭...");
}
@Override
public void run() {
System.out.println("电梯上下运行起来...");
}
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
public class Client {
public static void main(String[] args) {
ILift lift = new Lift();
//首先是电梯门打开,人进去
lift.open();
//r然后电梯门关闭
lift.close();
//电梯开始运行起来,向上或者向下
lift.run();
//最后到达目的地,电梯停下来
lift.stop();
}
}
/**Output
电梯门开启...
电梯门关闭...
电梯上下运行起来...
电梯停止了...
*/
代码非常简单,但是这个程序是有问题的。
————————————————————
1、电梯门可以打开,但不是随时都可以打开,电梯运行的时候不能突然开门。
2、电梯也不会出现停止了但是不开门的情况。
————————————————————
所以我们可以看出,电梯的这四个动作的执行都有前置条件,具体点说就是在特定状态下才能做做特定的事。
接下来我们分析一下电梯有哪些特定的状态
敞门状态
按了电梯上下按钮,电梯门开,这中间大概有10秒的时间,那就是敞门状态。在这个状态下电梯只能做的动作就是关门动作。
闭门状态
电梯门关闭了,在这个状态下,可以进行的动作是:开门(我不想坐电梯了)、停止(忘记按路层号了)、运行。
—————————————————————————————
在接口中添加了标识变量,类LIft中的open、close等方法带有了逻辑判断。
—————————————————————————————
代码如下:
public interface ILift {
//电梯的4个状态
final static int OPENING_STATE = 1;
final static int CLOSING_STATE = 2;
final static int RUNNING_STATE = 3;
final static int STOPPING_STATE = 4;
//开启电梯
public void open();
//关闭电梯
public void close();
//能运行,能上能下
public void run();
//电梯还要能停下来i
public void stop();
}
public class Lift implements ILift {
private int state;
public void setState(int state) {
this.state = state;
}
@Override
public void open() {
//电梯在什么状态下才能开启
switch (this.state) {
case OPENING_STATE:
break;
case CLOSING_STATE:
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this.openWithoutLogic();
this.setState(OPENING_STATE);
break;
}
}
@Override
public void close() {
//电梯在什么状态下才能关闭
switch (this.state) {
case OPENING_STATE:
this.closeWithoutLogic();
this.setState(CLOSING_STATE);
break;
case CLOSING_STATE:
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
break;
}
}
@Override
public void run() {
switch (this.state) {
case OPENING_STATE:
break;
case CLOSING_STATE:
this.runWithoutLogic();
this.setState(RUNNING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this.runWithoutLogic();
this.setState(RUNNING_STATE);
break;
}
}
@Override
public void stop() {
switch (this.state) {
case OPENING_STATE:
break;
case CLOSING_STATE:
this.stopWithoutLogic();
this.setState(STOPPING_STATE);
break;
case RUNNING_STATE:
this.runWithoutLogic();
this.setState(STOPPING_STATE);
break;
case STOPPING_STATE:
break;
}
}
public void closeWithoutLogic(){
System.out.println("电梯门关闭...");
}
public void openWithoutLogic(){
System.out.println("电梯门开启...");
}
public void runWithoutLogic(){
System.out.println("电梯上下运行起来...");
}
public void stopWithoutLogic(){
System.out.println("电梯停止了...");
}
}
public class Client {
public static void main(String[] args) {
ILift lift = new Lift();
lift.setState(OPENING_STATE);
//首先是电梯门打开,人进去
lift.open();
//r然后电梯门关闭
lift.close();
//电梯开始运行起来,向上或者向下
lift.run();
//最后到达目的地,电梯停下来
lift.stop();
}
}
/**Output
电梯门开启...
电梯门关闭...
电梯上下运行起来...
电梯停止了...
*/
好了,改过的代码已经实现了简单的逻辑控制。但是它好像还是有不少问题。
————————————————————————————
电梯实现类Lift有点长
-长的原因是因为我们在程序中使用了大量的switch…case这样的判断(if…else也是一样)
拓展类非常差,电梯还有两个状态,通电状态,断电状态,你要是在程序增加这两个方法,你看看Open()、Close()、Run()、Stop()这四个方法都要增加判断条件,也就是说在switch判断体中还要增加case项、这与开闭原则相违背。
—————————————————————————————
我们已经发现程序中有以上问题,我们怎么来修改呢?
刚刚我们是从电梯的方法以及这些方法执行的条件去分析,现在我们换个角度来看问题。我们来想,电梯在具有这些状态的时候能够做什么实行,也就是说电梯在处于某个具体状态时,我们来思考 这个状态是由什么动作出发触发的而产生的,以及在这个状态下电梯还能做什么事情。例如,电器在停止状态时,我们来思考两个问题:
—————————————————————————————
- 停止状态是怎么来的,那当然是由于电梯执行了stop方法而来的。
-
- 在停止状态下,电梯还能做什么动作?继续运行?开门?当然都可以了
—————————————————————————————
我们再来分析其他三个状态,也都是一样的结果,我们只要实现电梯在一个状态下的两个任务模型就可以了:这个状态是如何产生的,以及在这个状态下还能做什么其他动作(也就是这个状态怎么过度到其他状态),既然我们以状态作为参考模型,那我们先定义电梯的状态接口,类图如下
代码如下
public abstract class LiftState {
//定义一个环境角色,也就是封装状态的变化引起的功能变化
protected static Context context = new 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 Context {
//定义出所有的电梯状态
public final static OpenningState openningState =
new OpenningState();
public final static ClosingState closingState =
new ClosingState();
public final static RunningState runningState =
new RunningState();
public final static StoppingState stoppingState =
new StoppingState();
//定义一个当前的电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
// this.liftState.stop();
}
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 ClosingState extends LiftState {
//电梯门关闭,这是关闭状态要实现的动作
@Override
public void close() {
System.out.println("电梯门关闭...");
}
//电梯门关了再打开
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.getLiftState().open();
}
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.getLiftState().run();
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.getLiftState().stop();
}
}
public class OpenningState extends LiftState {
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void close() {
//状态修改
super.context.setLiftState(Context.closingState);
super.context.getLiftState().close();
}
@Override
public void run() {
}
@Override
public void stop() {
}
}
public class RunningState extends LiftState {
@Override
public void open() {
}
@Override
public void close() {
}
@Override
public void run() {
System.out.println("电梯门上下运行...");
}
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.getLiftState().stop();
}
}
public class StoppingState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.getLiftState().open();
}
@Override
public void close() {
}
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.getLiftState().run();
}
//电梯停止是怎么发生的
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
把逻辑放到没一个状态内,然后用context整合,做到添加状态的时候也不需要修改原来的代码。
状态模式的定义:当一个对象内在状态改变时允许其改变行为,这个对象 看起来像改变了其类。
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
状态模式的角色主要有三个:抽象状态角色,具体状态 角色,环境角色
状态模式的优点:
状态模式的缺点:
- 类膨胀
状态模式的使用场景
注意事项