Design patterns: state patterns and state machines

Thread. sleep(0) 2022-02-13 08:54:23 阅读数:685

design patterns state patterns state

Preface

I met such a demand at work :

 Control disinfection cabinet :
1. When the cabinet door opens , Disinfection... Shut down , And reset the disinfected time ;
2. When the cabinet door is closed , Open disinfection , And at the appointed time ( Such as 30 minute ) Turn off disinfection after ;

After receiving this demand , My first reaction was that there were a lot of State flow , You can try the state mode . Through such a real company demand , Explain the state mode in design mode , And his application ---- State machine .

The state pattern

In a nutshell , The state mode is , Give an object an attribute , This property represents the current state of the object , The object can perform different actions according to this state .

Realization

The realization of design patterns is inseparable from abstraction , The same goes for state mode , It can be divided into the following 3 Step implementation :

  1. Define base class BaseState: It contains doExecute Method ;
  2. Different states , inherit BaseState, And implement doExecute Method , Represents the actions that can be performed in this state ;
  3. For an object , increase BaseState attribute , So you can call BaseState.doExecute() Implement different states and perform different behaviors .

State machine

State mode after listening to the above explanation , You may think this model is of little use , I don't know when to use it . But in real work , Requirements are often very complex , A simple state mode can't solve any problems , It can only be said that this is an abstraction 、 The idea of encapsulation . The realization of a requirement cannot be solved by a design pattern , There must be multiple design patterns working together . Next, let's introduce the state machine , It is an application of state mode .

Concept

Simply speaking , The state machine is A collection of states , It maintains the flow relationship between this group of States .

The state machine has three roles , namely event 、 state 、 action

state

A state machine maintains a set of states , So the role of state is essential .

event

There will be a flow between States , And the event is the clue that triggers this flow .

action

A state can be unexplained or After a certain action Before the flow goes to another state

Implementation skills

When analyzing how the state machine is used , You can follow the steps below :

  1. Define three roles , That is to put the whole demand into , The state that will appear , What will happen , List all the actions to be performed

  2. Draw a state flow diagram , The flow chart needs to be clarified :

    a. What events can trigger each state ?
    b. After each state is triggered, what state will it flow to ?
    c. Each state triggers an event , What actions will be performed ?

actual combat

After being familiar with the basic knowledge above , Next, according to the real case we met at the beginning , Use state patterns to analyze and implement it .

analysis

First, we follow the above analysis method of state machine , To analyze this demand :

  1. Define the role :
    state : Not disinfected 、 Disinfecting 、 Sterilized state

    event : Closing event 、 Door opening event 、 Disinfection completion event

    action : Open disinfection 、 Disinfection... Shut down

    The definition codes of the above three roles are as follows

    public enum LockerEventEnum {
    
    /** * Closing event ----> Open disinfection And start timing */
    CLOSE_DOOR,
    /** * Door opening event ----> Turn off disinfection */
    OPEN_DOOR,
    /** * The disinfection time has expired ----> Turn off disinfection */
    TIME_OUT
    }
    public enum LockerStateEnum {
    
    /** * Not disinfected , Receive the closing event */
    UN_UV,
    /** * Disinfection status , Receive and open the door 、 Disinfection timeout event */
    UVING,
    /** * Sterilized state , Receive the door opening event */
    HAS_UV
    }
    /** * Action base class * * @author Hu */
    public abstract class BaseTransition {
    
    /** * The concrete implementation of the action * @param event The event that triggered the action * @param machine * @return After the action is executed , Next state */
    public abstract LockerState doExecute(LockerEvent event, LockerMachine machine);
    }
    /** * Turn off the disinfection action * * @author Hu */
    public class CloseUvTransition extends BaseTransition {
    
    private final String TAG = "CloseUvTransition";
    @Override
    public LockerState doExecute(LockerEvent state, LockerMachine machine) {
    
    // Close the specific implementation class 
    UvManager.getInstance().close(machine);
    // According to the event that triggered the action , Return to the corresponding next status 
    switch (state.getEvent()) {
    
    case OPEN_DOOR: {
    
    return StateFactory.createEvent(LockerStateEnum.UN_UV);
    }
    case TIME_OUT: {
    
    return StateFactory.createEvent(LockerStateEnum.HAS_UV);
    }
    default:
    return StateFactory.createEvent(LockerStateEnum.UN_UV);
    }
    }
    }
    /** * Open the disinfection task * * @author hu */
    public class OpenUvTransition extends BaseTransition {
    
    private final String TAG = "OpenUvTransition";
    @Override
    public LockerState doExecute(LockerEvent state, LockerMachine machine) {
    
    // Set the current time to start disinfection 
    machine.setLastUvTime(System.currentTimeMillis());
    // Add the object to the polling queue , Determine whether the specified disinfection time is reached 
    MachineManager.getInstance().addUvIngMachine(machine);
    // Open the specific implementation class 
    UvManager.getInstance().open(machine);
    return StateFactory.createEvent(LockerStateEnum.UVING);
    }
    }
    
  2. Draw a state diagram :
    a. What events can trigger each state ?

    b. After each state is triggered, what state will it flow to ?

    c. Each state triggers an event , What actions will be performed ?

    The three questions above :

     Not disinfected :
    Can be triggered by the closing event , And perform the opening disinfection action , The circulation is in the disinfection state
    Disinfection status :
    Can be triggered by the door opening event , And execute the closing disinfection action , The circulation is not sterilized
    Can be triggered by the disinfection completion event , And execute the closing disinfection action , The circulation is in the completed disinfection state
    Disinfection status completed :
    Can be triggered by the door opening event , And execute the closing disinfection action , The circulation is not sterilized
    

After analyzing the problem , Our thinking is quite clear , Next, let's look at the implementation of the code .

State class :

/** * The lock state * * @author Hu */
public class LockerState {

/** * State properties */
private LockerStateEnum state;
public LockerStateEnum getState() {

return state;
}
/** * Events that this state can respond to */
private List<LockerEvent> eventList;
/** * Constructors * @param state */
public LockerState(LockerStateEnum state) {

this.state = state;
eventList = new ArrayList<>();
}
/** * What events can be responded to by initializing this state ; * @param events */
public void declareEvents(LockerEvent... events){

eventList.addAll(Arrays.asList(events));
}
/** * Judge whether the event can be responded by this state * * @param event * @return */
public boolean canHandle(LockerEvent event) {

if (eventList != null && eventList.size() > 0) {

for (LockerEvent item : eventList) {

if (item.getEvent() == event.getEvent()) {

return true;
}
}
return false;
} else {

return false;
}
}
}

The state class contains a list of events that can be responded to , At the beginning of the program , Need to pass ahead of time declareEvents() Define the events that the state class can respond to , When the event happens, pass canHandle() Method to determine whether the state can respond to

Event classes :

/** * Event classes * * @author Hu */
public class LockerEvent {

/** * Event enumeration */
private LockerEventEnum event;
/** * When this event occurs, the corresponding action */
private BaseTransition transition;
public LockerEventEnum getEvent() {

return event;
}
public BaseTransition getTransition() {

return transition;
}
/** * The constructor needs to complete : * 1. Define the execution action of this event * @param event */
public LockerEvent(LockerEventEnum event, BaseTransition transition) {

this.event = event;
this.transition = transition;
}
}

The event class has BaseTransition attribute , Represents when the event is responded to , Actions that can be performed

Whether it's an event or a state , All need to be defined in advance , Here I implement the construction of events and states through a simple factory

/** * Status factory * * @author Hu */
public class StateFactory {

/** * todo take LockerState Make it into a single example , Save a space */
public static LockerState createEvent(LockerStateEnum stateEnum) {

LockerState state = new LockerState(stateEnum);
switch (stateEnum) {

case UN_UV: {

// Not disinfected , Door opening event and door closing event 
state.declareEvents(EventFactory.createEvent(LockerEventEnum.CLOSE_DOOR));
break;
}
case UVING: {

// Responding to disinfection status , Door opening event and disinfection completion event 
state.declareEvents(EventFactory.createEvent(LockerEventEnum.TIME_OUT),
EventFactory.createEvent(LockerEventEnum.OPEN_DOOR));
break;
}
case HAS_UV: {

// Disinfection completion status , Respond to door opening events 
state.declareEvents(EventFactory.createEvent(LockerEventEnum.OPEN_DOOR));
break;
}
default:
}
return state;
}
}
/** * Event factory * * @author Hu */
public class EventFactory {

/** * todo LockerEvent Make it into a single example , Save a space */
public static LockerEvent createEvent(LockerEventEnum eventEnum) {

switch (eventEnum) {

case OPEN_DOOR: {

// Door opening event 
return new LockerEvent(LockerEventEnum.OPEN_DOOR, new CloseUvTransition());
}
case CLOSE_DOOR: {

// Closing event 
return new LockerEvent(LockerEventEnum.CLOSE_DOOR, new OpenUvTransition());
}
case TIME_OUT: {

// Disinfection completion event 
return new LockerEvent(LockerEventEnum.TIME_OUT, new CloseUvTransition());
}
default:
return new LockerEvent(LockerEventEnum.TIME_OUT, new CloseUvTransition());
}
}
}

Finally, we need to write Objects that hold state properties , as well as Manage state machines for these objects class .

/** * Lock state machine * * @author Hu */
public class LockerMachine {

/** * Lock entity */
private Locker locker;
public Locker getLocker() {

return locker;
}
/** * Current state */
private LockerState curState;
public LockerState getCurState() {

return curState;
}
/** * Start time of disinfection */
private long lastUvTime;
public long getLastUvTime() {

return lastUvTime;
}
public void setLastUvTime(long lastUvTime) {

this.lastUvTime = lastUvTime;
}
/** * What the constructor needs to do : * <p> * 1. initialization locker data * <p> * 2. The current status of initialization is UN_UV */
public LockerMachine(Locker locker) {

this.locker = locker;
this.lastUvTime = 0;
curState = StateFactory.createEvent(LockerStateEnum.UN_UV);
}
/** * Responding to events : * <p> * 1. Determine whether the event can be responded to * <p> * 2. Execute the corresponding action through the event * * @param event */
public void execute(LockerEvent event) {

if (curState.canHandle(event)) {

// Can handle the event 
curState = event.getTransition().doExecute(event, this);
}
}
}

Each object has the property of the current state , And how to respond to events execute(), When the event comes, pass curState.canHandle Determine whether you can respond to , Re pass event.getTransition().doExecute Executive action .

/** * State machine control class , The role is : * <p> * 1. Maintain all lockerMachine, Event distribution * <p> * 2. Yes, it's disinfecting lockerMachine timing , Distribution timeout close disinfection event * * @author Hu */
public class MachineManager {

private final String TAG = "MachineManager";
/** * Single case */
private volatile static MachineManager instance;
private MachineManager() {

machines = new ArrayList<>();
uvIngMachines = new ArrayList<>();
machinesThread = new LoopMachinesThread();
machinesThread.setInterrupt(false);
queryLockerThread = new LoopQueryLockerThread();
queryLockerThread.setInterrupt(false);
}
public static MachineManager getInstance() {

if (instance == null) {

instance = new MachineManager();
}
return instance;
}
/** * adopt locker Query response LockerMachine */
private LockerMachine getLockerMachine(Locker locker) {

for (LockerMachine item : machines) {

if (item.getLocker().equals.locker) {

return item;
}
}
return null;
}
/** * Maintain all LockerMachine */
private List<LockerMachine> machines;
/** * Maintain the disinfecting LockerMachine */
private List<LockerMachine> uvIngMachines;
/** * polling uvIngMachines Threads , Judge whether it is time to close the disinfection */
private LoopMachinesThread machinesThread;
/** * polling Locker Threads , Determine whether the door is locker */
private LoopQueryLockerThread queryLockerThread;
/** * adopt Locker initialization machines, And start the detection thread , Start to enter the state of circulation * * @param list Locker data */
public void initData(List<Locker> list) {

for (Locker item : list) {

machines.add(new LockerMachine(item));
}
//
if (!machinesThread.getInterrupt()) {

machinesThread.setInterrupt(false);
machinesThread.start();
}
if (!queryLockerThread.getInterrupt()) {

queryLockerThread.setInterrupt(false);
queryLockerThread.start();
}
}
/** * Stop flow */
public void stop() {

machinesThread.setInterrupt(true);
queryLockerThread.setInterrupt(true);
}
/** * Pointing to locker Distribute events */
public void dispatchEvent(LockerEvent event, Locker locker) {

LockerMachine lockerMachine = getLockerMachine(locker);
if (lockerMachine != null) {

lockerMachine.execute(event);
}
}
/** * Add a value to the queue being disinfected * @param machine */
public void addUvIngMachine(LockerMachine machine) {

if (uvIngMachines != null) {

uvIngMachines.add(machine);
}
}
class LoopQueryLockerThread extends Thread {

private boolean isInterrupt = false;
public void setInterrupt(boolean isInterrupt) {

this.isInterrupt = isInterrupt;
}
public boolean getInterrupt() {

return isInterrupt;
}
@Override
public void run() {

super.run();
while (!isInterrupt) {

//10 Check the status of all cabinet doors every second 
SystemClock.sleep(10 * 1000);
// Query the results of each cabinet door , Return to the closed locker
LockerManager.query((result) -> {

// Distribute the closing event 
for (Locker i : result) {

dispatchEvent(EventFactory.createEvent(LockerEventEnum.CLOSE_DOOR),i);
}
});
}
}
}
class LoopMachinesThread extends Thread {

private boolean isInterrupt = false;
public void setInterrupt(boolean isInterrupt) {

this.isInterrupt = isInterrupt;
}
public boolean getInterrupt() {

return isInterrupt;
}
@Override
public void run() {

super.run();
while (!isInterrupt) {

SystemClock.sleep(10 * 1000);
List<LockerMachine> temp = new ArrayList<>();
// Polling the cabinet being disinfected 
for (LockerMachine item : uvIngMachines) {

// Judge whether the disinfection is completed 
if (System.currentTimeMillis() - item.getLastUvTime() >= ParamConfig.maxUvTime) {

//1. Distribution of disinfection completion Events 
dispatchEvent(
EventFactory.createEvent(LockerEventEnum.TIME_OUT),
item.getLocker().section,
item.getLocker().port);
//2. After distribution, remove the item 
temp.add(item);
}
}
uvIngMachines.remove(temp);
}
}
}
}

summary

In the process of re implementation , We found that design patterns are not implemented according to templates , It's customized according to the real scene . But the fundamentals of design patterns remain the same , That is to find the role , Find commonalities , Finally, pull away .

And we can find , When building events and states , We used a simple factory , Of course, we can use Builder 、 Single example , No problem . On the event distribution , Is it also like an observer mode , Therefore, the use of design patterns is very flexible , And it needs to be combined .

copyright:author[Thread. sleep(0)],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/02/202202130854172673.html