Situation: My States have an entry() and exit() method that needs to be called every time when a State transitions. To ensure that, I used a changeState() method in State that contains the necessary procedure. It is called by the Context every time it uses an operation involving the state machine. The problem however, is that I need to call state.changeState() every time when I add a new method and I'm sure there's a way to avoid that code duplication. Below is code for further clarification
class Context {
State state;
void method1() {
state.method1();
state.changeState();
}
void method2() {
state.method2();
state.changeState(); // Code duplication!!
}
abstract class State {
Context context;
State next;
void changeState() {
this.exit();
next.entry();
context.setState(next);
}
// other necessary methods
}
class ConcreteState extends State {
void method1() {
// do something
next = AnotherConcreteState;
}
void entry() {/*do something */};
void exit() {/*do something */};
}
If I want to add additional methods in Context, what can I do to avoid code duplication of calling state.changeState() every time inside the new methods?
You are very close. The changeState
method belongs in the Context
class, not the State
class. Here's a good article on the topic. Note that the class diagram shows changeState
in the Document
(context) class.
To make it even cleaner, changeState
could take the next
state as a parameter. Like this:
class Context {
State state;
void method1() {
state.method1();
}
void changeState(next) {
state.exit();
this.state = next;
state.enter();
}
}
abstract class State {
Context context;
// other methods
}
class ConcreteState extends State {
void method1() {
// do something
context.changeState(AnotherConcreteState);
}
void enter() { /* do something */ }
void exit() { /* do something */ }
}
Now, as you add more methods to Context
, there is no duplication in Context
. It'd look like this:
class Context {
State state;
void method1() {
state.method1();
}
void method2() {
state.method2();
}
void changeState(next) {
state.exit();
this.state = next;
state.enter();
}
}
abstract class State {
Context context;
// other methods
}
class ConcreteState extends State {
void method1() {
// do something
context.changeState(AnotherConcreteState);
}
void method2() {
// do something else
context.changeState(YetAnotherConcreteState);
}
void enter() { /* do something */ }
void exit() { /* do something */ }
}