2011-11-15 21 views
17

私はBaseComponentクラスとBaseManagerクラスの2つのジェネリッククラスを持っています。円ジェネリック型パラメータ

これらはいずれも抽象的なものであり、具体的に意図されています。

public abstract class BaseManager<T> where T : BaseComponent<?> 
public abstract class BaseComponent<T> where T : BaseManager<?> 

BaseManager私はそれが一般的なようにしたい理由である、BaseComponentsのリストを持っているので、PhysicsManager : BaseManager<PhysicsComponent>PhysicsComponentsのリストを持っているでしょう。

私はBaseComponentから派生したクラスを適切なマネージャーに欲しいと思っているだけなので、BaseComponentが一般的であることを望みたいです。理想的には、派生コンポーネントごとにコンストラクタを書く必要がないので、渡された具象マネージャクラスにコンストラクタを追加することができます。理想的には、私は、抽象的なBaseManagerクラスを取るコンストラクタをしたいです。

どのようにこの種の循環依存性を管理できますか?

+0

循環依存性を避けるために再設計することを強く検討します。たとえば、 'BaseComponent'を非ジェネリックにします。それは 'IManager'に依存していますか? 'BaseManager ' –

+0

の 'BaseComponent'から' TComponent'へのキャストを入れてください。Jonが指摘しているように少し臭いがあると私は同意しますが、私はそれほどフォローしません。 'BaseComponent'が' IManager'に依存している場合、 'BaseComponent'のすべての派生クラスに正しい具体的な' IManager'実装を受け入れたコンストラクタがあるようにして、マネージャのリストに追加することはできますか?あなたが時間を持っていれば、私は答えの精緻化に感謝します。 –

答えて

25

は、それはあなたが2つのジェネリック型パラメータを持っている場合がありますように聞こえる:

public abstract class BaseManager<TComponent, TManager> 
    where TComponent : BaseComponent<TComponent, TManager> 
    where TManager : BaseManager<TComponent, TManager> 
public abstract class BaseComponent<TComponent, TManager> 
    where TComponent : BaseComponent<TComponent, TManager> 
    where TManager : BaseManager<TComponent, TManager> 

はい、それは臭いだ - しかし、それは私がProtocol Buffersにやった事のようなものです。

だから、あなたが持っているだろう:コンポーネントは上司について知らなかった場合

public class PhysicsManager : BaseManager<PhysicsComponent, PhysicsManager> 

public class PhysicsComponent : BaseComponent<PhysicsComponent, PhysicsManager> 
2

最も緩いカップリングは次のようになります。これがどのように機能するかの例があります。この方法では、すべてのコンポーネントをマネージャに追加する必要がある場合は、何らかのファクトリメカニズムが必要であることに注意してください。 (ナット・プライス - "If a relationship exists between two objects, some other object should establish the relationship."

abstract class BaseComponent 
{ 
    public event EventHandler SomethingHappened; 
} 

abstract class BaseManager<TComponent> where TComponent : BaseComponent 
{ 
    List<TComponent> components = new List<TComponent>(); 

    public virtual void AddComponent(TComponent component) 
    { 
     components.Add(component); 
     component.SomethingHappened += (s, e) => OnSomethingHappened(component); 
    } 

    public abstract void OnSomethingHappened(TComponent component); 
} 

コンポーネントがその経営者から独立していることができない場合、私は彼らがコンポーネントの必要性によって定義されたインタフェースに依存することは良いことだと思います。これは、欠点は、型システムが正しいマネージャが渡され、そしてBaseManagerでキャストがその後、失敗することを強制しないことですInterface Segregation Principle

interface IManager 
{ 
    void ManageMe(BaseComponent component); 
} 

abstract class BaseComponent 
{ 
    public BaseComponent(IManager manager) 
    { 
     manager.ManageMe(this); 
    } 
} 

abstract class BaseManager<TComponent> : IManager where TComponent : BaseComponent 
{ 
    void IManager.ManageMe(BaseComponent component) 
    { 
     ManageMe((TComponent)component); 
    } 

    protected abstract void ManageMe(TComponent component); 
} 

interface IPhysicsManager : IManager 
{ 
    void AnotherCallback(PhysicsComponent comp); 
} 

abstract class PhysicsComponent : BaseComponent 
{ 
    public PhysicsComponent(IPhysicsManager manager) 
     : base(manager) 
    { 
     manager.AnotherCallback(this); 
    } 
} 

abstract class PhysicsManager : BaseManager<PhysicsComponent>, IPhysicsManager 
{ 
    protected override void ManageMe(PhysicsComponent component) 
    { 
     throw new NotImplementedException(); 
    } 

    public void AnotherCallback(PhysicsComponent comp) 
    { 
     throw new NotImplementedException(); 
    } 
} 

です。私はまだこの方法を好み、私の具体的なコンポーネントとマネージャをすべて汚染する円形のテンプレートを用意するのではなく、 "インフラストラクチャに匂いを残す"

+0

非常に詳細な答えをありがとう! –

+0

興味深い。循環依存関係は純粋なインタフェースを使用して行うことができるので、具体的なコンポーネントやマネージャを汚染しないことに注意してください。一般的な型名は 'using'ディレクティブで短縮できます(例:http://stackoverflow.com/a/161484/1429390)。 –

関連する問題