jeudi 29 décembre 2016

Using Java Generics for Code Reuse in Interface Inheritance

I am trying to create a framework for state machines using object oriented techniques in Java. Every System can have one and only one State active at a time.

public interface State {}
public interface System { 
    State currentState();
}

Let's say I want to add a new type of system that should be updated regularly to check and change its state, called an UpdateableSystem. It can only have States that are of the type UpdateableState.

public interface UpdateableState extends State {
    /**
     * Check to see if the system should switch to a different state.
     * @returns new state of the system
     */
    UpdateableState update();
}

public interface UpdateableSystem extends System {
    @Override
    UpdateableState currentState();
    /** 
     * Runs update method of the current state, or otherwise updates the system.
     * @returns the current state of the system after the update
     */
    UpdateableState update();
}

I overrode the currentState method to only return only UpdateableState, not State, because the UpdateableSystem can only have states that are of the type UpdateableState, and the client using the UpdateableSystem will expect UpdateableStates.

To avoid overriding many methods many times as more subclasses are created, generics seem to be the solution. Here are the updated interfaces.

public interface System<SystemState extends State> {
    SystemState currentState();
}

and

public interface UpdateableState<SystemState extends UpdateableState<SystemState>> extends State {
    SystemState update();
}

public interface UpdateableSystem<SystemState extends UpdateableState<SystemState>> extends System<SystemState> {
    SystemState update();
}

Now, lets say I wanted to add another type, interface Context, that systems and states need to be aware of. Context should be able to be subtyped, and clients and ContextState subtypes should be able to use the full interface of that Context subtype.

public interface Context {}
public interface ContextState<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableState<SystemState> {
    SystemState updateWithContext(SystemContext context);
}
public interface ContextSystem<SystemContext extends Context, SystemState extends ContextState<SystemContext, SystemState>> extends UpdateableSystem<SystemState> {
    // Some methods here that require SystemContext type
}

Suddenly, those parameterized types turned into a whole ton of boilerplate, which is confusing and hard to maintain.

This would be an example with concrete implementations:

class CarContext implements Context {...}
interface CarState extends ContextState<CarContext, CarState> {...}
    class CarDrivingState implements CarState {}
    class CarIdleState implements CarState {}
class Car implements ContextSystem<CarContext, CarState> {...}

Each concrete CarState would be able to access the additional methods provided by the CarContext interface, because the method signatures inherited from ContextState would be parameterized by the CarContext type. So no explicit casting would be required on the context parameter of updateWithContext.

Ultimately, the issue is too much boilerplate when adding an additional type to parameterize, and I do not know of a design alternative that would achieve code reuse through inheritance, and also maintain a strong type system, avoiding explicit casting when possible. Any suggestions to improve this design?

Aucun commentaire:

Enregistrer un commentaire