【问题标题】:Accessing implementation specific methods on an object that is returned to its API访问返回到其 API 的对象上的实现特定方法
【发布时间】: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) 这使我可以访问父对象的包装对象,但是子对象的包装对象仍然不可用。

  • 假设传递的NodeNodeImpl 类型并使用向下转换。这对我来说似乎是错误的。可能还有其他 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);
}

NodecreateNode 是公共 API 的一部分。 NodeImplWrapped 应该被隐藏。 run 是客户端代码。如您所见,NodeImpl 必须对客户端可见,因此这仍然是一个泄漏抽象。

【问题讨论】:

  • 我用您的新编辑编辑了答案

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


【解决方案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)
  {

  }
}

【讨论】:

  • 我同意这是最好的解决方案,但我假设 OP 无法控制所提供的接口。
  • OP说他已经尝试用一种新方法来编辑Node界面。所以我认为OP是Node接口的作者。
  • 啊,是的:“将连接方法放在节点接口中”等。然后将删除我的并为你的。
  • 感谢您的回答。事实上,我控制着接口声明。但是,这个解决方案仍然需要我向客户端公开 NodeImpl:在某些时候,我必须创建节点。然后这个工厂方法必须返回一个Node&lt;NodeImpl&gt;。我将在问题中添加一个示例。
  • 是的,我同意,这就是为什么我更喜欢简单的向下转换而不是这个解决方案。
猜你喜欢
  • 1970-01-01
  • 2021-01-18
  • 2016-08-07
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
相关资源
最近更新 更多