Доступ к методам реализации конкретного объекта, который возвращается в его API

Позвольте мне начать с абстрактной формулировки проблемы: у меня есть два открытых типа интерфейса. Один из них содержит метод, который получает как минимум два экземпляра другого типа интерфейса. Реализация метода зависит от реализации переданных объектов.

Рассмотрим следующий открытый 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 метод к 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 а также createNode являются частью общедоступного API. NodeImpl а также Wrapped должен быть скрыт. run это код клиента. Как вы видете, NodeImpl должен быть виден клиенту, так что это все еще утечка абстракции.

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)
  {

  }
}
Другие вопросы по тегам