简体   繁体   中英

A good design pattern for a state machine with specific behavior

I am was having a simply state machine, which is now getting interference from a lot of special cases.

Initially:

void state(SomeObject o) {
   doStep1();
   doStep2();
   doStep3();
}

Now, someobject had a field called type - say typeA, typeB, typeC. typeC demanded some special treatement.

   void state(SomeObject o) {
       doStep1();  
       doStep2();
       if (o.type == typeC) { o.doFoo(); } 
       doStep3();
       if (o.type == typeC) { o.doFoo(); } 
    }

Clearly this code is not extensible and fragile. I have a 4th type called typeD which would only add up more if-elses. Which pattern to use for such cases ? If I do use polymorphism, Assuming an interface SomeObject, which has 4 implementations for type ABC and D, I fear that A and B would have empty implementations for doFoo() which is not good. Any good design pattern ?

The state logic is as you already indicated inflexible. The problem can get even more sophisticated if, for example, for some objects we need to execute the operations in a different order (3->1->2).

As the behavior depends mainly on the SomeObject type, I believe that one "clean" approach would be to translate each object into a set of composable commands (Command pattern+Composite/Decorator).

/* Declare an interface for executing an operation */
public interface Command
{
    public void execute();
    public Command setNext();
    public Boolean hasNext();
}
/* Abstract class providing compose-ability and chaining behavior for its children.
*/
public abstract class BaseCommand implements Command
{
    private Command next;

    public Boolean hasNext()
    {
        return next != null;
    }
    public Command setNext(Command nextC)
    {
        next = nextC;
        return nextC;
    }
    public void execute(){
        executeImpl();
        if(hasNext()) next.execute();
    }

    public abstract void executeImpl();
}

Now, you can define a set of commands corresponding to a particular processing (each would map directly to a particular "line"/step of your state method).

public class Step1Command extends BaseCommand
{
    // If we need access to the SomeObject instance we can define a dependecy on it
    // ex. through a constructor

    //The necessary processing goes here
    public void executeImpl(){
         doStep1();
    }
}

Finally, you need to translate your objects into a set of commands, this can be achieved through a factory class:

public class CommandFactory
{

    //The necessary processing goes here
    public Command create(SomeObjectA typeA){
         Command firstCommand = new Step1Command(typeA);
         Command secondCommand = new Step2Command(typeA);
         //....
         Command lastCommand = new StepXCommand(typeA);
         //We can easily switch the order of processing for a particular object
         fistCommand.setNext(secondCommand)
                    //...
                    .setNext(lastCommand);

         return firstCommand;
    }
}

How would look your code now ?

CommandFactory cFactory = new CommandFactory();
void state(SomeObject o) {
   Command command = cFactory.create(o);
   command.execute();
}

So, what's the added value (as this may look like an overkill) ?

  1. The processing depending on object type is moved away from the state method. Method overloading + inheritance should allow you to bypass if/elses.

  2. You can easily switch the order of the necessary processing (), which makes the logic more flexible.

  3. Adding new SomeObject implementations processing won't alter your existing code ( Maintainability+Extensibility)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM