Complex System States
STATEMACHINE is like the State mode that allows client programmers to change (class), the state machine is automatically automatically changed from an object from an object from an object to another object. . Implementation represents the current state of the system, exhibits different behaviors between a state and the next state (because STATE mode is used). Basically, this is a "state machine" that uses an object.
Below you will see the framework of a basic state machine, making the code that migrate from a state to the next state is usually compliant with a template method mode (Template Method). First, let's define a tag interface (tagging interface) to enter the object.
//: StateMachine: INPUT.JAVA // INPUTS TO A State MachinePackage StateMachine; Public Interface Input {} ///: ~
Each status is completed through the run () function, and (in this design) you can pass it an input object so that it will tell it to migrate to which new state according to this input object. The main difference between this design and the design of the next section is: For this design, each State object determines which other status can be migrated according to the input object; the following design, all state migration is included in a separate table . In this design, each State object has a small status table, and the entire system has only one state migration in the design.
//: statemachine: state.java// A state Has An Operation, and Can Be Moved // INTO The next state given an input: package stateMachine; public interface state {void run (); state next (Input i); / //: ~
StateMachine will record the current state, which is initialized in the constructor of StateMachine. The runAll () method accepts an iterator about a series of input objects (here the iterator is simple and convenient to map, the input information is passed from other places). This method not only allows the state machine to migrate to the next state, but it also calls the Run () method of each status object - you will find it is the extension of the State mode, because Run () is made according to the different status of the system. Different things.
//: statemachine: StateMachine.java// Takes an Iterator of Inputs to move from State // to State using a template method.package statemachine; import java.util *; public class StateMachine {private State currentState; public StateMachine (State. initialState) {currentState = initialState; currentState.run ();} // Template method: public final void runAll (Iterator inputs) {while (inputs.hasNext ()) {Input i = (Input) inputs.next (); System .out.println (i); currentState = currentState.next (i); currentState.Run ();}}} ///: ~ I use Runall as a template method to implement. This is a typical approach, of course, doesn't say that you have to do this - you may want Concievably to overload it, but usually these behavior changes still in the RUN () method of the State object.
So far, this style of state machine (each state determines its next state), even if it is completed. I will write an imaginary mousetrap (MouseTrap) as an example, which will experience several different states during the trapping of mice. The MOUSE class and related information are stored in Mouse package, including a class that can act as a mouse, which is input as a state machine.
//: statemachine: mouse: MouseAction.java package statemachine.mouse; import java.util *; import statemachine *; public class MouseAction implements Input {private String action; private static List instances = new ArrayList (); private MouseAction (.. String a) {action = a; instances.add (this);} public string toString () {Return Action;} public int.comion.hashcode ();} public boolean equals (Object O) {Return (Object O) {Return ( o instanceof mousection && action.equals ((mouseAction) o);} public static mouseaction forstring (string description) {items = instances.Iterator (); while (it.hasnext ()) {mouseAction ma = (MouseAction); if (ma.action.equals (description)) Return Ma;} throw new runtimeException ("NOT Found:" description);} public static mouseaction APPEARS = New MouseAction ("Mouse Appears" ), Runsaway = New MouseAction ("Mouse Runs Away"), Enters = New Mouse Action ("Mouse Enters Trap"), escapes = new mousection ("mouse escapes"), trapped = new mousection ("mouse trapped"), removed = new mousection ("mouse removed");} ///: ~
You will notice that hashcode () and equals () are overloaded, so doing it for the MouseAction object to be used as the key value of HashMap, but we don't do this in the first version of MouseTrap. In addition, all possible movements of mice are enumerated as a static MouseAction object.
In order to facilitate writing test code, a series of entries related to mice are provided as a text file.
// :! statemachine: mouse: MouseMoves.txtmouse appearsmouse runs awaymouse appearsmouse enters trapmouse escapesmouse appearsmouse enters trapmouse trappedmouse removedmouse appearsmouse runs awaymouse appearsmouse enters trapmouse trappedmouse removed ///: ~ For use common (generic fashion) method to read the file, I wrote a universal StringList tool:
//: com: Bruceeckel: util: stringlist.java// general-purpose Tool That Reads a list, one line per list.package com.bruceeckel.util; import java.io. *; import java.util *;. public class StringList extends ArrayList {public StringList (String textFilePath) {try {BufferedReader inputs = new BufferedReader (new FileReader (textFilePath)); String line; while ((line = inputs.readLine ())! = NULL) Add (ion.trim ());} catch (ioException e) {throw new runtimeException (e);}}} ///: ~
This StringList can only store objects like ArrayList, so we also need an adapter to convert the String object into a mouseAction object:
//: statemachine: mouse: MouseMoveList.java// A "transformer" to produce a // List of MouseAction objects.package statemachine.mouse; import java.util *; import com.bruceeckel.util *; public class MouseMoveList.. Extends ArrayList (Ite.hasNext ());}} ///: ~}} ///: ~}} ///: ~}} ///: ~}} ///: ~}} ///: ~
MousemoveList looks a bit like a decoration (Decorator), and actually dry is like Adapter dry live. No matter what is, Adapter changes an interface to another interface, and Decorator adds functions or data to existing classes. MouseMovel is changed the content of the container, so it may be called TRANSFORMER more appropriate.
With these tools, you can now create the first version of Mousetrap. Each class derived from State defines its own Run () behavior, and then determines its next state with the IF-ELSE statement.
//: statemachine: mousetrap1: MouseTrapTest.java// State Machine pattern using 'if' statements // to determine the next state.package statemachine.mousetrap1; import statemachine.mouse *; import statemachine *; import com.bruceeckel... Util. *; import java.util. *; import java.io. *; import junit.framework. *; // a Different Subclass for Each State: Class Waiting Implements State {PUBLIC VOID RUN () {System.out.Println ("Waiting: Broadcasting Cheese Smell");} public state next (input i) {mouseaction ma = (mouseAction) i; if (mouseaction.appears) Return mousetrap.luRing;}} Class Luring Implements State {PUBLIC VOID RUN () {System.out.println ("Luring: Presenting Cheese, Door Open");} public state next (input i) {mouseaction ma = (mouseAction) i; if (m.equals (MouseAction.Runsaway)) Return mousetrap.waiting; if (mouseAction.enters) Return mousetrap.trapping; return mousetrap.luring;}} Class Trap PING IMPLEMENTS State {PUBLIC VOID RUN () {System.out.Println ("trapping: closing door");} public state next (input i) {mouseaction ma = (mouseAdition) i; if (mouseAction.escapes )) return MouseTrap.waiting; if (ma.equals (MouseAction.trapped)) return MouseTrap.holding; return MouseTrap.trapping;}} class Holding implements State {public void run () {System.out.println ( "Holding: Mouse caught ");} public State next (Input i) {MouseAction ma = (MouseAction) i; if (ma.equals (MouseAction.removed)) return MouseTrap.waiting; return MouseTrap.holding;}} class MouseTrap extends StateMachine { Public Static State Waiting =
new Waiting (), luring = new Luring (), trapping = new Trapping (), holding = new Holding (); public MouseTrap () {super (waiting); // Initial state}} public class MouseTrapTest extends TestCase {MouseTrap trap = new mousetrap (); mousemovelist moves = new mousemovel ("../ mouse / mousemoves.txt") .Iitrator ()); public void test () {trap.Runall ());} Public static void main (string args []) {junit.textui.teestrunner.run (mousetraptest.class);}} ///: ~ STATEMACHINE class Simply defines all possible states into static objects, then set your own Initial state. UnitTest creates a mousetrap object and then tests it with MouseMovelist as input as your input.
Although IF-ELSE statements are used inside the next () method, it is not very bad, but if the number of status is large, this method will become very troublesome. Another method is to create a table structure inside each State object, define a different sub-state according to the input information.
Just start, this seems to be very simple. You need to define a static table structure in each State subclass, and this table is to determine how it is migrated according to other STATE objects. However, this approach will generate cyclic dependencies when initialization. In order to solve this problem, I have to delay the initialization of the table structure to the next () method of a particular State object is first called for the first time. For this reason, the next () method seems to be a bit strange.
The STATET class is an implementation of the State interface (so that it can share the same StateMachine class with the above example), and it also adds a MAP and a method for initializing this MAP from a two-dimensional array. The next () method has an implementation in the base class. The NEXT () method of derived class overload is first tested whether the MAP is empty (if it is empty, then initializes), then it will call the base class next () method.
//: statemachine: mousetrap2: MouseTrap2Test.java// A better mousetrap using tablespackage statemachine.mousetrap2; import statemachine.mouse *; import statemachine *; import java.util *; import java.io *; import com..... bruceeckel.util *;. import junit.framework *;. abstract class StateT implements State {protected Map transitions = null; protected void init (Object [] [] states) {transitions = new HashMap (); for (int i = 0 i }}} Class trapping extends statet {public void run () {system.out.println ("trapping: closing door");} public state next (Input i) {if (transitions == null) init (new object [] [new object [] [ ] {MouseAction.escapes, mousetrap.waiting}, {mouseaction.trapped, mousetrap.holding},}; return super.next (i);}}} class holding extends statet {public void run () {system.out. Println ("Holding: Mouse Caught");} public state next (input i) {if (transitions == null) init (new object [] [] {{mouseaction.removed, mousetrap.waiting},}; return super .next (i);}} public class MouseTrap2Test extends TestCase {MouseTrap trap = new MouseTrap (); MouseMoveList moves = new MouseMoveList (new StringList ( "../ mouse / MouseMoves.txt") .iterator ()); public void Test () {trap.Runall (moves.iterator ());} public static void main (string args []) {junit.textui.testrunner.run (mousetrap2test.class);}} ///: ~ Part of the example is exactly the same - different parts NEXT () method and the statet class. If you have to create and maintain a very much State class, this method (relative to the previous example) has improved because the migration relationship between the various states is read easily and quickly read by viewing the table structure. Exercise 1. Change MouseTrap2test.java, load status table information from an external file containing only status table information. Table-Driven State Machine The advantage of the design is the information about the state, including state migration information, all in the STATE class. Usually, this is a good design habit. However, for a pure state machine, it can be represented by a separate state migration table. The benefits of doing this are all about the information of the state machine, which means that you can simply create and maintain it according to a traditional state migration map. Traditional status migration diagram uses a circle to represent all of the other states with different thin lines to a certain state can be migrated. Each migration line is marked with migration conditions and actions. Below is a state diagram: (A simple state machine map) Objective: · Direct translation status map · Vector of change: The State Diagram RePresentation · Reasonable implementation · There is no excess state (you can use new status to indicate each individual change) · Simple and flexible Observation: • State is not the most important - they do not contain information or function / data, just a logo · There is no similarity with the State mode · The state machine controls the migration between the various states. Some of the like. Each state may migrate to multiple other states · Migration conditions and action must be in the state. In order to make the configuration, it will describe all status changes, put it in a separate table. Example: • State machine and code for table driven • Implement a vending machine · Use a few modes to separate the general state machine code and a specific program code phase (like Template Method mode) · For each Enter Search Fit Solutions (like responsible chain mode) · Test and status migration are packaged in function objects (objects of package functions). · Java constraints: Methods Are Not First-Class Objects The State class is completely different from the above, it is just a placeholder, a placeholder. So it doesn't inherit the last State class. //: StateMachine2: state.javaPackage StateMachine2; Public Class State {Private String Name; Public State (String nm) {name = nm;} public string toString () {return name;}} ///: ~ Conditions for Transition On the status migration diagram, the input verifies whether it satisfies the migration conditions, and then determines whether it can migrate to the corresponding sub-state. As with the previous example, here the INPUT class is also a tag interface. //: StateMachine2: Input.java// INPUTS to a state machinePackage StateMachine2; public interface input {} ///: ~ The Condition class determines whether the line in the table is a qualified migration by determining the input of the input INPUT object. //: statemachine2: condition.java// condition function for state2; public interface condition {boolean condition (Input i);} ///: ~ Transition Actions If the Condition class returns true, (current status) will migrate to a new state, some movements occur during this migration (in the design of the state machine in front, this action is Run () method): //: StateMachine2: Transition.java/ Transition Object for State MachinePackage StateMachine2; Public Interface Transition {Void Transition (Input i);} ///: ~ Table Structure The table has these classes, we can create a three-dimensional table structure, each line just describes a state. The first line represents the current state, the rest of the rows include input types, must meet the status migration conditions, the action that occurs during the migration process, and the second state to migrate. Note that the Input object does not only represent the input, it itself is also a Messenger object, which will pass the information to the Condition and Transition objects. {{CurrentState}, transition, next}, {INPUT, CONDition (Input), Transition (Input), Next}, {INPUT, CONDition (Input), Transition (INPUT), NEXT}, ...} Basic state machine THE Basic Machine //: statemachine2: StateMachine.java // A table-driven state machine package statemachine2; import java.util *; public class StateMachine {private State state; private Map map = new HashMap (); public StateMachine (State initial) {. State = initial;} public void buildtable (Object [] [] Table) {for (int i = 0; i
Simple vending machine Simple Vending Machine
//: statemachine: vendingmachine: VendingMachine.java // Demonstrates use of StateMachine.java package statemachine.vendingmachine; import statemachine2 *; final class VM extends State {private VM (String nm) {super (nm);} public final static. VM Quiescent = New VM ("Quiesecent"), Collecting = New VM ("Collecting"), Selecting = New VM ("Selecting"), Unavailable = New VM ("Unavailable", Wantmore = New VM ("Want More? "), noChange = new VM (" Use Exact Change Only "), makesChange = new VM (" Machine makes change ");} final class HasChange implements Input {private String name; private HasChange (String nm) {name = nm; } public String toString () {return name;} public final static HasChange yes = new HasChange ( "Has change"), no = new HasChange ( "Can not make change");} class ChangeAvailable extends StateMachine {public ChangeAvailable () {super (Vm.makeschange); buildtable (new object [] [] [] {{vm.makeschange}, // current State // INPUT, TEST, Transition, Next State: {Haschange.No, Null, Null, VM.NOCHANGE}}, {{VM.NOCHANGE}, // Current State // INPUT, TEST, Transition, Next State: {Haschange .}} final class mother,}}} final class mother;}} final class mother;}}} Final Class Money Implements Input {Private String Name; Private Int Value; Private Money (String NM, INT VAL) {Name = Nm; Value = Val; PUBLIC STRING TOSTRING () {Return Name;} public int getValue () {Return Value;} public final static money quarter = new Money ("Quarter", 25), DOLLAR = New Money ("Dollar", 100);
} Final class Quit implements Input {private Quit () {} public String toString () {return "Quit";} public final static Quit quit = new Quit ();} final class FirstDigit implements Input {private String name; private int value ; private FirstDigit (String nm, int val) {name = nm; value = val;} public String toString () {return name;} public int getValue () {return value;} public final static FirstDigit A = new FirstDigit ( " A ", 0), B = New FirstDigit (" B ", 1), C = New Firstdigit (" C ", 2), D = New Firstdigit (" D ", 3);} Final Class SecondDigit IMPLEments Input {Private String name; private int value; private SecondDigit (String nm, int val) {name = nm; value = val;} public String toString () {return name;} public int getValue () {return value;} public final static SecondDigit One = New SecondDigit ("One", 0), Two = New SecondDigit ("Two", 1), Three = New SecondDigit ("Three", 2), Four = New SecondDigit ("FOUR ", 3);} class itemlot {int Price; int quantity; static int counter = 0; string id = integer.tostring (counter ); public itemslot (int prc, int quant) {price = prc; quantity = quant;} public String toString () {return id;} public int getPrice () {return price;} public int getQuantity () {return quantity;} public void decrQuantity () {quantity--;}} public class VendingMachine extends StateMachine {StateMachine changeAvailable = New changeavailable (); int AMOUNT = 0; firstdigit first = null; itemslot [] [] items = new itemslot [4] [4];
Condition notenough = new condition () {public boolean condition (input INPUT) {INT I1 = first.getValue (); int i2 = ((seconddigit) INPUT .GetValue (); return items [i1] [i2] .Getprice )> amount;}}; condition item () {int {INT I1 = first.getValue (); int I2 = ((seconddigit) INPUT .GetValue (); return items [i1 ] [i2] .getQuantity ()> 0;}}; Condition itemNotAvailable = new Condition () {public boolean condition (Input input) {return itemAvailable.condition (input);!}}; Transition clearSelection = new Transition () { Public void transition {INT I1 = first.getValue (); int i2 = ((seconddigit) INPUT .getValue (); itemslot is = items [i1] [i2]; system.out.println ("clearing Selection: Item " IS " COSTS " Is.GetPrice () " And Has Quantity " Is.GetQuantity ()); first = null;}}; trave NSition Dispense = new transition () {public void transition (INPUT INPUT) = first.getValue (); INT I2 = ((SecondDigit) Input) .getValue (); itemslot is = items [i1] [i2]; System.out.Println ("Dispensing Item" IS "COSTS" is.getPrice () "and has quantity" is.getquantity ()); items [i1] [i2] .decrquantity (); system. Out.println ("new quantity" is.getquantity ()); amount - = is.getprice (); system.out.println ("Amount Remaining Amount);}};
Transition showtotal = new transition () {public void transition (Input Input) {Amount = ((Money) INPUT .GetValue (); system.out.println ("total amount =" amount);}}; transition = new Transition () {public void transition (Input input) {System.out.println ( "Returning" amount); amount = 0;}}; Transition showDigit = new Transition () {public void transition (Input input) { First Digit) Input; System.out.Println ("first Digit =" first);}}; public vendingmachine () {super (vm.quiescent); // Initial State for (int i = 0; i < Items.lend (I ) for (int J = 0; j Buildtable (New Object [] [] [] {{vm.quiescent}, // current state // INPUT, TEST, Transition, Next State: {Money.class, Null, Showtotal, VM.COLLECTING}}} {{ VM.COLLECTING}, Test, Transition, Next State: {quit.quit, null, returnchange, vm.quiescent}, {Money.class, Null, Showtotal, VM.COLLECTING}, {FIRSTDIGIT .class, null, showdigit, {{vm.selecting}, {{vm.selecting}, test, transition, next state: {quit.quit, null, returnchchange, vm.quiescent}, { SecondDigit.class, notEnough, clearSelection, VM.collecting}, {SecondDigit.class, itemNotAvailable, clearSelection, VM.unavailable}, {SecondDigit.class, itemAvailable, dispense, VM.wantMore}}, {{VM.unavailable}, / / Current State // Input, Test, Transition, Next State: {quit.quit, null, Returnchange, vm.quiescent}, {firstdigit.class, null, showdigit, vm.selecting}}, {{vm.wantmore}, // current state // infut, test, transition, next state: {quit.quit, null ReturnChange, VM.quiescent}, {firstdigit.class, null, showdigit, vm.selecting}},};}} ///: ~ Test vending machine Testing the machine //: statemachine: vendingmachine: VendingMachineTest.java // Demonstrates use of StateMachine.java package statemachine.vendingmachine; import statemachine2 *; import junit.framework *; public class VendingMachineTest extends TestCase {VendingMachine vm = new VendingMachine (); Input.. [] inputs = {Money.quarter, Money.quarter, Money.dollar, FirstDigit.A, SecondDigit.two, FirstDigit.A, SecondDigit.two, FirstDigit.C, SecondDigit.three, FirstDigit.D, SecondDigit.one, Quit .quit,}; public void test () {for (int i = 0; i Tool Tools When the state machine is more and more complicated, another method is to use the automation tool to generate a state machine by configuring a table to generate a state machine. You can write one by language like Python, but someone has written free and is an open source tool, such as Libero, you can find http://www.imatix.com. Code for table drive (Table-Driven): Flexibility to achieve flexibility Realize the table driver by anonymous internal classes See Tij Chapter 9 ListPerFormance.java that example. Greenhouse.java Exercise