基于组件与行为的树状节点系统

本文介绍的是一套面向对象、高度模块化的树状节点管理框架。它的核心思想是将节点功能拆解为可插拔的组件和可执行的行为单元,通过统一的上下文传递数据,并借助生命周期管理器自动驱动节点及其子节点的更新。该框架适用于游戏实体管理、UI 面板系统、场景图构建等需要层次化组织与动态功能扩展的场景。

一、总体设计理念

框架遵循"组合优于继承"的原则,允许开发者为节点动态装配不同功能,而无需创建大量派生类。节点之间的层级关系通过树结构维护,节点的行为则通过组件(长期附加的功能)和行为(一次性或组合执行的操作)来描述。所有节点共享统一的生命周期管理,确保资源正确创建与释放。

二、核心模块介绍

  1. 节点基类
    节点是树结构的基本单位,每个节点拥有唯一标识符,可以包含子节点、组件集合和一个外部行为。节点维护自身的生命周期状态(初始、已创建、已销毁),并提供以下核心能力:

子节点管理:添加、移除子节点,并自动维护父子关系。

组件管理:动态附加或分离组件,并支持按类型查询组件。

行为挂载:可以设置一个外部行为,该行为将在节点更新时自动执行。

节点查找:支持按标识符或条件递归查找树中的节点。

生命周期钩子:节点提供创建(Create)、更新(Update)、销毁(Destroy)等方法,这些方法会递归调用子节点和组件中对应的回调。

  1. 组件系统
    组件是实现具体功能的模块,通过实现统一的组件接口,可以响应节点生命周期的各个阶段:

附加(OnAttach):组件被添加到节点时调用,用于初始化组件与节点的关联。

分离(OnDetach):组件从节点移除时调用,用于清理资源。

创建(OnCreate):节点创建时调用,组件可在此进行初始化。

更新(OnUpdate):节点每帧更新时调用,组件可在此执行持续逻辑(如渲染、输入处理等)。

销毁(OnDestroy):节点销毁前调用,组件可在此释放资源。

组件接口的所有方法均提供默认空实现,开发者只需重写需要的方法,使组件编写更加简洁。

  1. 行为系统
    行为是独立可执行的操作单元,实现了执行(Execute)和资源释放(Dispose)接口。框架内置了两种基础行为:

简单行为:包装一个无参委托,执行时调用该委托。

复合行为:按顺序执行多个子行为,并支持动态添加和移除子行为,释放时自动释放所有子行为。

通过组合简单行为,可以构建复杂的执行序列,且所有行为均可通过 Dispose 方法统一释放资源。

  1. 上下文容器

    上下文是一个轻量级的数据容器,用于在节点生命周期方法、组件更新和行为执行中传递参数。它提供键值对存取功能,并支持类型安全的读写。开发者可以通过上下文传递动态数据(如配置参数、输入事件等),避免方法签名膨胀。框架还提供了便捷的扩展方法,用于存取特定用途的参数(如 UI 面板参数)。

  2. 生命周期管理器

    为了避免每帧手动遍历树更新节点,框架引入生命周期管理器。它负责递归更新节点及其所有子节点,并在更新过程中智能处理子节点的移除情况,确保遍历安全。开发者只需在游戏循环或主线程中调用管理器的更新方法,传入根节点和上下文,即可驱动整个树的更新。

  3. 辅助工具

    唯一键跟踪器:一个线程安全的静态工具,用于记录已创建的键(如 UI 面板标识),防止重复创建同一资源。支持注册、移除和清空操作。

委托安全添加工具:提供向委托数组中添加新委托的方法,自动检查重复,避免同一委托被多次订阅,确保事件订阅的正确性。

三、工作流程示例

构建树结构:创建根节点和子节点,通过 AddChild 建立层级关系。

装配功能:为节点添加所需组件(如渲染组件、动画组件),并可设置外部行为。

初始化:调用根节点的 Create 方法,传入上下文,节点会递归创建所有子节点,并触发组件中的 OnCreate 回调。

每帧更新:通过生命周期管理器,传入根节点和上下文,自动遍历并更新所有节点。每个节点的 Update 会执行外部行为,并调用所有组件的 OnUpdate。

销毁节点:调用节点的 Destroy 方法,节点会递归销毁所有子节点,释放外部行为,并触发组件的 OnDestroy 和 OnDetach 回调,最后从父节点移除。

四、框架优势

高内聚低耦合:组件和行为的分离使功能模块独立,易于测试和复用。

灵活扩展:通过添加新组件或行为,无需修改现有节点类即可增加功能。

生命周期明确:统一的创建、更新、销毁流程,避免资源泄漏。

数据传递规范:上下文容器替代多参数传递,简化接口设计。

线程安全支持:键跟踪器考虑多线程环境,适合并发场景。

五、适用场景

该框架非常适合需要动态构建和管理层次化对象系统的项目,例如:

游戏中的实体(角色、道具、特效)管理

UI 界面中的面板、控件组织

场景图或场景层级管理

任何需要树状结构与动态行为的业务逻辑

通过将功能拆分为组件和行为,开发者可以像搭积木一样快速构建复杂的节点体系,同时保持代码清晰可维护。

csharp 复制代码
namespace Framework
{
    /// <summary>节点生命周期状态</summary>
    public enum NodeState
    {
        Initial,
        Created,
        Destroyed
    }

    // 简单的 Action 包装行为(可放在框架层)
    public class ActionBehavior : IBehavior
    {
        private readonly Action _action;
        public ActionBehavior(Action action) => _action = action;
        public void Execute(IContext context) => _action?.Invoke();
        public void Dispose() { }
    }

    /// <summary>节点组件接口,所有方法均提供默认空实现</summary>
    public interface INodeComponent
    {
        void OnAttach(TreeNode node) { }
        void OnDetach(TreeNode node) { }
        void OnCreate(IContext context) { }
        void OnDestroy(IContext context) { }
        void OnUpdate(IContext context) { }   // 新增:每帧更新
    }

    /// <summary>行为接口,可执行并释放资源</summary>
    public interface IBehavior : IDisposable
    {
        void Execute(IContext context);
    }

    /// <summary>复合行为,按顺序执行多个子行为</summary>
    public class CompositeBehavior : IBehavior
    {
        private readonly List<IBehavior> _behaviors = new();

        public void Add(IBehavior behavior)
        {
            if (behavior == null) throw new ArgumentNullException(nameof(behavior));
            _behaviors.Add(behavior);
        }

        public bool Remove(IBehavior behavior) => _behaviors.Remove(behavior);

        public void Execute(IContext context)
        {
            foreach (var b in _behaviors)
                b.Execute(context);
        }

        public void Dispose()
        {
            foreach (var b in _behaviors)
            {
                try { b?.Dispose(); } catch { }
            }
            _behaviors.Clear();
        }
    }

    /// <summary>上下文数据容器接口</summary>
    public interface IContext
    {
        void Set<T>(string key, T value);
        T Get<T>(string key);
        bool TryGet<T>(string key, out T value);
    }

    /// <summary>基于字典的上下文实现</summary>
    public class DictionaryContext : IContext
    {
        private readonly Dictionary<string, object> _data = new();

        public void Set<T>(string key, T value) => _data[key] = value;
        public T Get<T>(string key) => (T)_data[key];
        public bool TryGet<T>(string key, out T value)
        {
            if (_data.TryGetValue(key, out var obj) && obj is T t)
            {
                value = t;
                return true;
            }
            value = default;
            return false;
        }
    }

    /// <summary>树节点基类,管理组件、子节点和行为</summary>
    public abstract class TreeNode
    {
        public string Id { get; set; }
        public TreeNode Parent { get; internal set; }

        private readonly List<TreeNode> _children = new();
        public IReadOnlyList<TreeNode> Children => _children;

        public NodeState State { get; protected set; } = NodeState.Initial;
        public bool CanDynamicCreate { get; set; } = true;

        private IBehavior _externalBehavior;
        public IBehavior ExternalBehavior
        {
            get => _externalBehavior;
            set
            {
                var old = _externalBehavior;
                _externalBehavior = null; // 先清空,避免后续使用已释放的对象
                try
                {
                    old?.Dispose();
                }
                catch
                {
                    // 释放失败,旧行为已置空,重新抛出异常
                    throw;
                }
                _externalBehavior = value;
            }
        }

        private readonly Dictionary<Type, INodeComponent> _components = new();

        // ---------- 子节点管理 ----------
        public void AddChild(TreeNode child)
        {
            if (child == null) throw new ArgumentNullException(nameof(child));
            if (child.Parent != null && !child.Parent.RemoveChild(child))
                throw new InvalidOperationException("Failed to remove child from its current parent.");
            _children.Add(child);
            child.Parent = this;
        }

        public bool RemoveChild(TreeNode child)
        {
            if (child != null && _children.Remove(child))
            {
                child.Parent = null;
                return true;
            }
            return false;
        }

        // ---------- 组件管理 ----------
        public void AddComponent(INodeComponent component)
        {
            if (component == null) throw new ArgumentNullException(nameof(component));
            var type = component.GetType();
            if (_components.TryGetValue(type, out var old))
            {
                // 先通知旧组件即将分离,再移除
                old.OnDetach(this);
                _components.Remove(type);
            }
            _components[type] = component;
            component.OnAttach(this);
        }

        public bool RemoveComponent<T>() where T : INodeComponent
        {
            var type = typeof(T);
            if (_components.TryGetValue(type, out var comp))
            {
                comp.OnDetach(this);
                _components.Remove(type);
                return true;
            }
            return false;
        }

        public T GetComponent<T>() where T : INodeComponent
        {
            _components.TryGetValue(typeof(T), out var comp);
            return (T)comp;
        }

        public bool TryGetComponent<T>(out T component) where T : INodeComponent
        {
            if (_components.TryGetValue(typeof(T), out var comp) && comp is T t)
            {
                component = t;
                return true;
            }
            component = default;
            return false;
        }

        // ---------- 节点查找 ----------
        public TreeNode FindNode(string id)
        {
            if (id == null) throw new ArgumentNullException(nameof(id));
            if (Id == id) return this;
            foreach (var child in Children)
            {
                var result = child.FindNode(id);
                if (result != null) return result;
            }
            return null;
        }

        public List<TreeNode> FindNodes(Predicate<TreeNode> predicate)
        {
            if (predicate == null) throw new ArgumentNullException(nameof(predicate));
            var results = new List<TreeNode>();
            FindNodesInternal(predicate, results);
            return results;
        }

        private void FindNodesInternal(Predicate<TreeNode> predicate, List<TreeNode> results)
        {
            if (predicate(this)) results.Add(this);
            foreach (var child in Children)
                child.FindNodesInternal(predicate, results);
        }

        // ---------- 生命周期 ----------
        public virtual void Initialize(IContext context) { }

        public virtual void Update(IContext context)
        {
            // 执行外部行为
            _externalBehavior?.Execute(context);

            // 调用所有组件的 OnUpdate(快照避免修改集合异常)
            foreach (var comp in _components.Values.ToList())
                comp.OnUpdate(context);
        }

        public void Create(IContext context)
        {
            if (State != NodeState.Initial) return;
            OnCreate(context);
            foreach (var child in Children)
                child.Create(context);
        }

        protected virtual void OnCreate(IContext context)
        {
            foreach (var comp in _components.Values.ToList())
                comp.OnCreate(context);
            State = NodeState.Created;
        }

        public virtual void Destroy(IContext context)
        {
            if (State == NodeState.Destroyed) return;

            // 先释放外部行为
            _externalBehavior?.Dispose();
            _externalBehavior = null;

            // 通知所有组件即将销毁,并分离(快照处理)
            foreach (var comp in _components.Values.ToList())
            {
                comp.OnDestroy(context);
                comp.OnDetach(this);
            }
            _components.Clear();

            // 销毁所有子节点(子节点销毁时会从 _children 中移除)
            DestroyAllChildren(context);

            State = NodeState.Destroyed;

            // 最后从父节点移除自己
            Parent?.RemoveChild(this);
        }

        private void DestroyAllChildren(IContext context)
        {
            while (_children.Count > 0)
            {
                var snapshot = _children.ToList();
                foreach (var child in snapshot)
                    child.Destroy(context);
            }
        }
    }

    /// <summary>生命周期管理器接口</summary>
    public interface ILifecycleManager
    {
        void UpdateNode(TreeNode node, IContext context);
    }

    /// <summary>默认生命周期管理器</summary>
    public class DefaultLifecycleManager : ILifecycleManager
    {
        public void UpdateNode(TreeNode node, IContext context)
        {
            if (node.State != NodeState.Created) return;
            node.Update(context);

            // 动态遍历子节点,处理子节点在更新中被移除的情况
            int i = 0;
            while (i < node.Children.Count)
            {
                var child = node.Children[i];
                UpdateNode(child, context);
                // 如果子节点仍在当前节点下,才移动索引;否则子节点已被移除,当前索引自动指向下一个
                if (child.Parent == node)
                    i++;
            }
        }
    }

    // ---------- 上下文扩展 ----------
    public static class ContextExtensions
    {
        public const string ParamKey = "UICellParams";

        public static void SetParameters(this IContext context, Dictionary<string, object> parameters)
            => context.Set(ParamKey, parameters);

        public static Dictionary<string, object> GetParameters(this IContext context)
            => context.TryGet<Dictionary<string, object>>(ParamKey, out var dic) ? dic : null;
    }

    // ---------- UI 生命周期组件 ----------
    public class UILifecycleComponent : INodeComponent
    {
        private TreeNode _node;
        private readonly Action<UITree, Dictionary<string, object>> _updateAction;

        public UILifecycleComponent(Action<UITree, Dictionary<string, object>> updateAction)
        {
            _updateAction = updateAction ?? throw new ArgumentNullException(nameof(updateAction));
        }

        public void OnAttach(TreeNode node)
        {
            _node = node;
        }

        public void OnUpdate(IContext context)
        {
            if (_node is not UITree uiNode) return;
            var parameters = context.GetParameters();
            if (parameters == null) return;
            _updateAction(uiNode, parameters);
        }

        // 其他方法留空
    }

    /// <summary>
    /// 用于跟踪唯一创建键的静态工具,支持线程安全。
    /// </summary>
    public static class CreationKeyTracker
    {
        private static readonly HashSet<string> _createdKeys = new HashSet<string>();
        private static readonly object _lock = new object();

        /// <summary>尝试注册一个键,若首次注册返回 true,否则返回 false。</summary>
        public static bool IsFirstCreate(string key)
        {
            if (string.IsNullOrEmpty(key)) return false;
            lock (_lock)
                return _createdKeys.Add(key);
        }

        /// <summary>清空所有已注册的键。</summary>
        public static void Cleanup()
        {
            lock (_lock)
                _createdKeys.Clear();
        }

        /// <summary>移除指定的键。</summary>
        public static void RemoveKey(string key)
        {
            if (!string.IsNullOrEmpty(key))
                lock (_lock)
                    _createdKeys.Remove(key);
        }
    }

    /// <summary>
    /// 委托相关的通用工具方法。
    /// </summary>
    public static class DelegateUtility
    {
        /// <summary>
        /// 向委托数组中安全添加一个新委托,若已存在相同委托则跳过。
        /// </summary>
        /// <typeparam name="TDelegate">委托类型</typeparam>
        /// <param name="existing">现有的委托数组(可能为 null)</param>
        /// <param name="newAction">要添加的新委托</param>
        /// <returns>合并后的新数组</returns>
        public static TDelegate[] SafeAddAction<TDelegate>(TDelegate[] existing, TDelegate newAction) where TDelegate : Delegate
        {
            if (newAction == null)
                throw new ArgumentNullException(nameof(newAction));

            // 过滤掉 existing 中的 null
            var nonNullExisting = existing?.Where(d => d != null).ToArray() ?? Array.Empty<TDelegate>();

            // 检查是否已存在相同委托
            if (nonNullExisting.Any(action => AreDelegatesEqual(action, newAction)))
                return nonNullExisting; // 返回无 null 的现有数组

            // 添加新委托
            var newList = new List<TDelegate>(nonNullExisting) { newAction };
            return newList.ToArray();
        }

        private static bool AreDelegatesEqual(Delegate a, Delegate b)
        {
            if (a == null || b == null) return false;
            return a.Target == b.Target && a.Method == b.Method && a.GetType() == b.GetType();
        }
    }
}
相关推荐
超级大福宝2 小时前
N皇后问题:经典回溯算法的一些分析
数据结构·c++·算法·leetcode
bugcome_com3 小时前
C# 类的基础与进阶概念详解
c#
雪人不是菜鸡3 小时前
简单工厂模式
开发语言·算法·c#
岛雨QA3 小时前
常用十种算法「Java数据结构与算法学习笔记13」
数据结构·算法
weiabc3 小时前
printf(“%lf“, ys) 和 cout << ys 输出的浮点数格式存在细微差异
数据结构·c++·算法
铸人3 小时前
大数分解的Shor算法-C#
开发语言·算法·c#
wefg13 小时前
【算法】单调栈和单调队列
数据结构·算法
岛雨QA3 小时前
图「Java数据结构与算法学习笔记12」
数据结构·算法
czxyvX3 小时前
020-C++之unordered容器
数据结构·c++