Доступ к методам реализации конкретного объекта, который возвращается в его 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)
{
}
}