Is this a good use case of java 8's Stream:
final Car carWithInterior = Stream.of(carWithEngine)
.map(car -> installSeats.execute(car))
.map(car -> installDashBoard.execute(car))
.map(car -> installSunRoof.execute(car))
.collect(car);
carWithEngine
is a Car
. Note that sometimes even if the car (w/ dashboard) is pass to the installSunroof, it will not do any since there's no hole in the roof. I should always get a car at the end of the installation/mapping process.
The installation sequence is required (that's why I thought of streaming it) and next installation may sometimes need a parameter from the pass car instance to perform it's operation.
Is this a good use case for java 8's Stream?
Obvioulsy, my collect at the end is not right. How should I get the car at the end of this installation/assembly line? findFirst().get()
will work but I think that's bad, since I should always get a car at the end even if the installation didn't do anything with the carWitEngine
and I'm not stream multiple elements.
I'm not sure how cars are assembled but let say you need to put the engine first before adding the interiors for the sake of this analogy
Since you're looking to perform operations on a single object. Java 8 Optional would be appropriate.
The builder pattern (Phil C's comment) is definitely worth visiting if each of these map operations are simply hydrating the Car
object.
Car
gets dropped in the stream through a .map()
operation. When dealing with Streams
it is highly recommended to use these operations the way they are intended. If you plan on dropping an element from a stream .filter()
Edit to OP's edits:
There is some fundamental confusion around "sequence" and how that that translates
Optional
car => map(execute) => map(execute) => transformedcar
Streams
[car1, car2, car3]
=> map(execute) => map(execute) => updatedcar1 | => findFirst (Optional<Car>)
=> map(execute) => map(execute) => updatedcar2 |
=> map(execute) => map(execute) => updatedcar3 |
collect
[updatedcar1, updatedcar2, updatedcar3]
The findFirst
method will return an Optional
.
The collect
method will provide a terminal operation that lets you aggregate/reduce/group these results.
These map
operations transform the passed elements "sequentially". But a Stream
would be appropriate cases where you must deal with many or "sequence of" cars.
To revisit #2
You have the option of providing an alternative with Optional with orElse(car)
.
But this is where you have to be contentious of the side effects you are performing.
If the execute method is manipulating and returning the same object that was passed into it, there will be unintended consequences. I'll be using Optionals for example but same will apply for Streams.
private final Car car Optional.of(carWithEngine)
.map(installSunroof.execute) // <-- updatedCarWithSunroof
.map(installDashboard.execute) // <-- updatedCarWithSunroofAndDashboard
.orElse(carWithEngine) // <-- updatedCarWithSunroofAndDashboard
Answering your questions:
- Is this a good use case for java 8's Stream?
No, the Java Stream Api is meant to be use on collections. From here the definition of a stream is:
a sequence of elements from a source that supports aggregate operations
You have a single object that is not a sequence of elements, so the use of Stream make your code more difficult to understand. To answer question 2, you can do
findFirst().orElse(carWithEngine)
An alternative to your example is simply to nest the function calls, like this:
installSunRoof.execute(installDashBoard.execute(installSeats.execute(car)))
Another alternative will be to define a method install on the car that receives a BaseIinstaller and returns the modified car, something like this:
final Car carWithInterior = car.install(installSeats)
.install(installDashBoard)
.install(installSunRoof);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With