2011-12-03 3 views
0

То, что я пытаюсь сделать, непросто; Я это понимаю. Но я думаю, что есть лучший способ, что я приготовил. Вот проблема: я пытаюсь написать общий класс для рендеринга дерева, в котором хранятся узлы, которые могут быть оценены - например, узел может хранить значение и просто оценивать это значение, или он может генерировать случайное значение, или быть операцией на множестве других узлов - следовательно, я говорю дерево, но правда в том, что внутреннее состояние узла неизвестно (например, узел может иметь 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); 
    } 
} 
+1

Почему бы не попробовать шаблон посетителя? –

+0

Это не сработает - каждый раз, когда я добавляю новый тип узла, мне придется изменить интерфейс посетителя. Посетитель замечательный, если вы хотите добавлять операции без изменения структуры данных, но в моем случае я хотел бы иметь возможность добавлять новые виды данных (в этом случае новые узлы) И добавлять новые операции (дополнительные методы визуализации). Честно говоря, я не уверен, что есть хороший способ сделать это. – Cubic

+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); 
    } 

То, как вы делаете это будет работать, хотя я нашел, что это излишне сложным. Некоторые комментарии:

  1. renderer.addRenderMethod(IntegerAdditionNode.class, addRender)

    Вызов addRenderMethod со значением addRender мне потребовалось некоторое время, чтобы понять, по понятным причинам, так как add имеет несколько значений здесь. Возможно, использование глагола put, который соответствует Map, лучше.

  2. Вызов NodeToString.render() с вашего addRender() был запутанным, хотя я понимаю необходимость.

  3. Метод - это перегруженный термин в Java, который делает RenderMethod похожим на метод Java, а не способ рендеринга. Как насчет NodeRenderer или просто Renderer?

  4. Присвоение NodeString переменной renderer просто странно. Он сильно отличается от addRender или valueRender, поскольку он не является RenderMethod. Может быть, так и должно быть?

  5. Все классы main, первое, что я делаю в методе main, - это сделать что-то вроде следующего. Это делает его менее сложным для меня, по крайней мере:

    public static void main(String[] args) { 
        new NodeString().doMain(args); 
    } 
    private void doMain(String[] args) { 
        ... 
    
Смежные вопросы