Let's say that I have a class called Mission
. This class is intended to represent a mission requested by a professor working in a faculty. Mission
has a private enum field representing whether the mission is PLANNED
, IN_PROGRESS
, COMPLETED
, CANCELED
, ABORTED
, etc.
There are some things that the specs require to be doable upon a Mission
that only make sense depending on the current state of the mission. For instance, it would only make sense to requestReimbursement
for a mission that is completed. On the other hand, it doesn't make sense to cancel
a mission that is completed. Having all these methods inside the Mission
class and throwing an exception whenever the wrong method is called doesn't seem like a clean approach.
An alternative solution could be to create a subclass for each value of the mission states. This can be done by making the class Mission
abstract while holding a reference to a data class representing other information about the mission. Each subclass would have a method that represents the transition between the current state and and the one that follows. For instance:
public enum MissionState {PLANNED, IN_PROGRESS, ABORTED, COMPLETED, ...}
public abstract class Mission {
private MissionDetails details;
private MissionState state;
}
public class PlannedMission extends Mission {
public PlannedMission(MissionDetails details) {
super.details = details;
super.state = MissionState.PLANNED;
}
public InProgressMission start() {
return new InProgressMission(super.details);
}
}
public class InProgressMission extends Mission {
public InProgressMission(MissionDetails details) {
super.details = details;
super.state = MissionState.IN_PROGRESS;
}
public AbortedMission abort() {
return new AbortedMission(super.details);
}
public CompletedMission complete() {
return new CompletedMission(super.details);
};
}
public class CompeltedMission extends Mission {
public CompletedMission(MissionDetails details) {
super.details = details;
super.state = MissionState.COMPLETED;
}
public void requestReimbursement() {
//
}
}
While this approach manages to avoid the problem mentioned above, it's significantly more complex, and might not scale well if the specs were to change to include additional mission states.
The kind of system I'm trying to design seems very prevalent and I myself have interacted with multiple similar solutions as an end user, so I was wondering if there is a standard, clean approach to go about solving this problem.