2011-11-15 3 views
17

나는 2 개의 일반 클래스, BaseComponent 클래스 및 BaseManager 클래스가 있습니다.원형 제네릭 형식 매개 변수

둘 다 추상적이어서 구체적인 내용을 나타냅니다.

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

그것은 두 개의 제네릭 형식 매개 변수를 할 수 있습니다 같은 소리 :

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

느슨한 결합이 될 것입니다. 어떻게 작동하는지 예를 들어 보겠습니다. 모든 구성 요소를 관리자에 추가해야하는 경우이 방법을 사용하려면 일종의 기본 메커니즘이 필요합니다. 구성 요소가 관리자의 독립 할 수없는 경우 - (NAT 프라이스 "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). –

관련 문제