2011-01-19 9 views
1

私は現在、シミュレーションプロジェクトのクラス階層を設計中です。これは、Elementインスタンスのツリーの離散事象シミュレーションになります。簡潔にするために(そして、私が問題のジェネリックの部分だけに興味があるので)ここではクラス/インタフェースのスケッチをここに提示するので、構文は完璧ではありません。始まるために何かを持っているために、要素は次のようになります。コンテナをジェネリックで制限する

public abstract class Element { 
    private Set<Element> children = new HashSet<Element>(); 
    public Element getContainer(); 
    public Collection<Element> getChildren() { 
     return Collections.unmodifiableSet(children); 
    } 
    public Position getPosition(); 
    protected void add(Element e) { children.add(e); } 
    protected void remove(Element e) {children.remove(e); } 
} 

Positionの定義は本当に問題ではありませんし、どのような他の方法を行うことになっていることは自明私は願っています。

public abstract class Solid extends Element { 
    public Size getSize(); 
} 

は再び、Sizeの正確な意味は重要ではありません、Solidはちょうどシミュレートされた世界で空間を満たす物理的なオブジェクトであるために仮定されています。私たちはプールに別のインターフェイスを投げる事が穏やかに面白くするために。今、私たちはここに、私たちは互いの上にそれらをスタックすることができます固形物を持っていることをStackです:トラブルが始まる

public abstract class Stack extends Solid { 
    public void add(Solid s); // <- trouble ahead! 
} 

そして、ここです。私は本当にSolidのインスタンスだけをStackに追加したいと思っています。サイズがないオブジェクトを積み重ねるのは難しいため、StackにはSizeというものが必要です。したがって、私はこれを表現できるように、私のElementをリワーク:ここ

public abstract class Element<E extends Element<?>> { 
    private final Set<E> children = new HashSet<E>(); 
    public Element<?> getContainer(); 
    public Collection<E> getChildren(); 
    public Position getPosition(); 
    public void add(E e); 
    public void remove(E e); 
} 

私の意図はElementは、それが含まれている可能性がある(サブ)Elementの種類に制約を持つことができることを表明することにあります。これまでのところ、これはすべて機能します。しかし今は、コンテナの制限をElementにする必要があると感じました。それはどう?

public interface Element<E extends Element<?,?>, C extends Element<?, ?>> 

それとも、もっとこのように行くん:にそこに持って

  • public interface Element<E extends Element<?,C>, C extends Element<E, ?>> 
    

    私はこれについて少しファジーを感じるように始めている、そしてゲームへのより多くのがあります要素をあるコンテナから別のコンテナに転送する「ポート」(InputおよびOutput)である必要があります。私はジェネリックの魔法を投げなければなりません。

  • 私のToDoリストにモデル用のGUIエディタがあります。だから私は実行時にもこれらの小切手を利用可能にする必要があります。ですから、タイプリテラルなどがあり、public boolean canAdd(Element<?, ?> e)のようなエディタが頼りになるでしょう。このメソッドは非常に重要なので、私はそれをのクラスのメソッドにしたいと思っています。だから、酔っている開発者は午前4時に間違ってしまうことはありません。
  • サブクラスがElementなら、のいずれかから完全一致と思われるので、Collectionクラスを使用していることを決定できます。
    • は私が再発明し、ホイールを午前:

    だからここに私の質問ですか?私にはどのライブラリがそれをしていますか?

  • これにジェネリックで取り組むことができますか? (答えが「はい」の場合:実装に関するいくつかのヒントは大歓迎です)
  • これは少し過剰に設計されていますか?
  • この質問には、より良いタイトルまたはタグがありますか? :-)
  • 私はあなたのデザインの私の理解でこの答えを基づかてる
+0

ここにあなたの考えがあります。 javaのジェネリックの目的はClassCastExceptionを防ぐことです。フルストップ。それは彼らが穏やかなやり方でする唯一のことです。あなたが何か他のもののためにそれらを使用しようとしているなら、その方法は狂気にあります。 – Affe

+0

"そのやり方は狂気にある"と言っていました。私はタイプ消去を理解する前に、ジェネリックであらゆる種類のワイルド・トリックを試みていましたが、ほとんどの場合、ジェネリックの創造的な使用は失敗することになりました。要素のように本当に狂ったように見えたら、Cは要素を拡張します>あなたはそれらを使うことに懐疑的でなければなりません。これらの用途を概念化することは難しいです。 –

+0

@Affe私がここでやろうとしていることの目的は、最終的にCCEの防止につながります。 Stackについて考えてみましょう。これは、getSize()を持つElementsでしか動作しません。つまり、Solidです。したがって、これを表現するためにジェネリックスを使用すると、コンパイラはそれが正常であることを確認します。それ以外の場合はキャストして、そのキャストが有効かどうかを確認する別の方法を見つける必要があります。 – Waldheinz

答えて

1

は、私はまだあなたが何をしようとしてのように少しあいまいだけど、あなたはその論理的結論にこれですべての方法を実行したい場合、私はこのような何か見ている:

/** 
@param P parent 
@param C child 
@param S self 
*/ 
interface Element<P extends Element<?, S, P>, 
     C extends Element<S, ?, C> , 
     S extends Element<P, C, S>> { 
    public P getParent(); 
    public Collection<C> getChildren(); 
} 

私はあなたのすべての制約を表現しています:私の親は私を子供として持つことができるものでなければならず、私の子供は私を親として持つことができるものでなければなりません。

実装が含まれます:

/** An agglomeration of stacks: no parent */ 
class Agglomeration extends Element<?, Stack, Agglomeration> {…} 

/** A stack of solids */ 
class Stack extends Element<Agglomeration, Solid, Stack> {…} 

/** A solid in a stack: no children */ 
class Solid extends Element<Stack, ?, Solid> {…} 

を私も、今のJava IDEを開いていないので、私はこれを確認することはできません実際にコンパイルします - 私は、コンパイラが文句を言うだろうという気持ちを持っていますこれらの疑問符について、あなたは別々のインターフェース(getChildren()と1とと一つにParentChildを分ける場合は、しかし、その周りを取得することができます

class Agglomeration extends Element<? 
    extends Element<? 
     extends Element<? 
      extends... 

のようなものにあなたをドラッグしようしたがって、Agglomerationは最初を実装し、Solidは2番目の実装のみを実装し、Stackは両方を実装します。

それは価値があるよりも難しいかもしれませんが、それをしばらく実行して、どこに行くかを見てください。 :)

+0

私が今話す限り、「自己」という概念は私が今まで持っていたものと「リンク」していることを本当に真実に感じます。私はあなたが提案したインターフェースを少し変更しなければならなかった、私は現在 'インタフェースElem

、C extends Elem 、S extends Elem > {/ * ... * /}'それは私にかかります。 :-)これまで多くのありがとう! – Waldheinz

0

を(ちょうどここにあなたの意見をドロップします)。私の理解が間違っているなら、私に知らせてください!

ジェネリックの主な目的は、型の安全性を保証し、オブジェクトの関係(私が知る限り、とにかく)を保証することです。型安全性を保証することでオブジェクト間の関係を強化することができますが、最終的に型安全性を確保することが目的です(その結果、それが属していない部分に固執しないようにします)。

抽象クラスでは、addというメソッドを提供しています。つまり、この抽象クラスを拡張するものはaddを実装する必要があります。後で、addには、addSolidオブジェクト(Elementのサブクラス)しか指定できないという制限が課されています。しかし、それを行うには、Element(またはそのサブクラス)をセットに追加できるという抽象クラスのコントラクトに違反しています。

したがって、2つのオプションがあります。デザインを再考する必要があります(抽象クラ​​スではaddを指定しないでください)。あなたの他の選択肢は、サイズがないオブジェクトをaddにしようとすると実行時例外(UnsupportedOperationException)がスローされ、特定のオブジェクトを追加できるかどうかを開発者に知らせる検査方法(canAddなど)

1

ただ、いくつかのランダムな発言:

の代わりにあなたがまたはCollection<? extends E>をたくない場合がありCollection<E>

私はかなり確信しています。Element<E extends Element<**E**>で開始したいのは、このタイプをキャプチャしたいからです(java.lang.Enumを見てください)。だから、おそらく何かが必要ですElement<E extends Element<E,C>, C extends Element<C, ?>> それはあなたとコンパイラの両方のために複雑すぎるかもしれません(おそらくそれは良くなっていますが、少し前にはあまりにもハードにしようとするといくつかの本当に奇妙なエラーがあります)。

おそらくメソッドElement.addとStack.addは実際には異なるメソッドですか?スタックに子を追加することは、それらを積み重ねることを意味し、それらを任意の要素に追加することは、全く異なる何かを意味するかもしれませんか?

Elementのサブクラスが、LinkedListのいくつかまたは完全に適合していると思われるものから、使用しているCollectionクラスを自分で決めることができたらうれしいです。

基本クラスのctorで呼び出されるCollection<C> makeCollection()のようなメソッドができます。しかし、LinkedListの使用はほとんどの場合間違っています(その悪い空間の局所性と高いスペースのオーバーヘッドは非常に遅くなり、その利点は無視されます)。

だから、実行時にこれらのチェックを利用できるようにする必要があります。

したがって、Class<E>パラメータを使用してインスタンスを構築する必要があります。

返品コレクション.unmodifiableSet(children);

このようにして、子供のライブビューが返されます。代わりにcom.google.common.collect.ImmutableSet.copyOf(子)を使用することを検討してください。この行を見ると

0

public interface Element<E extends Element<?,?>, C extends Element<?, ?>> 

は私がはい、これは過剰設計は確かであることを感じました。

もちろん、状況にはさまざまな方法がありますが、私にとって妥当であると思われるものは、継承の代わりにDecoratorパターンを使用することです(どこにでも、元のデザインが擦れていると感じる場所のみ)より良くフィットします。

関連する問題