我想做的事情并不容易;我意识到这一点。但我认为有一些更好的方法,我做了什么。这是问题:我正在尝试编写一个泛型类来呈现一个存储可评估节点的树 - 例如,一个节点可以存储一个值,只是评估该值,或者它可能生成一个随机值,或者是对一些其他节点的操作 - 因此我说树,但事实是节点的内部状态是未知的(例如,节点可能有0,1,2或更多的节点字段为儿童,或一个孩子的ArrayList等)。当然,如果每个Node知道如何渲染自己,这不会是一个问题,但我试图避免这种情况。 (理想情况下,我希望能够通过更改渲染器将树渲染为字符串或OpenGL图形或其他任何东西)。哦,请不要问诸如“那会有什么好处?”之类的问题。因为我这样做是因为它看起来很有趣。
(到现在为止,我认为我至少可以让节点知道它们可以被渲染,并且能够决定渲染的逻辑结构,但这可能不会对此产生太大的影响)
这是我到目前为止所做的: 节点的接口
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;
}
}
二元运算符的通用类:
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;
}
}
整数加法的类:
/**
* 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);
}
}
答案 0 :(得分:0)
我知道你说你“试图避免这种情况”但我真的认为Node
应该有一个render()
方法。如果您不希望渲染代码在每个节点类型中,那么您可以执行以下操作:
public class ValueNode<T> implements Node<T> {
private static RenderMethod rendorMethod;
public static void setRendorMethod(RenderMethod rendorMethod) {
ValueNode.rendorMethod = rendorMethod;
}
...
public String render() {
rendorMethod(this);
}
你做这件事的方式会奏效,虽然我发现它不必要地复杂化了。一些评论:
renderer.addRenderMethod(IntegerAdditionNode.class, addRender)
使用值addRenderMethod
调用addRender
花了我一些时间才明白原因显而易见,因为add
在这里有多重含义。也许使用与Map
匹配的动词put更好。
虽然我理解需要,但在NodeToString.render()
内拨打addRender()
令人困惑。
Method是Java中的重载术语,它使RenderMethod
看起来像Java方法而不是渲染方式。 NodeRenderer
或Renderer
怎么样?
将NodeString
分配给变量renderer
只是很奇怪。它与addRender
或valueRender
非常不同,因为它不是RenderMethod
。也许它应该是?
在所有main
类中,我在main
方法中做的第一件事就是执行以下操作。这至少对我来说不那么复杂了:
public static void main(String[] args) {
new NodeString().doMain(args);
}
private void doMain(String[] args) {
...