在返回到其API的对象上访问特定于实现的方法

时间:2016-09-01 09:43:38

标签: java module api-design interface-design leaky-abstraction

首先让我从抽象的方式来解决问题:我有两种公共接口类型。其中一个包含接收另一个接口类型的至少两个实例的方法。该方法的实现取决于传递的对象的实现。

考虑以下公共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的一部分。这是一个实现细节。

所以问题是:如何在不泄漏API的实现细节的情况下实现connect方法?

这是我到目前为止所尝试的内容:

  • connect方法放在Node界面中并调用parent.connect(child)这使我可以访问父级的包装对象,但是孩子的包裹物仍然无法使用。

  • 假设传递的Node属于NodeImpl类型并使用向下转发。这对我来说似乎不对。可能还有其他Node实现。

  • 不要将包装好的对象放在节点中,而是使用将TreeImpl映射到Node的{​​{1}}中的地图} objects。这与上面基本相同。一旦Wrapped实例传递给没有关联映射的Node方法,它就会崩溃。

请注意,connect界面可能包含方法。但是,这对于这个问题并不重要。

另外,请注意我控制了两者:接口声明以及实现。

解决此问题的另一种尝试是将Node方法转换为connect接口中的addChild方法,并使Node接口通用:

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是公共API的一部分。应隐藏createNodeNodeImplWrapped是客户端代码。正如您所看到的,run必须对客户端可见,因此这仍然是一个泄漏的抽象。

1 个答案:

答案 0 :(得分:1)

如果connect方法需要访问每个节点中的Wrapped对象,这意味着NodeImpl只能连接到NodeImpl,因此无需使其复杂,添加方法addChild或连接到Node接口,在NodeImpl实现中你可以将参数下载到NodeImpl,如果存在类型不匹配,则可能抛出异常。

没有降级你可以使用泛型,但我认为简单的解决方案是向下投射

interface NodeConnector<T extends Node>
{
  void  connect(T parent,T child);
}


public abstract class AbstractNode implements Node
{
  @Override
  public void connect(Node node)
  {

    NodeConnector<Node> nodeConnector = getNodeConnector();
    nodeConnector.connect(this, node);
    Node parent = this;
  }

  protected abstract NodeConnector<Node> getNodeConnector();


}

class NodeImpl extends AbstractNode
{
  @SuppressWarnings("unchecked")
  protected NodeConnector<Node> getNodeConnector()
  {
    return (NodeConnector) new NodeConnectorImpl();
  }
}



class NodeConnectorImpl implements NodeConnector<NodeImpl>
{
  @Override
  public void connect(NodeImpl parent, NodeImpl child)
  {

  }
}