2011-12-03 5 views
0

私がやっていることは簡単ではありません。私はそれを認識します。しかし、私は私が調理したものがより良い方法があると思います。問題は次のとおりです。評価可能なノードを格納するツリーをレンダリングするためのジェネリッククラスを作成しようとしています。たとえば、ノードが値を格納していて、その値に評価している、またはランダムな値を生成している、他の多くのノード上での操作です - したがって、私はツリーと言いますが、真実はノードの内部状態が不明であることです(例えば、NodeはChildrenのNodeフィールドを0,1,2以上持つかもしれません)。子のArrayListなど)。もちろん、各ノードがレンダリングする方法を知っていれば、これは問題ではありませんが、私はこれを避けようとしています。 (理想的には、レンダーを変更するだけで、ツリーを文字列や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; 
    } 

} 

A GEN二項演算子のための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; 
    } 

} 

整数加算のためのクラス:

/** 
* 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(); 
    } 
} 

そして最後に、新しいレンダリングオプションが可能になります例文字列レンダラークラスを動的に追加します。これは、しかし、非常に醜いだ、と私は考えか、私はこの1つはより良い行うことができますどのように右方向にだけ光のプッシュを本当に感謝:

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

それはうまくいかないでしょう - 新しいNode型を追加するたびに、私はビジターインターフェイスを変更する必要があります。ビジターは、データ構造を変更することなく操作を追加したい場合は素晴らしいですが、私の場合、新しい種類のデータ(この場合は新しいノード)を追加し、新しい操作(追加のレンダリング方法)を追加できるようにしたいと考えています。正直なところ、これを行う良い方法があるのか​​どうか分からない。 – 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)

    addRenderの値でaddRenderMethodを呼び出すには、addがここに複数の意味を持っているので、明白な理由のために理解することは私にしばらく時間がかかりました。たぶん、Mapと一致する動詞を使用する方が良いでしょう。

  2. addRender()の中からNodeToString.render()を呼び出すと、私は必要性を理解しましたが、混乱しました。

  3. メソッドはJavaのオーバーロードされた用語で、RenderMethodはレンダリングの方法ではないJavaメソッドのように見えます。 NodeRendererまたはRendererはどうですか?

  4. 変数rendererNodeStringを割り当てるのはちょっと変です。それはRenderMethodではないので、addRenderまたはvalueRenderとは非常に異なります。多分それはすべきでしょうか?

  5. すべてのmainクラスでは、mainメソッドで最初に行うことは、次のようにすることです。

    public static void main(String[] args) { 
        new NodeString().doMain(args); 
    } 
    private void doMain(String[] args) { 
        ... 
    
関連する問題