java - 遵循 Venkat Subramaniam 的书,使用 lambda 表达式来玩策略设计模式?

标签 java generics lambda stream method-reference

我正在关注book来自 Subramaniam 教授。书中教授试图解释使用 Lambda 表达式进行委托(delegate)的原理。

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

这是一个具有静态方法的特定类,它执行所需的计算/获取信息。

 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 应用程序中有三个实体(InvoiceTourInvoiceLabTestInvoicePenatly)。它们具有不同的属性并且不以任何方式相关,因此没有继承,没有接口(interface)。但我确实需要将它们发送到某个 SOAP Web 服务,该服务将为每个实体返回相同的对象类型 (InsertInvoiceResponse)。 SoapHelper 类非常类似于 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);
}

接下来,我创建了一个类,负责使用方法引用调用实际的方法实现。 SoapCaller 类与 CalculateNAV 非常相似。 SoapCaller 将具有 Executor 类型的私有(private)字段,但我想让它更通用。我确实知道界面本身已经是通用的,但由于缺乏语言,我不确定如何以不同的方式表达。

我的想法是能够传递给 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
    
}

当然,还有编译错误的提示音。我不知道如何实现使功能接口(interface)更加通用(如果这有意义的话)?有谁知道如何使用 lambda 和方法引用使其更具可读性吗?

最佳答案

如果我很理解您的意思,您也希望 SoapCaller (委托(delegate)给 lambda 的类)是通用的,因此可以将其定义如下:

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而不是定义您自己的功能接口(interface)。

然后您可以按如下方式使用它:

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;

    ...
}

关于java - 遵循 Venkat Subramaniam 的书,使用 lambda 表达式来玩策略设计模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66547208/

相关文章:

Scala 到 Java8 流兼容性问题

java - 在 Java 中将数据表示为图表

java - NotificationManager 和 NotificationManagerCompat 之间有什么区别?

java - 如何在android中随机设置文本颜色?

c# - 作为协变类型参数,类和结构之间存在差异

lambda - 从 lambda 调用方法,该 lambda 是单元测试下系统的一部分

java - USB 串行驱动程序 Java 插件在 Unity Android 版本中无法工作

c# - 在不同的泛型类实例之间共享静态字段

java - 通用工厂方法约定

linq - LINQ查询和Lambda表达式之间的区别