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 UpdateableState
s.
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