前言:

最近研究了一下有限状态机,感觉挺有意思的,这里用Java语言实现了一个示例。前人珠玉在前,曾有前辈按照电梯做了讲解,我按照自己的理解重新实现了一下。以上是背景。

1、电梯运行状态

首先,梳理电梯运行时的状态,共计6个状态,同时梳理状态转换条件。见下图。

java中状态机案例 java 状态机实现_java电梯fsm图

梳理好电梯运行状态后,开始为各个状态进行编码。基于接口编程的原则设计运行状态公共接口,通过上图分析发现电梯运行状态都只有一个单一动作,因此接口如下:

package statusMachine3;
public interface IElevatorState {
/**
* 运行
* @param aElevator 电梯实体
*/
void run(IElevator aElevator);
}

wait状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public class WaitState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
System.out.println(String.format("电梯【%s】:等待!当前楼层:%d",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.WAIT);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

open状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public final class OpenState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
System.out.println(String.format("电梯【%s】:在第%d层打开了电梯门!",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.OPEN);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

close状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public class CloseState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
System.out.println(String.format("电梯【%s】:在%d层关闭了电梯门!",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.CLOSE);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

up状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public class UpState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
aElevator.refreshFloor(ElevatorState.UP);
System.out.println(String.format("电梯【%s】:上行!当前楼层:%d",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.UP);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

down状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public class DownState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
aElevator.refreshFloor(ElevatorState.DOWN);
System.out.println(String.format("电梯【%s】:下行!当前楼层:%d",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.DOWN);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

reset状态对应的实现类:

package statusMachine3.elevatorState;
import statusMachine3.ElevatorState;
import statusMachine3.IElevator;
import statusMachine3.IElevatorState;
public class ResetState implements IElevatorState {
@Override
public void run(IElevator aElevator) {
aElevator.refreshFloor(ElevatorState.RESET);
System.out.println(String.format("电梯【%s】:复位!当前楼层:%d",aElevator.getElevatorBrand(),aElevator.getCurrentFloor()));
aElevator.transitionState(ElevatorState.RESET);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

电梯运行状态对应的枚举:

package statusMachine3;
public enum ElevatorState {
OPEN,CLOSE,UP,DOWN,WAIT,RESET;
}

2、电梯实体类

功能分析:

1.实现电梯运行功能

2.允许用户设置电梯去往的楼层

3.转换当前电梯状态

4.刷新电梯所在位置

因此,设计接口如下:

package statusMachine3;

public interface IElevator {
/**
* 运行
*/
void run();
/**
* 使用电梯
* @param floor 目标楼层
*/
void call(int floor);
/**
* 转换电梯状态
* @param cState 当前电梯状态
*/
void transitionState(ElevatorState cState);
/**
* 刷新电梯所在楼层
* @param elevatorState 电梯当前状态
*/
void refreshFloor(ElevatorState elevatorState);
/**
* 获取电梯品牌名称
* @return 电梯名称
*/
String getElevatorBrand();
/**
* 获取当前楼层
* @return 当前楼层
*/
int getCurrentFloor();
}

具体实现如下:

package statusMachine3;
import statusMachine3.elevatorState.*;
import java.awt.event.WindowAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @author ym
*/
public class Elevator implements IElevator {
public final Integer beginFloor = 1;
private Integer cfloor = beginFloor;
private String brand = "";
private IElevatorState openState;
private IElevatorState closeState;
private IElevatorState upState;
private IElevatorState downState;
private IElevatorState waitState;
private IElevatorState resetState;
private IElevatorState cElevatorState;
private List desList = new ArrayList();
private ElevatorActionTrends cElevatorActionTrends = ElevatorActionTrends.WAIT;
private int waitCount = 0;
public final int countLimit = 5;//等待5次后,复位
public Elevator(String brand) {
this.brand = brand;
openState = new OpenState();
closeState = new CloseState();
upState = new UpState();
downState = new DownState();
waitState = new WaitState();
resetState = new ResetState();
cElevatorState = waitState;
}
@Override
public void run() {
cElevatorState.run(this);
}
@Override
public void call(int floor) {
synchronized (desList){
if(!desList.contains(floor)){
desList.add(floor);
System.out.println(String.format("电梯【%s】:被召唤!楼层为:%d",brand,floor));
}
}
}
@Override
public void transitionState(ElevatorState cState){
switch (cState){
case UP:
case DOWN:
synchronized (desList){
if(desList.size() == 0){
cElevatorState = waitState;
}else if(desList.contains(cfloor)) {
desList.remove(cfloor);
cElevatorState = openState;
}
}
break;
case OPEN:
cElevatorState = closeState;
break;
case CLOSE:
cElevatorState = waitState;
break;
case WAIT:
synchronized (desList){
if(desList.size() == 0){
if(!cfloor.equals(beginFloor)) {
if (waitCount > countLimit) {
cElevatorState = resetState;
} else {
waitCount++;
}
}else{
cElevatorActionTrends = ElevatorActionTrends.WAIT;
}
}else if(desList.contains(cfloor)){
cElevatorState = openState;
}else{
waitCount = 0;
Integer maxFloor = Collections.max(desList, Comparator.comparingInt((Integer a)-> a));
Integer minFloor = Collections.min(desList,Comparator.comparingInt((Integer a)->a));
if(cElevatorActionTrends == ElevatorActionTrends.UP){
if(maxFloor > cfloor) {
cElevatorState = upState;
}else{
cElevatorState = downState;
cElevatorActionTrends = ElevatorActionTrends.DOWN;
}
}else if(cElevatorActionTrends == ElevatorActionTrends.DOWN){
if(minFloor < cfloor){
cElevatorState = downState;
}else{
cElevatorState = upState;
cElevatorActionTrends = ElevatorActionTrends.UP;
}
}else{
if(maxFloor > cfloor || minFloor > cfloor){
cElevatorActionTrends = ElevatorActionTrends.UP;
cElevatorState = upState;
}else{
cElevatorActionTrends = ElevatorActionTrends.DOWN;
cElevatorState = downState;
}
}
}
}
break;
case RESET:
if(cfloor.equals(beginFloor)){
cElevatorState = waitState;
}
break;
default:
break;
}
}
@Override
public final String getElevatorBrand(){
return brand;
}
@Override
public final int getCurrentFloor(){
return cfloor;
}
@Override
public final void refreshFloor(ElevatorState elevatorState){
if(elevatorState == ElevatorState.DOWN){
cfloor -= 1;
}else if(elevatorState == ElevatorState.UP){
cfloor += 1;
}else if(elevatorState == ElevatorState.RESET){
if(cfloor > beginFloor){
cfloor -= 1;
}else{
cfloor += 1;
}
}
}
}

其中用到电梯运行趋势枚举,主要是配合运行状态变换来使用:

package statusMachine3;
/**
* 电梯行动趋势
*/
public enum ElevatorActionTrends {
UP,DOWN,WAIT;
}

3、测试

当当当,重头戏来了。测试代码如下:

package statusMachine3;
import java.io.IOException;
public class ElevatorTest {
public static void main(String[] args){
Elevator elevator = new Elevator("大西门子高端豪奢");
Thread l = new Thread(()->{
for(int i=0;i<100000;i++){
elevator.run();
}
});
/*
* 楼层是【-10,29】
* */
Thread p = new Thread(()->{
for (int i=0;i<1000;i++){
Integer m = (int) (Math.random() * 40 - 10);
elevator.call(m);
try {
Thread.sleep((int)(Math.random() * 10000 + 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
l.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.start();
}
}

运行效果如下:

电梯【大西门子高端豪奢】:等待!当前楼层:1
电梯【大西门子高端豪奢】:被召唤!楼层为:-1
电梯【大西门子高端豪奢】:等待!当前楼层:1
电梯【大西门子高端豪奢】:下行!当前楼层:0
电梯【大西门子高端豪奢】:被召唤!楼层为:19
电梯【大西门子高端豪奢】:下行!当前楼层:-1
电梯【大西门子高端豪奢】:在第-1层打开了电梯门!
电梯【大西门子高端豪奢】:在-1层关闭了电梯门!
电梯【大西门子高端豪奢】:等待!当前楼层:-1
电梯【大西门子高端豪奢】:上行!当前楼层:0
电梯【大西门子高端豪奢】:上行!当前楼层:1
电梯【大西门子高端豪奢】:上行!当前楼层:2
电梯【大西门子高端豪奢】:被召唤!楼层为:26
电梯【大西门子高端豪奢】:上行!当前楼层:3
电梯【大西门子高端豪奢】:上行!当前楼层:4
电梯【大西门子高端豪奢】:上行!当前楼层:5
电梯【大西门子高端豪奢】:上行!当前楼层:6
电梯【大西门子高端豪奢】:上行!当前楼层:7
电梯【大西门子高端豪奢】:上行!当前楼层:8
电梯【大西门子高端豪奢】:被召唤!楼层为:3
电梯【大西门子高端豪奢】:上行!当前楼层:9
电梯【大西门子高端豪奢】:上行!当前楼层:10
电梯【大西门子高端豪奢】:上行!当前楼层:11
电梯【大西门子高端豪奢】:上行!当前楼层:12
电梯【大西门子高端豪奢】:上行!当前楼层:13
电梯【大西门子高端豪奢】:上行!当前楼层:14
电梯【大西门子高端豪奢】:上行!当前楼层:15
电梯【大西门子高端豪奢】:上行!当前楼层:16
电梯【大西门子高端豪奢】:上行!当前楼层:17
电梯【大西门子高端豪奢】:上行!当前楼层:18
电梯【大西门子高端豪奢】:上行!当前楼层:19
电梯【大西门子高端豪奢】:在第19层打开了电梯门!
电梯【大西门子高端豪奢】:在19层关闭了电梯门!
电梯【大西门子高端豪奢】:等待!当前楼层:19
电梯【大西门子高端豪奢】:上行!当前楼层:20
电梯【大西门子高端豪奢】:上行!当前楼层:21

4、总结

用了不到一天的时间,最终实现了电梯运行,想想蛮有成就感的。不过完成之后,仍感觉程序有些不足。最初我想的是电梯运行状态在【运行状态类】内部自动转换,但是这样会导致各个【运行状态类】耦合以及杂糅,不得已放在了【电梯主体类】来实现状态转换,因此感觉十分别扭,一点也不优雅。跟我设想的【状态之间】首尾相接、逻辑自洽相差太远。假如我学习到到更好的实现方式后,我会继续更新。至此,感谢屏幕前的你阅读我的分享,谢谢!--------by ym(浮生若梦欢几何)。