C# Unity中如何优化ShipComponent抽象类的虚/抽象方法实现,减少重复代码并统一管理委托监听?
看起来你已经搭好了一个挺清晰的组件化船舰架构,但每个ShipComponent子类都要重复写Init、IsPlayer、IsNotPlayer这些方法,大部分逻辑都是雷同的,只有委托监听和个别协程不一样,这确实太繁琐了。我之前做Unity游戏架构的时候也遇到过类似的问题,重复代码一多维护起来真的头疼,咱们一步步来优化。
一、收拢通用逻辑到基类,只给子类留扩展点
首先,咱们可以把基类里的通用逻辑固化,比如Init方法里的shipControl赋值是所有子类都需要的,那基类就直接实现这个逻辑,然后提供一个虚的扩展方法让子类实现自己的初始化(比如找子物体、启动专属协程)。同理,IsPlayer、IsNotPlayer里的通用流程也可以放到基类,子类只需要补充自己的特殊逻辑。
修改后的ShipComponent基类示例:
public abstract class ShipComponent : MonoBehaviour { [HideInInspector] public ShipControl shipControl; // 通用初始化逻辑,子类不用重写这个 public void Init(ShipControl control) { this.shipControl = control; // 调用子类的专属初始化逻辑 InitComponent(); // 统一设置监听 SetListeners(); } // 子类重写这个方法实现自己的初始化(比如找子物体、启动协程) protected virtual void InitComponent() { } // 通用的IsPlayer逻辑,子类可以选择重写扩展 public virtual void IsPlayer() { SetListeners(); // 调用子类的Player模式专属逻辑 OnEnterPlayerMode(); } // 子类重写这个方法处理Player模式下的特殊逻辑(比如启动协程) protected virtual void OnEnterPlayerMode() { } public virtual void IsNotPlayer() { // 统一移除所有监听 RemoveAllListeners(); // 调用子类的非Player模式专属逻辑 OnExitPlayerMode(); } // 子类重写这个方法处理非Player模式下的特殊逻辑(比如停止协程) protected virtual void OnExitPlayerMode() { } public virtual void ReEnable() { SetListeners(); // 调用子类重新启用时的专属逻辑 OnReEnable(); } // 子类重写这个方法处理重新启用时的特殊逻辑 protected virtual void OnReEnable() { } // 子类需要实现这个方法,返回自己的委托绑定关系 protected abstract IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings(); // 基类统一处理监听添加 private void SetListeners() { // 先移除避免重复绑定 RemoveAllListeners(); if (!shipControl.shipWrapper.ship.IsPlayer) return; foreach (var (Event, Handler) in GetEventBindings()) { // 根据事件类型适配添加逻辑 if (Event is Action actionEvent && Handler is Action actionHandler) actionEvent += actionHandler; else if (Event is Action<int> intActionEvent && Handler is Action<int> intActionHandler) intActionEvent += intActionHandler; // 其他事件类型(比如Action<bool>)同理扩展 } } // 基类统一处理监听移除 private void RemoveAllListeners() { foreach (var (Event, Handler) in GetEventBindings()) { if (Event is Action actionEvent && Handler is Action actionHandler) actionEvent -= actionHandler; else if (Event is Action<int> intActionEvent && Handler is Action<int> intActionHandler) intActionEvent -= intActionHandler; // 其他事件类型同理扩展 } } // 统一处理Disable时的清理逻辑 protected virtual void OnDisable() { RemoveAllListeners(); StopAllCoroutines(); } }
二、简化子类实现,只关注特有逻辑
现在子类只需要实现自己的专属逻辑和返回委托绑定关系就行,不用再重复写那些通用的方法了。比如修改后的Rudder类:
public class Rudder : ShipComponent { [Header("Settings")] public Transform rudder; [Header("Debug Info")] [SerializeField] float rudderSpeed; [SerializeField][Range(-45, 45)] int setRudderAngle = 0; [SerializeField][Range(-45f, 45f)] float realRudderAngle = 0f; protected override void InitComponent() { rudder = transform.GetChild(0).GetChild(4); StartCoroutine(SmoothRudderChange()); } protected override IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings() { // 返回自己需要绑定的事件和处理方法 yield return (PlayerShipControl.OnRudderChange, (Action<int>)SetRudder); } // 重新启用时的特殊逻辑 protected override void OnReEnable() { StartCoroutine(SmoothRudderChange()); } void SetRudder(int angle) { setRudderAngle = angle; // 你的舵机逻辑... } IEnumerator SmoothRudderChange() { // 你的平滑旋转逻辑... yield return null; } }
再看简化后的Guns类:
public class Guns : ShipComponent { private IEnumerator mouseAimCycle; protected override void InitComponent() { InitCannons(); } protected override void OnEnterPlayerMode() { mouseAimCycle = AimCycle(); StartCoroutine(mouseAimCycle); } protected override void OnExitPlayerMode() { StopCoroutine(mouseAimCycle); } protected override IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings() { yield return (PlayerShipControl.OnFireGuns, (Action)TryFire); } void TryFire() { // 你的开火逻辑... } IEnumerator AimCycle() { // 你的瞄准循环逻辑... yield return null; } void InitCannons() { // 你的大炮初始化逻辑... } }
三、额外优化:用泛型统一处理委托(可选)
如果你的项目里事件类型很多,上面的类型判断会显得繁琐,可以用泛型绑定类来优化:
// 定义不同类型的绑定类 public class ActionBinding { public Action Event; public Action Handler; } public class ActionIntBinding { public Action<int> Event; public Action<int> Handler; } public class ActionBoolBinding { public Action<bool> Event; public Action<bool> Handler; }
然后基类里可以定义多个抽象方法让子类返回对应类型的绑定,或者用一个统一的列表来容纳,这样处理起来更灵活,不过这属于进阶优化,当前的方案已经能解决你的核心痛点了。
总结
优化之后,每个子类只需要关注自己的核心逻辑:初始化专属资源、处理Player/非Player模式的特殊逻辑、返回自己需要监听的事件和处理方法。所有通用的监听管理、初始化流程、Disable清理都收拢到了基类里,再也不用复制粘贴那些重复的代码了,维护起来也方便很多。
备注:内容来源于stack exchange,提问作者user20494201

