【发布时间】:2017-01-09 03:14:01
【问题描述】:
让我从抽象地表述问题开始:我有两种公共接口类型。其中一个包含一个方法,该方法至少接收另一个接口类型的两个实例。方法的实现依赖于传递的对象的实现。
考虑以下公共 API,它由两个接口组成:
public interface Node {
}
public interface Tree {
void connect(Node parent, Node child);
}
现在,我想实现该 API,如下所示:
public class NodeImpl implements Node {
private final Wrapped wrapped;
public NodeImpl(Wrapped wrapped) {
this.wrapped = wrapped;
}
public Wrapped getWrapped() {
return wrapped;
}
}
public class TreeImpl implements Tree {
@Override
public void connect(Node parent, Node child) {
// connect parent and child using the wrapped object
}
}
public class Wrapped {
// wrapped object which actually represents the node internally
}
我需要访问connect 方法中的包装对象,这是不可能的,因为getWrapped 方法不是API 的一部分。这是一个实现细节。
所以问题是:如何实现connect 方法而不将实现细节泄露给API?
这是我目前尝试过的:
将
connect方法放入Node接口并调用parent.connect(child)。 这使我可以访问父对象的包装对象,但是子对象的包装对象仍然不可用。假设传递的
Node是NodeImpl类型并使用向下转换。这对我来说似乎是错误的。可能还有其他Node实现。不要将包裹的对象放在节点中,而是在
TreeImpl中使用一个映射,将Node映射到Wrapped对象。 这基本上是和上面一样。只要将Node实例传递给没有关联映射的connect方法,它就会崩溃。
请注意,Node 接口可能包含方法。但是,这对于这个问题并不重要。
另外,请注意,我同时控制:接口声明和实现。
解决此问题的另一种尝试是将connect 方法转换为Node 接口中的addChild 方法并使Node 接口通用:
public interface Node<T extends Node<T>> {
void addChild(Node<T> child);
}
public class NodeImpl implements Node<NodeImpl> {
private final Wrapped wrapped;
public NodeImpl(Wrapped wrapped) {
this.wrapped = wrapped;
}
public Wrapped getWrapped() {
return wrapped;
}
@Override
public void addChild(Node<NodeImpl> child) {
}
}
public class Wrapped {
// wrapped object which actually represents the node internally
}
public Node<NodeImpl> createNode() {
return new NodeImpl(new Wrapped());
}
private void run() {
Node<NodeImpl> parent = createNode();
Node<NodeImpl> child = createNode();
parent.addChild(child);
}
Node 和 createNode 是公共 API 的一部分。 NodeImpl 和 Wrapped 应该被隐藏。 run 是客户端代码。如您所见,NodeImpl 必须对客户端可见,因此这仍然是一个泄漏抽象。
【问题讨论】:
-
我用您的新编辑编辑了答案
标签: java module api-design interface-design leaky-abstraction