.Net中的接口继承/层次结构 - 有更好的方法吗?

时间:2012-09-11 21:53:22

标签: c# inheritance interface

我想我会在代码中使用一些案例研究作为我的意思的一个例子。

现在,我正在为基于组件的游戏系统开发一个行为/动作系统。 GameObjects可以有一个IBehaviorComponent和一个附加到它们的IActionComponent,只显示相关细节,公开以下内容:

public interface IBehaviorComponent : IBaseComponent
{
   IBehavior Behavior { get; }
}

public interface IActionComponent : IBaseComponent
{
   IAction Action { get; }
   void ExecuteAction(IGameObject target = null);
}

现在,到目前为止这一切都很好(至少对我而言!)。但是,当我查看IActionComponent的实现时,麻烦开始酿造。

例如,一个简单的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
}

但是,假设我想引入一个更复杂的IActionComponent实现,允许按照定时计划执行操作:

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

现在,假设我想公开IdleTime,以便可以通过外部影响进行更改。我最初的想法是创建一个新界面:

public interface ITimedActionComponent : IActionComponent
{
   float IdleTime { get; set; }

   float IdleTimeRemaining { get; set; }
}

然而,这里的问题是我的组件系统将所有内容存储在IBaseComponent的一级。因此,GameObject的动作组件将被检索为IActionComponent,例如,ITimedActionComponent,或IRandomizedActionComponent,甚至是ICrashTheProgramActionComponent。我希望其中的原因是显而易见的,因为我希望能够查询GameObject中的一个组件,而不必知道除了基本类型的组件(IActionComponent,IRenderableComponent,IPhysicsComponent等)之外的确切内容。 / p>

是否有更简洁的方法来处理这个问题,这将允许我公开子类中定义的这些属性,而不必将检索到的IActionComponent转换为它感兴趣的类型?或者这只是实现这一目标的唯一/最佳方式。类似的东西:

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

现在我认为那是唯一的方式,但我想我会看到是否有一种模式隐藏在我不知道的木制品中,或者是否有人可以提出更好的建议开始实现这一目标的方式。

谢谢!

2 个答案:

答案 0 :(得分:1)

您展示的将IActionComponent强制转换为ITimedActionComponent的代码(据我所知)是不可避免的 - 您必须按顺序注意IdleTime属性使用它们吧?

我认为这里的诀窍是隐藏那些看起来不太好看的代码,其中使用IActionComponent的类不必处理它。
我首先想到的是如何使用Factory

  public void IWantYouToAttackSuperSlow(IGameObject target)
{
   //Let's get the action component for the gameobject 
   IActionComponent actionComponent = ActionComponentsFactory.GetTimedActionComponentIfAvailable(int.MaxValue); 
}

和你工厂的方法:

public IActionComponent GetTimedActionComponentIfAvailable(IGameObject target, int idleTime)
{
var actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent;

   if (actionComponent != null) //We're okay!
   {
      actionComponent.IdleTime = int.MaxValue; //Mwhaha
   }
return (actionComponent != null)? actionComponent : target.GetComponent<IActionComponent>();
}

答案 1 :(得分:0)

你是对的,通过转换为子类型来测试特定的实现并不是一个好主意。从您提供的示例中可以看出,您希望对IGameObject执行某些操作。不要让你的IActionComponent公开一个以IGameObject为参数的方法,你可以反过来做,也就是让IGameObject取一个或多个,如果你愿意的话,您可以执行的操作。

interface IActionComponent
{
    ExecuteAction();
}

interface IGameObject
{
    IActionComponent ActionComponent { get; }
}

然后在执行方法中执行

public void IWantYouToAttackSuperSlow(IGameObject target)
{
    target.ActionComponent.ExecuteAction();
}

根据您实现IActionComponent的方式,多态将确保执行正确的代码。

有一种名为Command Pattern的模式似乎符合您的需要。