按照 Venkat Subramaniam 的书使用 lambda 表达式玩 Strategy Design Pattern?
Playing with Strategy Design Pattern using lambda expression by following Venkat Subramaniam's book?
我正在关注 Subramaniam 教授的 book。书中教授试图解释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
这是一个特定的 class 静态方法,它执行所需的 calculation/fetching 信息。
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.
这是客户端代码(即 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)));
}
}
我的领域问题在某种程度上与教授试图做的非常相似。
我的 Spring 应用程序中有三个实体(InvoiceTour
、InvoiceLabTest
、InvoicePenatly
)。它们具有不同的属性并且没有任何关系,因此没有继承,没有接口。但我确实需要将它们发送到某个 SOAP Web 服务,该服务将为这些实体中的每一个提供 return 相同的对象类型 (InsertInvoiceResponse
)。 SoapHelper
class 很像 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);
}
}
方法send(requestPayload)
是这样的:
//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"));
}
我做了什么?
首先我创建了功能接口,像这样:
@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);
}
接下来,我创建了一个class,它负责使用方法引用来调用实际的方法实现。 class SoapCaller
很像 CalculateNAV
。 SoapCaller
将具有 Executor
类型的私有字段,但我想让它更通用。我知道接口本身已经是通用的,但由于缺乏文字,我不确定如何表达不同。
我的想法是能够像这样传递给 SoapCaller
的构造函数:
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);
}
}
我的客户端代码应该如下所示:
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
}
当然有编译错误的语气。我不确定如何实现使功能接口比现在更通用(如果这有意义的话)?有没有人知道如何使用 lambda 和方法引用使它更具可读性?
如果我理解你的话,你希望 SoapCaller
(委托给 lambda 的 class)也是通用的,所以它可以定义如下:
class SoapCaller<T, U> {
private BiFunction<T, U, InsertInvoiceResponse> soapExecutor;
public SoapCaller(final BiFunction<T, U, InsertInvoiceResponse> soapExecutor) {
this.soapExecutor= soapExecutor;
}
public InsertInvoiceResponse callWebService(T t, U u) {
return soapExecutor.apply(t, u);
}
}
请注意,您可以使用 BiFunction
而不是定义您自己的功能接口。
那你就可以这样使用了:
SoapCaller<InvoiceTour, Company> soapCallerForInvoiceTour = new SoapCaller<>(SoapHelper::getValueForTour);
InsertInvoiceResponse invoiceTourResponse = soapCallerForInvoiceTour.callWebService(it, c);
//do what ever with invoiceTourResponse
SoapCaller<InvoiceLabTest, Company> soapCallerForInvoiceLabTest= new SoapCaller<>(SoapHelper::getValueForLabTest);
InsertInvoiceResponse invoiceLabTestResponse = soapCallerForInvoiceLabTest.callWebService(ilt, c);
你可以尝试使用通用参数,例如,如果你知道 Company
不会改变,那么你可以删除类型参数:
class SoapCaller<T> {
private BiFunction<T, Company, InsertInvoiceResponse> soapExecutor;
...
}
我正在关注 Subramaniam 教授的 book。书中教授试图解释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
这是一个特定的 class 静态方法,它执行所需的 calculation/fetching 信息。
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.
这是客户端代码(即 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)));
}
}
我的领域问题在某种程度上与教授试图做的非常相似。
我的 Spring 应用程序中有三个实体(InvoiceTour
、InvoiceLabTest
、InvoicePenatly
)。它们具有不同的属性并且没有任何关系,因此没有继承,没有接口。但我确实需要将它们发送到某个 SOAP Web 服务,该服务将为这些实体中的每一个提供 return 相同的对象类型 (InsertInvoiceResponse
)。 SoapHelper
class 很像 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);
}
}
方法send(requestPayload)
是这样的:
//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"));
}
我做了什么?
首先我创建了功能接口,像这样:
@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);
}
接下来,我创建了一个class,它负责使用方法引用来调用实际的方法实现。 class SoapCaller
很像 CalculateNAV
。 SoapCaller
将具有 Executor
类型的私有字段,但我想让它更通用。我知道接口本身已经是通用的,但由于缺乏文字,我不确定如何表达不同。
我的想法是能够像这样传递给 SoapCaller
的构造函数:
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);
}
}
我的客户端代码应该如下所示:
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
}
当然有编译错误的语气。我不确定如何实现使功能接口比现在更通用(如果这有意义的话)?有没有人知道如何使用 lambda 和方法引用使它更具可读性?
如果我理解你的话,你希望 SoapCaller
(委托给 lambda 的 class)也是通用的,所以它可以定义如下:
class SoapCaller<T, U> {
private BiFunction<T, U, InsertInvoiceResponse> soapExecutor;
public SoapCaller(final BiFunction<T, U, InsertInvoiceResponse> soapExecutor) {
this.soapExecutor= soapExecutor;
}
public InsertInvoiceResponse callWebService(T t, U u) {
return soapExecutor.apply(t, u);
}
}
请注意,您可以使用 BiFunction
而不是定义您自己的功能接口。
那你就可以这样使用了:
SoapCaller<InvoiceTour, Company> soapCallerForInvoiceTour = new SoapCaller<>(SoapHelper::getValueForTour);
InsertInvoiceResponse invoiceTourResponse = soapCallerForInvoiceTour.callWebService(it, c);
//do what ever with invoiceTourResponse
SoapCaller<InvoiceLabTest, Company> soapCallerForInvoiceLabTest= new SoapCaller<>(SoapHelper::getValueForLabTest);
InsertInvoiceResponse invoiceLabTestResponse = soapCallerForInvoiceLabTest.callWebService(ilt, c);
你可以尝试使用通用参数,例如,如果你知道 Company
不会改变,那么你可以删除类型参数:
class SoapCaller<T> {
private BiFunction<T, Company, InsertInvoiceResponse> soapExecutor;
...
}