如何在没有类型问题的情况下在派生类上强制对象字段或属性



我正在Unity 3D中制作一个回合制游戏。由于它是基于回合的,我正在尝试实现一个状态机来控制游戏的总体流程。玩家将控制几个单位,就像在XCOM这样的游戏中一样,我认为每个单位的状态机也可能有助于控制一个单位是否空闲、当前被选中、被摧毁等。武器可能也会在空闲和瞄准等状态下使用它们。

重点是,我试图尽可能地抽象我的状态逻辑,这样它在不同的状态机中是通用的,同时也对我的代码进行DRYing,这样我就不会重复了。为了实现这一点,我当然要使用接口,比如我的IState接口:

namespace MechGame.Common.States
{
public interface IState
{
void EnterState();
void ExitState();
void UpdateState();
}
}

当我试图在派生类上强制字段时,问题就出现了。我已经多次被告知要将接口视为一个契约,告诉类";必须实现X、Y和Z;我知道我的状态机逻辑中的一些对象需要一些私有成员字段。我想在基类中声明这些字段,以便清空代码,几乎就像接口强制方法一样强制它们。问题是如何以抽象的方式声明这些对象字段的类型?例如,每个状态机都有一个扩展StateManager类的状态管理器:

namespace MechGame.Common.States
{
public interface IStateManager
{
State CurrentState { get; }
void NextState();
void TransitionToState(State state);
}
}
using UnityEngine;
namespace MechGame.Common.States
{
public abstract class StateManager : MonoBehaviour, IStateManager
{
protected State _current;
public State CurrentState { get => _current; }
public abstract void NextState();
public virtual void TransitionToState(State state)
{
if(_current != null)
_current.ExitState();
_current = state;
_current.EnterState();
}
public virtual void Update()
{
_current.UpdateState();
}
}
}

例如,我有一个名为CombatStateManager的类,它扩展了StateManager,并将控制游戏的流程、玩家回合、敌人回合等。问题是,当我需要引用StateManager的CurrentState字段这样的抽象类型State时,我需要将其划分为更具体的字段,例如在以下类的OnCellClick方法中:

using MechGame.Common.States;
using MechGame.Selection;
namespace MechGame.Combat.States
{
public class CellEventDispatcher : IStateEventDispatcher
{
private CombatStateManager _manager;
public CellEventDispatcher(CombatStateManager manager)
{
_manager = manager;
}
public void DeregisterEventHandlers()
{
SelectionManager.OnCellClick -= OnCellClick;
}
void OnCellClick(int cellIndex, int buttonIndex)
{
((CombatState)_manager.CurrentState).CellEventHandler.OnCellClick(cellIndex, buttonIndex);
}
public void RegisterEventHandlers()
{
SelectionManager.OnCellClick += OnCellClick;
}
}
}

这似乎不是什么大不了的事,但这是一个开始在许多地方出现的问题。基本上,就像接口强制某些方法必须实现一样,我希望我的基类强制任何子级实现一些必要的字段。但它导致了如上所述的类型问题。难道没有像我想的那样在子类上强制执行字段的好方法吗?如果没有,我怎么能把代码弄干呢?我对泛型不太了解,这在这里有用吗?

您可以实现一个通用变体。尽管只有当每个StateManager始终只使用相同类型的状态时,这才有效。

例如:

public interface IStateManager<TState> where TState : IState
{
TState CurrentState { get; }
void NextState();
void TransitionToState(TState state);
}
public class StateManager<TState>
:  MonoBehaviour, IStateManager<TState> where TState : IState
{
//...
}
public class CombatStateManager : StateManager<CombatState>
{
//...
}

现在,当您在CombatStateManager实例上访问CurrentState时,它将始终是CombatState。这里的要点是总是。因此,它不能是另一种状态类型(除非从CombatState派生(。

还有,作为一个插件。没有必要同时拥有抽象基类和接口。除非您计划将接口作为一个也将包含在其他不相关的类中的东西,否则一个抽象的基础就足够了。你对接口功能的定义并没有错,但在实践中,更适合将其视为";addon";功能性,或者如果某种";多重继承;就是你想要的。

最新更新