I was assigned to design a piece of software (in java) with projects and jobs where jobs each have a status. As we learned about the GoF-patterns, the State pattern seemed like an obvious choice to me, but now I'm having some troubles with the implementation.
In my current design I have a Job
-class to represent the job with a certain state and a Project
-class that has a list of jobs. To represent the four possible states (AVAILABLE
- UNAVAILABLE
- FINISHED
- FAILED
), I made an enum State
with the state dependent methods of job implemented in the enum.
The issues with the state-pattern I'm having are the following:
- In
Project
, I would like to have a method that returns allAVAILABLE
jobs of the project (and maybe even one that returns all jobs that are eitherFINISHED
orFAILED
), but the state pattern doesn't mention on how to do this. We also got to hear that having methods aspublic boolean isAvailable()
inJob
orState
should not be used when using the state pattern (because this is exactly what one wants to avoid when using this pattern). - There is need for a method that changes the state from an
AVAILABLE
job to eitherFINISHED
orFAILED
. The problem with this is that I'm not really sure on how to implement this when using the State pattern.
I have some solutions in mind, but I would like to know whether there are other/better solutions to implement the state pattern well.
- For the first problem, I would implement a
public Collection<Job> filter(Collection<Job>)
method inState
that would return the subset of the given set with only those jobs that are of the current state. ThegetAvailableJobs()
method inProject
would be like below. The disadvantage of this approach for me is that coupling grows whenProject
needs to know aboutState
as well. - For the second problem I could either write a
update(State newState)
method where I could check whethernewState
is either finished or failed or I could write a methodfail()
and a methodfinish()
(both inState
of course). The problem with the second seems to be the fact that some of the state logic gets back in theJob
, but I'm not sure whether theupdate
method is the best method either.
I hope I have been clear enough and that someone can help me understand why these solutions would be good enough or bad and what possible alternatives might be.
Thanks in advance.
Some extra code to make some more things clear:
public class Project {
private final Set<Job> jobs;
// constructors, getters, setters, ...
// for updating, I need to be able to present only the available tasks
public Collection<Job> getAvailableJobs() {
return Status.AVAILABLE.filter(getJobs());
}
}
public class Job {
private final State state;
// Constructors, getters, setters, ...
// update either to finished or failed
// (Timespan is nothing more than a pair of times)
public void update(Timespan span, State newState) {
getState().update(this, span, newState);
}
}
public enum State {
AVAILABLE {
@Override
public void update(Job job, Timespan span, State newState) {
if(newState == AVAILABLE || newState == UNAVAILABLE)
throw new IllegalArgumentException();
job.setSpan(span);
job.setState(newState);
}
},
UNAVAILABLE, FINISHED, FAILED;
public void update(Job job, State newState) {
throw new IllegalStateException();
}
public Collection<Job> filter(Collection<Job> jobs) {
Collection<Job> result = new HashSet<>();
for(Job j : jobs)
if(j.getStatus() == this)
result.add(j);
return result;
}
}