Параметры типового типового типа
У меня есть 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
.
Как я могу управлять такой циклической зависимостью?
Ответы
Ответ 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 затем завершится неудачей. Я бы по-прежнему предпочел этот способ и "сохранить запах в своей инфраструктуре", а не круговые шаблоны, загрязняющие все мои конкретные компоненты и менеджеров.