То, что я пытаюсь сделать, непросто; Я это понимаю. Но я думаю, что есть лучший способ, что я приготовил. Вот проблема: я пытаюсь написать общий класс для рендеринга дерева, в котором хранятся узлы, которые могут быть оценены - например, узел может хранить значение и просто оценивать это значение, или он может генерировать случайное значение, или быть операцией на множестве других узлов - следовательно, я говорю дерево, но правда в том, что внутреннее состояние узла неизвестно (например, узел может иметь 0,1,2 или более полей Node для детей, или ArrayList детей и т. д.). Конечно, это не было бы проблемой, если бы каждый узел знал, как сделать себя, но я стараюсь избегать этого. (В идеале, я хотел бы иметь возможность отображать Tree на String или как OpenGL-графику или что угодно, просто изменив Renderer). О, и, пожалуйста, не задавайте вопросов вроде: «Какая польза?» потому что я просто делаю это, потому что это казалось интересным.Обобщение рендеринга дерева неизвестных узлов
(Теперь я понял, что я мог бы по крайней мере, дать Узлов знания, которые они могут быть оказаны, и способность решить логическую структуру рендеринга, но это, вероятно, не было бы улучшить это слишком много)
Это то, что я до сих пор: интерфейс для узлов
public interface Node<T> {
T evaluate();
}
класс для значений узлов:
public class ValueNode<T> implements Node<T> {
private T value;
@Override
public T evaluate() {
return value;
}
public ValueNode(T value) {
this.value = value;
}
}
поколения Eral класс для бинарных операторов:
public abstract class BinaryOperator<A, B, T> implements Node<T> {
private Node<A> left;
private Node<B> right;
public BinaryOperator(Node<A> left, Node<B> right) {
this.left = left;
this.right = right;
}
public Node<A> getLeft() {
return left;
}
public Node<B> getRight() {
return right;
}
}
Класс для Integer Дополнительно:
/**
* I couldn't figure out a generic way to add 2 numbers,
* so I'm going with just Integer for now.
*
*/
public class IntegerAdditionNode extends BinaryOperator<Integer, Integer, Integer> {
public IntegerAdditionNode (Node<Integer> left, Node<Integer> right) {
super(left,right);
}
public Integer evaluate() {
return getLeft().evaluate() + getRight().evaluate();
}
}
И, наконец, пример строки визуализатор класс, который позволяет для новых вариантов рендеринга, которые будут добавлены динамически. Это очень некрасиво, хотя, и я был бы очень признателен идея или только легкий толчок в правильном направлении, как я мог бы сделать это один лучше:
import java.util.HashMap;
public class NodeToString {
public interface RenderMethod {
public <T extends Node<?>> String renderNode(T node);
}
public static void main(String[] args) {
//test
NodeToString renderer = new NodeToString();
RenderMethod addRender = new RenderMethod() {
private NodeToString render;
public RenderMethod addNodeToString(NodeToString render) {
this.render = render;
return this;
}
@Override
public <T extends Node<?>> String renderNode(T node) {
IntegerAdditionNode addNode = (IntegerAdditionNode) node;
return render.render(addNode.getLeft()) +"+"+render.render(addNode.getRight());
}
}.addNodeToString(renderer);
renderer.addRenderMethod(IntegerAdditionNode.class, addRender);
RenderMethod valueRender = new RenderMethod() {
@Override
public <T extends Node<?>> String renderNode(T node) {
return ((ValueNode<?>)node).evaluate().toString();
}
};
//I don't know why I have to cast here. But it doesn't compile
//if I don't.
renderer.addRenderMethod((Class<? extends Node<?>>) ValueNode.class,
valueRender);
Node<Integer> node = new IntegerAdditionNode(new ValueNode<Integer>(2),
new ValueNode<Integer>(3));
System.out.println(renderer.render(node));
}
private HashMap<Class<? extends Node<?>>, RenderMethod> renderMethods = new
HashMap<Class<? extends Node<?>>, NodeToString.RenderMethod>();
/**
* Renders a Node
* @param node
* @return
*/
public <T extends Node<?>> String render(T node) {
Class nodeType = node.getClass();
if(renderMethods.containsKey(nodeType)) {
return renderMethods.get(nodeType).renderNode(node);
} else {
throw new RuntimeException("Unknown Node Type");
}
}
/**
* This adds a rendering Method for a specific Node type to the Renderer
* @param nodeType
* @param method
*/
public void addRenderMethod(Class<? extends Node<?>> nodeType, RenderMethod method) {
renderMethods.put(nodeType, method);
}
}
Почему бы не попробовать шаблон посетителя? –
Это не сработает - каждый раз, когда я добавляю новый тип узла, мне придется изменить интерфейс посетителя. Посетитель замечательный, если вы хотите добавлять операции без изменения структуры данных, но в моем случае я хотел бы иметь возможность добавлять новые виды данных (в этом случае новые узлы) И добавлять новые операции (дополнительные методы визуализации). Честно говоря, я не уверен, что есть хороший способ сделать это. – Cubic
Вам нужно добавить средство визуализации где-нибудь, несмотря ни на что. –