2

私は 4 種類のデータ オブジェクトがあるとします。

class DataTypeAlpha extends DataType
class DataTypeBeta extends DataType
class DataTypeGamma extends DataType
class DataTypeDelta extends DataType

GUI フレームワークからの 4 つの異なる TreeNode タイプで、それぞれがラップされた DataType に固有です。

class AlphaTreeNode extends MyAppTreeNode
...

現在、DataType のインスタンスがあり、MyAppTreeNode の新しいインスタンスが必要であるというパターンがよくあります。私は2つの解決策を見ています。解決策 1:

class DataType {
  // Instantiates and returns the appropriate MyAppTreeNode for this DataType
  abstract MyAppTreeNode createTreeNode();
}

解決策 2:

class MyAppTreeNode {
  static MyAppTreeNode createTreeNodeForDataType(DataType dataType) {
    if(dataType instanceOf DataTypeAlpha) return new AlphaTreeNode((DataTypeAlpha)dataType)
    else if (dataType instanceOf DataTypeBety) return new BetaTreeNode((DataTypeBeta)dataType)
    else if ...
    else if ...
    else throw new IllegalArgumentException();
  }
}

ソリューション 1 はポリモーフィズムを使用し、より短く、より「エレガント」です。しかし、DataType クラスには、私が使用している GUI フレームワークに関する知識がないようにしたいと思います。2 つの異なる GUI フレームワークを使用することもできますか?

3 つ目の解決策はありますか? この質問に Guice タグを追加しました。たぶん、ここで役立つ Guice または別の依存性注入ライブラリの機能があるでしょうか?

同様の質問に目を通す:

  • もちろん、これにはファクトリー パターンを使用しますが、ファクトリー内ではまだ疑問が残ります。
4

2 に答える 2

1

これには、訪問者にインスパイアされたアプローチを使用できます。いつものように、すべての DataType オブジェクトにはacceptメソッドがありますが、通常の訪問者パターンとは対照的に、子をトラバースせず、値を返します。混乱を避けるためacceptに、 のoperator代わりに に渡されたオブジェクトを呼び出しましょうvisitor。トリックは、ジェネリック型を作成acceptしてoperators返すことです。

したがって、コードはデータモデルで次のようになります

public abstract class DataType {
  public abstract <T> T accept(Operator<T> op);
}

public interface Operator<T> {
  T operateAlpha(DataTypeAlpha data);
  T operateBeta(DataTypeBeta data);
  ...
}

public class DataTypeAlpha extends DataType {
  public <T> T accept(Operator<T> op) {
    return op.operateAlpha(this);
  }
}
....

そしてGUIにはあなたが持っているでしょう

public class TreeNodeFactory implements Operator<MyAppTreeNode> {
    public MyAppTreeNode operateAlpha(DataTypeAlpha data) {
      return new AlphaTreeNode(data);
    }
    ...
 }

 public class MyAppTreeNode {
   static TreeNodeFactory factory = ...;
   static MyAppTreeNode createTreeNodeForDataType(DataType dataType) {
     return dataType.accept(factory);
   }       
 }
于 2013-06-21T10:29:46.777 に答える
0

したがって、簡潔で簡単な答えは、コンストラクターはそれ自身の型しか返せないということです。サブタイプも、他のクラスも、再利用されたインスタンスも、nullそのタイプの新しいインスタンスだけです。したがって、ここでコンストラクターの範囲外で動作するソリューションを探しています。最も簡単で最も一般的な回避策は、静的ファクトリ メソッド (通常はnewInstanceまたはという名前getInstance) を作成することです。このメソッドは、外側のクラスの新規または既存のインスタンスを返し、サブクラスを返すかnull、問題なく実行できます。

ソリューション1と2に関するあなたのポイントは有効です。データ型が UI を認識しないようにするのは素晴らしいことです。あなたの状況 (4 つの型のみ) では、おそらく解決策 2 を選択するでしょう。ツリーに型の混合物を配置する GUI ではかなり一般的な要件です。Bittenus のソリューションは、おそらくそれに値するものです。(この種のことを 1 回だけ行う必要がある場合は、処理するコードが大量になります。)

何らかの形で型の数が増えることを期待しているが、操作がまったく増えない場合、1 つの代替手段として、ポリモーフィックな作成を別の Factory に抽出することができます。これは次のようになります。

class MyAppTreeNode {
  interface Factory {
    MyAppTreeNode create(DataType type);
  }
}

class AlphaTreeNode extends MyAppTreeNode {
  static class Factory implements MyAppTreeNode.Factory {
    @Override public AlphaTreeNode create(DataType type) {
      // Remember, in an override your return types can be more-specific
      // but your parameter types can only be less-specific
      return new AlphaTreeNode((DataTypeAlpha) type);
    }
  }
}

次に、マップを作成するだけです(ただし、より良いセマンティクスのためにグアバの ImmutableMap を検討してください):

private static Map<Class<?>, MyAppTreeNode.Factory> factoryMap = new HashMap<>();
static {
  factoryMap.put(DataTypeAlpha.class, new AlphaTreeNode.Factory());
  // ...
}

public static createTreeNode(DataType type) {
  return factoryMap.get(type.getClass()).create(type);
}

価値以上のトラブル? おそらく、ほとんどの場合。しかし、おそらく Guice が得られる最高のものであることを心に留めておいてください。Guice には Factory 実装を自動生成する機能がありますが、何らかの方法でマッピングDataTypeする必要がありMyAppTreeNode.Factory、Map、条件付き、または Visitor に電力を供給する二重の間接化に存在する必要があります。パターン。

あなたがすでに持っている答えを支持するためだけに、これが役立つことを願っています!

于 2013-06-24T03:38:40.053 に答える