Information hiding and functional programming style
I was developing a simple class called Simulator
that applies a list Simulation
to a specific input. For each input, the simulation may or may not provide an output based on some conditions that the input must fulfill for each simulation. The result generated Simulator
is a list of outputs.
Here is the code.
class Simulator {
final List<Simulation> simulations;
// Some initialization code...
List<Ouput> execute(Input input) {
return simulations
.stream()
.filter(s -> s.processable(input))
.map(s -> s.prepareOutput(input))
.collect(Collectors.toList());
}
}
As you can see, I first check to see if the input is processed with by Simulation
filtering the simulations for which it isn't, and then applying those simulations to the input.
From an object oriented point of view, I expose the internals of the class Simulation
. The validation operation performed by the method processable
must be hidden inside the method prepareOutput
.
However, if processable
seen on Simulator
, I can take a more functional approach, which is very convenient.
Which approach is better? Are there any other solutions I am missing?
source to share
Since your class is Simulation
already showing an operation prepareOutput
that might fail, there is no additional exposure if you provide a method processable
for detecting in advance if an operation will be performed prepareOutput
. In fact, this is a good API design, offering this kind of validation as long as it is not too expensive to calculate in advance.
You may still want to consider doing a bulk processing operation in the class Simulation
.
public class Simulation {
public Output prepareOutput(Input input) {
…
}
public static List<Output> prepareWhenPossible(List<Simulation> list, Input input) {
return simulations.stream()
.filter(s -> s.processable(input))
.map(s -> s.prepareOutput(input))
.collect(Collectors.toList());
}
}
It is important to instruct the caller to skip items that cannot be operated on instead of performing an all-or-nothing behavior.
This still does not preclude exposure processable
if it is cheap to implement. It is not as if it were an impossible operation, as you can always simply call prepareOutput
and discard the result to see if the operation is possible. Having a method processable
for this purpose is much cleaner.
source to share
If you need to hide processable
, why not do it a little differently:
Optional<Output> prepareOutput(Input input) {
boolean isProcessable = processable(input); // processable is private
if(isProcessable){
// prepare Output
return Optional.of(Output);
}
return Optional.empty();
}
And here's something like this:
List<Ouput> execute(Input input) {
return simulations
.stream()
.map(s -> s.prepareOutput(input))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
source to share