使用一系列lambda函数来定义类功能是否合适?

时间:2016-01-15 23:34:29

标签: java oop lambda java-8 readability

在最近的一次讨论中,我和朋友不同意以下使用lambda函数来定义类功能。使用动态值创建对象时,是应该使用lambdas传递动态值,还是使用自定义子类中的overriden方法提供动态值?

考虑以下示例,其中的目标是使用具有动态文本和图标特征的自定义标签组件。此标签必须包含方法getText()getIcon()。以下是两个示例:一个使用多个lambdas,另一个定义子类。

Lambda方法:

class Label {
    private Supplier<String> text;
    private Supplier<Image> icon;

    public Label(Supplier<String> text, Supplier<Image> icon) {
        this.text = text;
        this.icon = icon;
    }

    public String getText() {
        return text.get();
    }

    public Image getIcon() {
        return icon.get();
    }
}

使用:

Label timeLabel = new Label(
    () -> System.currentTimeMillis(),
    () -> CLOCK_IMAGE
);

子类方法:

class Label {
    private String text;
    private Image icon;

    public Label() {
        // some default
    }

    public Label(String text, Image icon) {
        // set fields
    }

    // simple getters
}

使用:

class TimeLabel extends Label {
    public String getText() {
        return System.currentTimeMillis();
    }

    public Image getImage() {
        return CLOCK_IMAGE;
    }
}

Label timeLabel = new TimeLabel();

考虑到内部和外部开发人员的期望,可读性和可用性(包括作为API的类),哪种方法更合适?

4 个答案:

答案 0 :(得分:2)

Lambdas有它们的用途,但这很可能过度使用。注意:当您创建lambda时,您必须构建一个关于您正在使用哪些参数的假设。例如,您无法在外部定义使用受保护字段的lambda。

Lambdas在CPU /内存和概念开销方面也带来了一些开销。它们是在运行时生成的新类,如果您正在分析或调试它,那么工具只能帮助您解释代码。

答案 1 :(得分:0)

这两个例子并不相同。第一个示例使用供应商推迟创建实际内容,直到调用getter并在每次调用时重复创建内容。在第二个示例中,抽象类只存储数据。

哪个更好取决于您使用它的上下文。如果您希望重新连续生成数据,那么第一个示例是最好的。它满足大多数Solid Principles。如果您只想保存数据,那么第二个示例更好。但是,第二个示例在Open/Closed原则和Liskof原则上存在严重问题。

通过使Label成为一个接口并让特定的实现为他们做最好的事情,你会更好。您确实希望隐藏API用户的这些详细信息。你可以在静态构造函数的外观后面做到这一点。

例如:

public interface Label {

  public String getText();

}

public class Labels {

   public static Label createCurrentTimeLabel(){
       return new Label{

          public String getText() {
            return System.currentTimeMillis();
          }

       } 


   }

}

答案 2 :(得分:0)

您是否需要对方法体进行动态运行时重新配置?使用lambdas。否则,使用具有巨大运行时性能优势的子类,在搜索用法时更容易(找到get()的所有可能的调用位置,并且更加惯用)。

答案 3 :(得分:0)

这里的lambda方法本质上是策略或委托模式,并且还允许您更喜欢组合而不是继承,这可以很好地与依赖注入(这可以帮助使测试更容易等等)。

我看到的一个缺点是,在这里使用的lambdas可能会错过你可以通过在他们自己的类中定义这些而不是在实例化Label的地方定义它们来实现的潜在封装,这就是你朋友的继承模式做得更好。

然而,你朋友的模特错过了你可能想要的策略模式。

一个很好的妥协可能是忘记lambdas,但保持战略的想法。而且,由于您正在消除lambdas,因此您可以将策略实现的定义扩展到get()方法之外。但是你必须决定是否需要为每个房产制定个人策略。

class Label {
    private LabelSupplier supplier;

    public Label(LabelSupplier supplier) {
        this.supplier = supplier;
    }

    public String getText() {
        return supplier.getText();
    }

    public Image getIcon() {
        return supplier.getIcon();
    }
}

interface LabelSupplier {
    String getText();
    Image getIcon();
}

class TimeLabelSupplier implements LabelSupplier {
    public String getText() {
        return System.currentTimeMillis();
    }

    public Image getImage() {
        return CLOCK_IMAGE;
    }
}

策略模式通常用于可以由类封装并提供给某些消费者的算法,只要它符合特定协议,可能不关心使用哪种算法。

委托模式更简单,更易于理解。您将某些操作/控制推迟到某个外部实体。