c# - parámetros de tipo genéricos circulares
generics circular-dependency (2)
El acoplamiento más flojo sería si los componentes no supieran sobre sus gerentes. Aquí hay un ejemplo de cómo eso funcionaría. Tenga en cuenta que este enfoque requiere algún tipo de mecanismo de fábrica si todos los componentes deben agregarse a un administrador. (Nat Pryce - "Si existe una relación entre dos objetos, algún otro objeto debe establecer la relación" ).
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);
}
Si los componentes no pueden ser independientes de sus administradores, creo que sería mejor que dependan de una interfaz definida por la necesidad del componente. Este es el principio de segregación de interfaz
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();
}
}
La desventaja es que el sistema de tipos no exige que se entregue el administrador correcto, y el molde en BaseManager fallará. Todavía preferiría esta manera y "mantendría la olfato en mi infraestructura" en lugar de tener plantillas circulares que contaminen a todos mis componentes y gerentes de concreto.
Tengo 2 clases genéricas, una clase BaseComponent
y una clase BaseManager
.
Ambos son abstractos y están destinados a ser hechos concretos.
public abstract class BaseManager<T> where T : BaseComponent<?>
public abstract class BaseComponent<T> where T : BaseManager<?>
BaseManager
tiene una lista de BaseComponents, por lo que quiero que sea genérica, por lo que un PhysicsManager : BaseManager<PhysicsComponent>
tendría una lista de PhysicsComponents
.
Quiero (o más bien creo que necesito) que BaseComponent
sea genérico porque solo quiero que las clases derivadas de BaseComponent
estén ''unidas'' a su administrador apropiado. Idealmente, no quiero tener que escribir un constructor por componente derivado solo para poder agregarlo a una clase de administrador concreta aprobada. Idealmente, quiero tener un constructor que tome la clase abstracta de BaseManager
.
¿Cómo puedo gestionar este tipo de dependencia circular?
Parece que es posible que desee tener dos parámetros genéricos de tipo:
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>
Sí, es maloliente, pero ese es el tipo de cosas que he hecho en Protocol Buffers .
Entonces tendrías:
public class PhysicsManager : BaseManager<PhysicsComponent, PhysicsManager>
public class PhysicsComponent : BaseComponent<PhysicsComponent, PhysicsManager>