Подтвердить что ты не робот

Параметры типового типового типа

У меня есть 2 общих класса, класс BaseComponent и <<21 > .

Они оба абстрактны и предназначены для того, чтобы быть конкретными.

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.

Как я могу управлять такой циклической зависимостью?

4b9b3361

Ответ 1

Похоже, вы можете иметь два типичных типа:

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>

Да, это вонючий - но это то, что я сделал в Буферах протоколов.

Итак, у вас будет:

public class PhysicsManager : BaseManager<PhysicsComponent, PhysicsManager>

public class PhysicsComponent : BaseComponent<PhysicsComponent, PhysicsManager>

Ответ 2

Самое слабое соединение было бы, если бы компоненты не знали об их менеджерах. Вот пример того, как это будет работать. Обратите внимание, что для этого подхода требуется какой-то механизм factory, если все компоненты должны быть добавлены в диспетчер. (Nat Pryce - "Если существует связь между двумя объектами, некоторый другой объект должен установить связь." )

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);
}

Если компоненты не могут быть независимы от своих менеджеров, я думаю, что было бы лучше, чтобы они зависели от интерфейса, определенного потребностью компонента. Это Принцип сегрегации интерфейса

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

Недостатком является то, что система типов не гарантирует, что правильный менеджер будет передан, и приведение в BaseManager затем завершится неудачей. Я бы по-прежнему предпочел этот способ и "сохранить запах в своей инфраструктуре", а не круговые шаблоны, загрязняющие все мои конкретные компоненты и менеджеров.