【发布时间】:2018-08-12 11:46:02
【问题描述】:
我对抽象工厂模式很好奇。从 Java 8 开始我们有了默认方法,这是否意味着我们可以将抽象类替换为接口?我可以看到的一个缺点是我们需要非静态/最终字段的情况。我们不能做它的接口。你能举几个例子(我列出的这个除外)什么时候老式工厂更有优势吗?
【问题讨论】:
标签: java design-patterns interface factory
我对抽象工厂模式很好奇。从 Java 8 开始我们有了默认方法,这是否意味着我们可以将抽象类替换为接口?我可以看到的一个缺点是我们需要非静态/最终字段的情况。我们不能做它的接口。你能举几个例子(我列出的这个除外)什么时候老式工厂更有优势吗?
【问题讨论】:
标签: java design-patterns interface factory
技术上你可以,但你不应该。
接口的默认实现是一个具有一些非常特定目的的工具 - 主要用于向很可能已由您无法控制的客户端实现的接口添加功能,或者为已重复实现的接口添加功能,默认实现将难以重新实现。
它们不打算作为抽象类的替代(甚至补充),当您从一些共同的父行为扩展时。
现在,就是说,抽象工厂模式 与Java 对abstract 关键字的使用关系不大。抽象工厂模式是关于隐藏(或抽象掉)给定客户端实际用于生成对象的具体工厂实现。工厂的工厂方法定义返回的可能是具体类、抽象类或接口。
所以,例如-
假设你有一些课程,GuiPainter。它有一个方法,#paintWindow。
在幕后,您已经介绍了一个 Window,它具有特定于操作系统的实现,例如 AppleWindow、AndroidWindow、UbunutuWindow(等等)。每个 Window 实现在构建方式上都略有不同。
一种方法是使用 AppleWindowFactory、AndroidWindowFactory、UbuntuWindowFactory(等等)构建 GuiPainter,并提供一种查找操作系统并决定使用哪个工厂的方法。然而,GuiPainter 真正想要的只是任何 Window 实例——它没有其他特定于操作系统的知识。
因此,我们引入了一个 WindowFactory,它返回一个 Window。 WindowFactory 是一个工厂,它拥有发现操作系统和决定使用哪个具体的 Window 工厂的知识——将责任从 GuiPainter 中抽象出来。
Window 本身可能是一个具有单一实现的具体类,只是基于操作系统的配置差异。它可能是具有特定于操作系统的实现(如 AppleWindow、AndroidWindow 等)的抽象类。它甚至可能是一个由工厂匿名实现的接口。 Window 是什么并没有改变客户端不再需要担心操作系统特定的废话来获得它想要的窗口。
这有意义吗?
【讨论】:
是的,这是一种广泛使用的技术。
你经常会偶然发现公共interfaces,例如:
public interface Cache<K, V> {
public Optional<V> get(K k);
public V put(K k, V v);
// ...
}
和隐藏(=package-private 或嵌套private)实现。
class SoftCache<K, V> implements Cache<K, V> {
private final Map<K, SoftReference<V> dataMap;
// ...
}
class WeakCache<K, V> implements Cache<K, V> {
private final Map<K, WeakReference<V> dataMap;
// ...
}
不需要多个实现,即使只有一个子类,该模式也是有效且完全适用的。
您的类使用您的缓存系统不关心实现细节。他们只关心暴露的行为,这可以通过interface 描述很好。
您在谈论 default 和 factory 方法,但它们确实彼此不同,我觉得您有点混淆了。
default 方法 were mostly added 因为如果你有 20 个实现 MyInterface 的类并且你在你的接口中添加一个方法,那么实现一个行为将是一件非常痛苦的工作(这通常在所有类中都是相同的) 在 20 个不同的地方。
我觉得Java 8/9+ 正在大力发展这种模式:interfaces 中的工厂方法。以 API Set.of(...)、Map.of(...) 等为例。
以java.util.Stream<T>为例。
您通常以这种方式使用它(用于对象):
private Stream<Element> stream;
或
private IntStream intStream;
您不关心当前拥有的元素是 Head 元素、OfRef 还是其他任何元素。
这些是隐藏的细节,您的代码无法访问。
然而,interface Stream<T> 确实暴露了工厂方法 of(以及其他):
/**
* Returns a sequential {@code Stream} containing a single element.
*
* @param t the single element
* @param <T> the type of stream elements
* @return a singleton sequential stream
*/
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
让您的 interface 公开工厂方法而不是 abstract 类并没有错,但归根结底,这完全取决于您以及您如何更舒服地做事。
还需要注意的是java 通常使用这种模式:
interface > abstract class > other classes
另见java.util.Collection<E> 及其子类。
【讨论】: