You can download the binary here.
The API-documentation is hosted here.
And the sources are also available here.
Now what's different when you use that framework? I'd say the difference is that the dip-framework follows good OO design principles, like the open-closed-principle that says: "A module should be open for extension but closed for modification." In other words I have seperated concerns in a fork and join scenario to make the whole more flexible and easy to change.
In my last blog I presented a code snippet that illustrated how to use plain fork and join to calculate offers of car insurances. Let's see how this can be done using my dip-framwork.
The input to the proposal calculation is - well - a list of proposals :-) In the dip framework you wrap the input of a
ForkJoinTaskinto a subclass of
DecomposableInput. The name originates from the fact that input to
ForkJoinTaskis decomposable. Here is the snippet:
The class wraps the raw input to
ForkJoinTaskand provides a method how that input can be decomposed. Also, it provides a method
computeDirectly()that can decide on whether this input needs further decomposition to be small enough for direct computation.
The output of proposal calculation is a list of maps of prices. If you have four input proposals, you'll get a list of four maps with various prices. In the dip framework, you wrap the output into a subclass of
The class implements the
composemethod that can compose an atomic result of a computation into the existing raw result. It returns a
ComposableResultinstance that holds the new composition.
I agree it's a little abtsract. Not only that concurrency is inherently complex. I am also putting another abstraction onto it. But once you've used the framework you'll realize the strength. So stay tuned, we're almost finnished :-)
Now, you have an input and an ouptut and the last thing you need is a computation object. In my example that's the pricing engine. To connect the pricing engine to the dip framework, you'll need to implement a subclass of
The PricingEngineBridge implements the
computemethod that calls the pricing engine. It translates the
DecomposableInputinto an input that the pricing engine accepts. And it creates an instance of
ComposableResultthat contains the output of the pricing engine.
Last thing to do is to get the stuff started.
The example creates an instance of
GenericRecursiveTaskand passes the
ListOfProposalsas well as the
PricingEngineBrigeas input. If you pass that to the
ForkJoinPoolthen you receive an instance of
What's the advantage when you use the dip-framework? For instance:
- you could pass arbitrary processing input to
GenericRecursiveTaskby implementing a subclass of
- you could implement your own custom
RecursiveTaskthe same way I have implemented
GenericRecursiveTaskand pass the proposals and the
PricingEngineBridgeto that task
- you could implement a custom
ForkAndJoinProcessorand use that by subclassing
GenericRecursiveTask: that way you can control the creation of subtask and their distribution across threads
- you could exchange the processing activity (here:
PricingEngineBridge) by implementing a custom
ComputationActivityBridgeand try alternative pricing engines or make something completely different then calculating prices ...
I think I have made my point: the whole is closed for modification, but open for extention now.
The complete example code is here in my git repository.
Let me know if you like it. Looking forward to critical and enjoyable comments.