You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何处理实体复杂度及实体方法高内聚问题?以IUnit接口重构为例

重构臃肿IUnit接口的实践探讨

问题背景

正在重构代码,当前的IUnit接口定义如下:

public interface IUnit : Entity, IUpdatable
{
    byte Team { get; }
    bool CanHelp(IUnit unit);
    bool CanHurt(IUnit unit);

    Position Position { get; }

    void CastSpell(ISpell spell, IUnit target);
    void GiveSpell(ISpell spell);
    void RemoveSpell(ISpell spell);
    Duration GetCooldown(ISpell spell);
    void SetCooldown(ISpell spell, Duration cooldown);

    bool Alive { get; }
    float CurrentHealth { get; }
    float MaxHealth { get; }

    void TakeDamage(HealthChangeEvent @event);
    void Heal(HealthChangeEvent @event);

    void ApplyStatus(EffectApplicationData data, Aura aura, long duration);
    void RemoveStatus(SpellId aura);
    bool HasStatus(SpellId aura);

    bool CanPay(AbilityCost cost);
    bool HasResource(ResourceType type);
    float GetResourceValue(ResourceType type);
    void GiveResource(ResourceType type, float value);
    void SpendResource(AbilityCost cost);

    Vector3 GetMovement();
}

当前最大的问题是IUnit作为所有ISpell/IAura效果的目标,每次新增功能都要往这个已经臃肿的接口里加新方法。目前想到的方案是将IUnit作为IAuraOwner、IHealthOwner等的容器,采用组件系统,通过IUnit.GetComponent<T>()或直接访问IUnit.Health/IUnit.Auras来实现。

通用解决方案与实践

1. 基于职责拆分的组件化设计

将IUnit的职责拆分为多个单一职责的子接口,每个接口只负责一个领域的功能:

  • IUnitIdentity:包含团队(Team)、存活状态(Alive)、位置(Position)等核心标识信息
  • IHealthSystem:封装生命值相关操作(CurrentHealth/MaxHealth、TakeDamage、Heal)
  • ISpellSystem:处理法术的施放、管理、冷却(CastSpell、GiveSpell、GetCooldown等)
  • IAuraSystem:负责光环的施加、移除与检测(ApplyStatus、RemoveStatus、HasStatus)
  • IResourceSystem:处理资源的查询与消耗(GetResourceValue、SpendResource、CanPay)
  • IMovementSystem:提供移动相关数据(GetMovement)

然后让IUnit作为这些子系统的容器,通过显式属性暴露:

public interface IUnit : Entity, IUpdatable
{
    IUnitIdentity Identity { get; }
    IHealthSystem Health { get; }
    ISpellSystem Spells { get; }
    IAuraSystem Auras { get; }
    IResourceSystem Resources { get; }
    IMovementSystem Movement { get; }

    bool CanHelp(IUnit unit);
    bool CanHurt(IUnit unit);
}

这种方式的优势是:

  • 每个子接口职责单一,符合单一职责原则
  • 新增功能只需扩展对应的子系统或新增子接口,不会污染核心IUnit接口
  • ISpell/IAura可以根据需求依赖特定的子系统,而非整个IUnit,降低耦合

2. 基于服务定位的组件查询模式

如果需要更灵活的组件扩展(比如未来可能新增大量个性化组件),可以采用类似ECS(实体组件系统)的通用组件查询接口:

public interface IUnit : Entity, IUpdatable
{
    byte Team { get; }
    bool CanHelp(IUnit unit);
    bool CanHurt(IUnit unit);
    Position Position { get; }
    bool Alive { get; }

    T GetComponent<T>() where T : class;
    bool HasComponent<T>() where T : class;
}

然后将各个功能模块实现为独立的组件类:

public class HealthComponent
{
    public float CurrentHealth { get; private set; }
    public float MaxHealth { get; private set; }

    public void TakeDamage(HealthChangeEvent @event) { /*...*/ }
    public void Heal(HealthChangeEvent @event) { /*...*/ }
}

public class SpellComponent
{
    public void CastSpell(ISpell spell, IUnit target) { /*...*/ }
    // 其他法术相关方法
}

这种模式的好处是:

  • 完全解耦功能模块,新增功能只需添加新组件,无需修改IUnit接口
  • 可以根据不同Unit类型动态挂载不同组件,支持更灵活的实体配置
  • ISpell/IAura在使用时,先通过HasComponent<T>判断目标是否具备对应功能,再调用组件方法,避免不必要的依赖

3. 针对Spell/Aura的目标抽象

既然IUnit是ISpell/IAura的核心目标,可以为这些效果定义更精准的目标接口,而非直接依赖臃肿的IUnit。例如:

  • 伤害类法术只需依赖IDamageable接口:
public interface IDamageable
{
    void TakeDamage(HealthChangeEvent @event);
}
  • 治疗类法术依赖IHealable接口:
public interface IHealable
{
    void Heal(HealthChangeEvent @event);
}
  • 光环效果依赖IAuraTarget接口:
public interface IAuraTarget
{
    void ApplyStatus(EffectApplicationData data, Aura aura, long duration);
    void RemoveStatus(SpellId aura);
    bool HasStatus(SpellId aura);
}

然后让IUnit实现这些精准接口,同时内部将实现委托给对应的组件。这种方式的优势是:

  • ISpell/IAura只依赖自身需要的最小接口,符合依赖倒置原则
  • 未来如果有非Unit类型的目标(比如场景物体),只需实现对应接口即可被法术/光环影响,扩展性更强

4. 剥离跨职责判断逻辑

对于CanHelp/CanHurt这类跨领域的判断逻辑,建议将其移到专门的规则服务中,而非放在IUnit接口里:

public interface IUnitInteractionRules
{
    bool CanHelp(IUnit source, IUnit target);
    bool CanHurt(IUnit source, IUnit target);
}

这样可以避免IUnit承担不属于自身职责的判断逻辑,同时方便后续修改交互规则(比如新增阵营关系、状态限制等),无需修改IUnit接口。


内容的提问来源于stack exchange,提问作者DaemendLock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 19:27:51