Reputation: 5660
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 A B C and D, I fear that A and B would have empty implementations for doFoo() which is not good. Any good design pattern ?
Upvotes: 1
Views: 2399
Reputation: 1881
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) ?
The processing depending on object type is moved away from the state
method. Method overloading + inheritance should allow you to bypass if/elses.
You can easily switch the order of the necessary processing (), which makes the logic more flexible.
Adding new SomeObject implementations processing won't alter your existing code ( Maintainability+Extensibility)
Upvotes: 6