1.概述

本文将介绍在Java中如何通过枚举来实现状态机。通过与接口及具体类来的实现方式来说明枚举实现状态机器的优势。

2.Java Enums

枚举是一种定义了一组常量的特殊的类。通过枚举使代码可读性更好。我们以人力资源系统为例,该系统可以批准雇员离职申请。这个申请需要小组领导审批,随后将审批结果提交给部门领导。部门领导最终有权利批准申请。

首先定义离职请求涉及的状态。

public enum LeaveRequestState {
	Submitted,
	Escalated,
	Approved
}

使用枚举:

LeaveRequestState state = LeaveRequestState.Submitted;

枚举可以包含方法,我们可以在枚举中写一个抽象方法,每个枚举实例都必须实现这个方法。由于Java 枚举会隐式继承java.lang.Enum类,因此不能在继承其它类。然而,可以再实现接口。

public enum LeaveRequestState {
	Submitted {
		@Override
		public String responsiblePerson() {
		     return "Employee";
		}
	},
	Escalated {
		@Override
		public String responsiblePerson() {
		      return "Team Leader";
		}
	},
	Approved {
		@Override
		public String responsiblePerson() {
		      return "Department Manager";
		}
	};

    public abstract String responsiblePerson();
}

我们通过定义responsiblePerson来获取处理每个状态的负责人。比如Escalated状态是由Team Leader来处理。

LeaveRequestState state = LeaveRequestState.Escalated;
assertEquals("Team Leader", state.responsiblePerson());

同理可以检查Approved状态负责人

LeaveRequestState state = LeaveRequestState.Approved;
assertEquals("Department Manager", state.responsiblePerson());

3.枚举实现状态机

通过枚举实现状态机时,我们不要显式去设置状态。我们只需要提供状态转移的逻辑。

public enum LeaveRequestState {

	Submitted {
		@Override
		public LeaveRequestState nextState() {
			return Escalated;
		}
	
		@Override
		public String responsiblePerson() {
			return "Employee";
			}
	},
	Escalated {
		@Override
		public LeaveRequestState nextState() {
			return Approved;
		}
		
		@Override
		public String responsiblePerson() {
			return "Team Leader";
		}
	},
	Approved {
		@Override
		public LeaveRequestState nextState() {
			return this;
		}
		
		@Override
		public String responsiblePerson() {
			return "Department Manager";
		}
	};

	public abstract LeaveRequestState nextState();
	public abstract String responsiblePerson();
}

上面的例子中,状态机的转换通过枚举的方法nextState来实现。此外还可以根据希求提供previousState方法,来获得前一个状态。下面的测试来检查我们的实现

LeaveRequestState state = LeaveRequestState.Submitted;

state = state.nextState();
assertEquals(LeaveRequestState.Escalated, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

我们从初始状态Submitted开始,并通过nextState方法来转换到下一个状态,直到Approved终止。

4.通过Java枚举实现状态机的优点

如果通过类来实现会产生大量的代码。而Java枚举是一组常量,我们可以通过使用枚举来定义状态。而枚举也可以包含行为,我们可以使用方法来实现状态机的转换。所有逻辑均放在枚举中使得代码简介干净,对于逻辑简单的状态机便于维护。