I'm following book from professor Subramaniam. In the book professor is trying to explain principle of Delegating Using Lambda Expressions.
We used lambda expressions and the strategy pattern to separate a concern from a method. We can also use them to separate a concern from a class. From a reuse point of view, delegation is a better design tool than inheritance. With delegation it’s easier to vary the implementation we rely on, and we can plug in a different behavior more dynamically. This can help vary the behavior of classes independent of the behavior of the parts they depend on, and make the design more flexible without forcing a deep class hierarchy
Here is a particular class with static method which does the required calculation/fetching of information.
public class YahooFinance {
public static BigDecimal getPrice(final String ticker) {
try {
final URL url = new URL("http://ichart.finance.yahoo.com/table.csv?s=" + ticker);
final BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
final String data = reader.lines().skip(1).findFirst().get();
final String[] dataItems = data.split(",");
return new BigDecimal(dataItems[dataItems.length - 1]);
} catch(Exception ex) {
throw new RuntimeException(ex);
}
}
}
Creating a Delegate: Rather than delegating part of the responsibility to another class, we can delegate it to lambda expressions and method references. This will further reduce class proliferation.
And here is the client code (i.e. Delegate):
public class CalculateNAV {
private Function<String, BigDecimal> priceFinder;
public CalculateNAV(final Function<String, BigDecimal> aPriceFinder) {
priceFinder = aPriceFinder;
}
public BigDecimal computeStockWorth(final String ticker, final int shares) {
return priceFinder.apply(ticker).multiply(BigDecimal.valueOf(shares));
}
//... other methods that use the priceFinder ...
public static void main(String[] args) {
final CalculateNAV calculateNav = new CalculateNAV(YahooFinance::getPrice);
System.out.println(String.format("100 shares of Google worth: $%.2f",
calculateNav.computeStockWorth("GOOG", 100)));
}
}
My domain problem is to certain extent very similar to what the professor is trying to do.
I have three entities in my Spring application (InvoiceTour, InvoiceLabTest, InvoicePenatly). They have different properties and are not related in any way, so no inheritance, no interface. But I do need to send them to some SOAP web service which will return same object type (InsertInvoiceResponse) for each of these entities. The SoapHelper class is much like YahooFinance.
public class SoapHelper {
public InsertInvoiceResponse getValueForTour(InvoiceTour invoiceTourEntity, Company company) {
//Some processing of passed parameters which results in InsertInvoiceRequest (requestPayload) object (stub for soap service)
return send(requestPayload);
}
public InsertInvoiceResponse getValueForLabTest(InvoiceLabTest invoiceLabTestEntity, Company company) {
//Some processing of passed parameters which results in InsertInvoiceRequest (requestPayload) object (stub for soap service)
return send(requestPayload);
}
public InsertInvoiceResponse getValueForPenalty(InvoicePenalty invoicePenalty Entity, Company company) {
//Some processing of passed parameters which results in InsertInvoiceRequest (requestPayload) object (stub for soap service)
return send(requestPayload);
}
}
Method send(requestPayload) is like this:
//Spring's abstraction for sending SOAP requests
public InsertInvoiceResponse send(InsertInvoiceRequest requestPayload) {
return (InsertInvoiceResponse) getWebServiceTemplate()
.marshalSendAndReceive("https://clienttesthorizon.horizonafs.com/AFSServices/AFSService.svc/basicHttpBinding",
requestPayload, new SoapActionCallback("http://tempuri.org/IAFSService/InsertInvoice"));
}
WHAT I DID?
First I created functional interface, like so:
@FunctionalInterface
public interface Executor<A,B,C> { //A - stands for any InvoiceXXX; B - Company parameter and C will be result of soap response (InsertInvoiceResponse)
C apply(A a, B b);
}
Next, I created a class which is responsible for invoking actual method implementation using method reference. The class SoapCaller is much like CalculateNAV. The SoapCaller will have private field of type Executor, but I would like to make it more generic. I do understand that interface is already generic by itself, but in lack of words I'm not sure how to express differently.
My idea is to be able to pass to constructor of SoapCaller something like this:
public class SoapCaller {
private Executor<A, B, C> exec;
public SoapCaller(final Executor<Class<T> t, Class<F> f, Class<E> e> execGeneric) {
this.exec = execGeneric;
}
public InsertInvoiceResponse callWebService(Class<T> t, Class<F> f) {
return exec.apply(t, f);
}
}
My client code, is supposed to look something like this:
public static void main(String[] args) {
InvoiceTour it = db.getInvoiceTour();
InvoiceLabTest ilt = db.getInvoiceLabTest();
InvoicePenalty ip = db.getInvoicePenalty();
Company c = db.getCompany();
SoapCaller soapCallerForInvoiceTour = new SoapCaller(SoapHelper::getValueForTour);
InsertInvoiceResponse invoiceTourResponse = soapCallerForInvoiceTour.callWebService(it, c);
//do what ever with invoiceTourResponse
SoapCaller soapCallerForInvoiceLabTest= new SoapCaller(SoapHelper::getValueForLabTest);
InsertInvoiceResponse invoiceLabTestResponse = soapCallerForInvoiceTour.callWebService(ilt, c);
//do what ever with invoiceLabTestResponse
}
Of course, there is tone of compilation errors. I'm not sure how to achieve to make functional interface more generic then it already is (if that makes any sense)? Does any have an idea how to make this more readable using lambdas and method references?