However this project is not that widely used.
I've chosen to experiment with Lombok due to it's popularity, thus many projects could introduce DCI using this approach right away.
So, how does it work?
Assuming the classic bank account example, with a simple object of type Account, then any public static method that takes as first parameter an object of type Account is a candidate for an extension method.
So, if I have a class MoneyTransferContext that contains a nested class called Account_Source (that will represent a role) as follows:
static class Account_Source
{
public static void transfer(Account thiz, Account destination, Double amount)
{
...
}
...
}
then placing the following annotation on the Context class:
@ExtensionMethod(MoneyTransferContext.Account_Source.class)
public class MoneyTransferContext
{
...
static class Account_Source ...
the result will be that we can do the following:
public void executeSourceToDestinationTransfer(
final Double amountToTransfer,
final Account source,
final Account destination)
{
source.transfer(destination, amountToTransfer);
}
So, the "source" object gains the extension method "transfer" that does not exist in the Account class.
Just to be precise, the annotation @ExtensionMethod specifies *where* a certain extension method will be used (they probably should have named it UsesExtensionMethod ...)
According to the Lombok docs: "Calls are rewritten to a call to the extension method", so there's no object wrapping - the identity is preserved.
What's nice about the way the Lombok folks implemented extension methods is that they do not apply globally, but only where they are specified (think of it as role-binding inside the Context only).
The complete example can be found here:
https://github.com/alexbalmus/dci_java_playground/tree/ext_method_lombok_approach Please note that I've also added two new marker annotations @DciContext and @DciRole just to better express intentionality.
***
Alternatively, I've also searched for ways to implement DCI in pure Java (w/o Lombok), and the best I could come up with is something similar to the approach taken by Andreas Söderlund for TypeScript and described in his tutorial series:
https://blog.encodeart.dev/series/dci-typescript-tutorial Andreas implemented DCI in TypeScript using nested (inner) functions and a naming convention. Something similar can be achieved in Java using a combination of a generic functional interface (i.e. interface containing a single abstract method) and a Lambda expression to basically achieve something that's almost like a reference to an anonymous inner function.
So, if we have a functional interface such as:
public interface RoleMethod<T>
{
void call(T t);
}
Then we can have something similar to JavaScript nested functions like this:
public void executeSourceToDestinationTransfer(
final Double amountToTransfer,
final A source,
final A destination)
{
//----- Role methods:
// Destination account:
RoleMethod<Double> destination_receive = (amount) ->
{
destination.increaseBalanceBy(amount);
};
// Source account:
RoleMethod<Double> source_transferToDestination = (amount) ->
{
if (source.getBalance() < amount)
{
throw new BalanceException(INSUFFICIENT_FUNDS); // Rollback.
}
source.decreaseBalanceBy(amount);
// equivalent of: destination.receive(amount):
destination_receive.call(amount);
};
//----- Interaction:
// equivalent of: source.transferToDestination(amount)
source_transferToDestination.call(amountToTransfer);
}
Notice the naming conventions and calling of the (almost) inner functions:
source_transferToDestination.call(amountToTransfer);
and
destination_receive.call(amount);
Alas, the Java syntax does not allow to just simply write source_transferToDestination(amountToTransfer); instead you need to include the .call(...).
This is as good as it gets, but if inner functions are good enough an option for DCI in TypeScript, I suppose this approach should also be viable for Java.
Full example here:
https://github.com/alexbalmus/dci_java_playground/tree/method_reference_approach ***
Let me know what you think.
Cheers!
Alex Balmuș