在最近的一次讨论中,我和朋友不同意以下使用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的类),哪种方法更合适?
答案 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;
}
}
策略模式通常用于可以由类封装并提供给某些消费者的算法,只要它符合特定协议,可能不关心使用哪种算法。
委托模式更简单,更易于理解。您将某些操作/控制推迟到某个外部实体。