Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Design pattern for working with state and inheritance

I will try to explain my problem on cars. I have AbstractCar and the users (developers) of my library will create many their ConcreteCars. This AbstractCar has state and this state is very important for right working of the library! Only the car can control its state (no any Drivers etc). The state changes in methods start/stop at the beginning and at the end of the methods. Besides all cars must implement interface Car.

public enum State{
   STARTING, STARTED, STOPPING, STOPPED
}

public interface Car{
   public void start();
   public void stop();
   public State getState();
}

I tried two variants.

Variant 1

public abstract class AbstractCar implements Car{
  private State state;
  public void setState(State state){...}
  public State getState(){...}
}

public class ConcreteCar extends AbstractCar{
  @Override
  public void start(){
     setState(stateK);
     ...
     setState(stateN);
  }

  @Override
  public void stop(){
     setState(stateR);
     ...
     setState(stateO);
  }
}

At variant 1 the user of the library will have to remember to change the state. If he forgets to do it, then there will be a bug in the code.

Variant 2

public abstract class AbstractCar implements Car{
  private State state;
  protected void doOnStart(){ }
  protected void doOnStop(){ }
  public final void start(){
    state=...;
    doOnStart();
    state=...;
  }
  public final void stop(){
    state=...;
    doOnStop();
    state=...;
  }
}

public class ConcreteCar extends AbstractCar{
  @Override
  protected void doOnStart(){
    ....
  }

  @Override
  protected void doOnStop(){
     ...
  }
}

In variant 2 the user can't forget about state because it is already outside his control, but if I have many states and many methods in which they can be changed this is not a very good way.

Could anyone advise any pattern or technologies how to solve such problem?

like image 753
Pavel_K Avatar asked Nov 09 '22 03:11

Pavel_K


1 Answers

If you want to retain full control over which state the car will be at a given moment and which transitions are allowed, the second approach is the basic pattern to use.

You may modify the way you call the subclass' code (be it by calling an abstract method, or some other kind of callback), but the basic pattern will be the same - your AbstractCar's code will contain the logic of states and transitions, with defined points where "external" code may be called. These are also sometimes referred to as "hooks".

A (perhaps a bit far-fetched) example of such approach is JSF life-cycle where the request goes through a complex workflow and in some given phases (e.g. validation) user-supplied code may be executed - but it has no way to directly set the state of the request.

If you want to allow your users (i.e. subclass authors) to be able to affect the car's state, you can do so in a controlled way by accepting a return value from the callback that affects the following state transition, or in some cases simply by doing proper error-handling:

public final void start(){
    state=STARTING;
    try {
        doOnStart();
        state=STARTED;
    } catch (RuntimeException e) {
        // handle error
        state=STOPPED;
    }
}
like image 118
Jiri Tousek Avatar answered Nov 14 '22 21:11

Jiri Tousek