Me imagino que usaré un caso de estudio de algo en mi código como un ejemplo de lo que quiero decir.Herencia/jerarquías de interfaz en .Net: ¿hay una forma mejor?
Ahora mismo, estoy trabajando en un sistema de comportamiento/acción para mi sistema de juegos basado en componentes. GameObjects pueden tener un IBehaviorComponent y un IActionComponent unido a ellos, que, mostrando sólo los detalles relevantes, exponer lo siguiente:
public interface IBehaviorComponent : IBaseComponent
{
IBehavior Behavior { get; }
}
public interface IActionComponent : IBaseComponent
{
IAction Action { get; }
void ExecuteAction(IGameObject target = null);
}
Ahora bien, esto es todo muy bien hasta ahora (al menos para mí!). Pero los problemas comienzan a surgir cuando veo las implementaciones de IActionComponent.
Por ejemplo, una implementación simple IActionComponent:
public class SimpleActionComponent : IActionComponent
{
public IAction Action { get; protected set; }
public void ExecuteAction(IGameObject target = null)
{
Action.Execute(target);
}
public void Update(float deltaTime) { } //from IBaseComponent
}
Pero, digamos que quiero introducir una implementación IActionComponent más complejo que permite acciones a ejecutar en un horario programado:
public class TimedActionComponent : IActionComponent
{
public IAction Action { get; protected set; }
public float IdleTime { get; set; }
public float IdleTimeRemaining { get; protected set; }
public void ExecuteAction(IGameObject target = null)
{
IdleTimeRemaining = IdleTime;
}
public void Update(float deltaTime)
{
if (IdleTimeRemaining > 0f)
{
IdleTimeRemaining -= deltaTime;
}
else
{
Action.Execute(target);
}
}
}
Y ahora, digamos que quiero exponer IdleTime para que pueda ser modificado por influencias externas. Mis pensamientos en un principio eran crear una nueva interfaz:
public interface ITimedActionComponent : IActionComponent
{
float IdleTime { get; set; }
float IdleTimeRemaining { get; set; }
}
Sin embargo, el problema aquí es que mi sistema de componentes almacena todo en un nivel arriba de IBaseComponent. Entonces, el componente de acción para un GameObject se recupera como un IActionComponent, -not- como, por ejemplo, un ITimedActionComponent, o un IRandomizedActionComponent, o incluso un ICrashTheProgramActionComponent. Espero que las razones sean obvias, ya que quiero que cualquier cosa pueda consultar el GameObject para uno de sus componentes sin tener que saber exactamente qué quiere más allá del tipo básico de componente (IActionComponent, IRenderableComponent, IPhysicsComponent, etc.)
¿Existe una forma más limpia de manejar esto, que me permita exponer estas propiedades definidas en clases secundarias sin que todo tenga que convertir el IActionComponent recuperado al tipo que le interesa? ¿O es simplemente la única/mejor forma de lograr esto? Algo así como:
public void IWantYouToAttackSuperSlow(IGameObject target)
{
//Let's get the action component for the gameobject and test if it's an ITimedActionComponent...
ITimedActionComponent actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent;
if (actionComponent != null) //We're okay!
{
actionComponent.IdleTime = int.MaxValue; //Mwhaha
}
}
En este momento estoy pensando que es La única manera, pero pensé que había que ver si hay un patrón escondido en la madera que soy ignorante, o si alguien puede sugerir una una forma mucho mejor de lograr esto para empezar.
Gracias!
Mi otro pensamiento era utilizar otro objeto en el interior, como IActionComponentExecutor, lo que haría Decidir cuándo llamar a Action.Execute(), pero creo que eso me lleva de vuelta al principio en una moda de ida y vuelta: los objetos externos tendrán que buscar IActionComponentTimedExecutor (oh ~ estamos llegando a la empresa-y ahora !) para cambiar IdleTime. – Terminal8
Fuera de interés: ¿está el estado de actionComponent.IdleTime almacenado en algún tipo de bolsa genérica? Si es así, puede tener los métodos 'Apply (BagData bag)' y 'Store (BagData bag)' en su interfaz base. Dicho esto, es posible que también deba considerar algún sistema de tipo metadata que pueda usar para consultar propiedades que necesiten conocer los tipos derivados. 'TypeDescriptor' es un punto de inicio listo para ser utilizado por elementos como cuadrículas de propiedades, etc. –
No, actualmente no lo es. Tampoco estoy completamente familiarizado con el concepto. ¿Hay alguna referencia que puedas proporcionar? – Terminal8