Unity有限状态机 一个支持消息调度系统的泛型状态机

ASP.NET前后端用户安全管理技术全解析 本文系统介绍ASP.NET Core中用户管理的核心技术,涵盖注册登录、密码加密(PBKDF2)、JWT身份认证及AccessToken/RefreshToken机制。通过轻量级Web API项目演示,强调安全实践:密码明文不存储,使用加盐哈希;登录后生成带有效期的JWT令牌,结合短时效AccessToken与安全存储的RefreshToken实现无状态身份验证,提升系统安全性与用户体验。 阅读详情

本文介绍了 SharedFSM 框架的使用和设计思路
gitee地址
github地址

使用示例

安装

  1. 找到项目的manifest.json

  2. 在dependencies中加一句:

“com.shared.sharedfsm”: “https://gitee.com/TheSourceCode/SharedFSM.git?path=src/SharedFSM/Assets”,

实战示例:好吃懒做的Bob 和 勤劳的Elsa

场景剧本
  1. Elsa做家务

  2. Bob回家睡觉

  3. Elsa停止家务,并开始做饭

  4. Elsa做好饭,通知Bob吃饭,并开始做家务

  5. Bob 收到消息,从睡觉状态切换到吃饭状态

  6. Bob吃饭完后,回到第2条,继续睡觉。

游戏状态定义

推荐在项目中都建立这两个类

public class GameFSM : FSM<IGameState, BaseGameEntity>
{
    public GameFSM(BaseGameEntity owner, IGameState globalState = null) : base(owner, globalState)
    {
    }
}

public interface IGameState : IState<BaseGameEntity>
{
    // 一些自定义方法
}
实体定义
public class BaseGameEntity : FSMEntity<IGameState, BaseGameEntity>
{
    // 一些自定义方法
}
public class Bob : BaseGameEntity
{
    private void Awake()
    {
        _fsm = new GameFSM(this);
        ChangeState(ServiceProvider.Get<GoHomeAndSleepTilRested>());
    }
}
public class Elsa : BaseGameEntity
{
    public bool IsCooking { get; set; }

    private void Awake()
    {
        _fsm = new GameFSM(this, ServiceProvider.Get<WifeGlobalState>());
        ChangeState(ServiceProvider.Get<WifeDoHouseWork>());
    }
}
游戏控制器
using System.Collections;
using System.Reflection;
using SharedFSM;
using SharedFSM.Entities;
using SharedFSM.Msgs;
using UnityEngine;

public class GameController : MonoBehaviour
{
    private IFSMEntityManager _entityManager;

    private void Awake()
    {
        // 注册所有state
        SimpleServiceContainer.Instance.RegisterAllImplementations<IGameState>(
            // 当前程序集
            Assembly.GetExecutingAssembly()
            //Assembly.Load("Other")  // 其他程序集)
            );

        SimpleServiceContainer.Instance.Register<IFSMEntityManager, FSMEntityManager>();
        SimpleServiceContainer.Instance.Register<MessageDispatcher>();

        DontDestroyOnLoad(gameObject);
    }

    private void Start()
    {
        // 通过接口获取管理器,不依赖具体实现
        _entityManager = ServiceProvider.Get<IFSMEntityManager>();

        // 初始化妻子Elsa
        var elsaObject = new GameObject("Elsa");
        var elsa = elsaObject.AddComponent<Elsa>();
        elsa.ID = 2;
        _entityManager.RegisterEntity(elsa);

        StartCoroutine(InitBob());
    }

    private IEnumerator InitBob()
    {
        // 一段时间后初始化Bob
        yield return new WaitForSeconds(2);
        var bobObject = new GameObject("Bob");
        var bob = bobObject.AddComponent<Bob>();
        bob.ID = 1;
        _entityManager.RegisterEntity(bob);
    }
}
此处省略状态类
在unity运行

新建一个空场景,加两个空对象,分别挂载 MessageController 和GameController

设计思路

状态机核心(FSM)

public class FSM<TState, TOwner> where TState : IState<TOwner>
{
    protected readonly TOwner Owner;

    public TState PreviousState { get; protected set; }
    public TState CurrentState { get; protected set; }
    public TState GlobalState { get; protected set; }

    public FSM(TOwner owner, TState globalState = default)
    {
        Owner = owner;
        GlobalState = globalState;
    }

    public void ChangeState(TState newState)
    {
        // 先执行上一个状态的退出,在执行当前状态的进入。
        PreviousState = CurrentState;
        CurrentState?.ExitState(Owner);

        CurrentState = newState;
        CurrentState.EnterState(Owner);
    }

    public void Update()
    {
        CurrentState?.UpdateState(Owner);
        GlobalState?.UpdateState(Owner);
    }

    public void RevertToPreviousState()
    {
        if (PreviousState != null)
        {
            ChangeState(PreviousState);
        }
    }

    public bool HandleMessage(Telegram msg)
    {
        if (CurrentState != null && CurrentState.OnMessage(Owner, msg))
        {
            return true;
        }

        if (GlobalState != null && GlobalState.OnMessage(Owner, msg))
        {
            return true;
        }

        return false;
    }
}

public interface IState<TOwner>
{
    void EnterState(TOwner owner = default);
    void UpdateState(TOwner owner = default);
    void ExitState(TOwner owner = default);
    bool OnMessage(TOwner owner, Telegram msg);
}
设计思路
  • 全局状态:例如,在《模拟人生》中,Sim可能会感到本能的迫切要求,不得不去洗手间去方便。这种急切的需求会发生在Sim的任何状态或任何可能的时间。最好创建一个全局状态,这样每次FSM更新时就会被调用。

  • 记录上一个状态:例如,还是在《模拟人生》中,Sim去完洗手间,还需要回到之前的状态,这种需求就需要记录上一个状态。

  • 消息系统:HandleMessage函数处理外部消息。例如,《刺客信条》中,玩家进入了敌人的视野范围,敌人就会停止巡逻,开始攻击追击玩家。

消息系统(Message Dispatcher)

public class MessageDispatcher
{
    // 通过接口依赖,而非具体类
    private readonly IFSMEntityManager _entityManager;

    public MessageDispatcher()
    {
        _entityManager = ServiceProvider.Get<IFSMEntityManager>();
    }

    public SortedSet<Telegram> PriorityQueue { get; private set; } =
        new SortedSet<Telegram>(Comparer<Telegram>.Create((t1, t2) => t1.DispatchTime.CompareTo(t2.DispatchTime)));

    public void DispatchMessage(double delay, int sender, int receiver, int msg, object extraInfo = null)
    {
        if (delay <= 0)
        {
            var receiverEntity = _entityManager?.GetEntityFromID(receiver);
            receiverEntity?.HandleMessage(new Telegram(sender, receiver, msg, 0, extraInfo));
        }
        else
        {
            var telegram = new Telegram(sender, receiver, msg, Time.time + delay, extraInfo);
            PriorityQueue.Add(telegram);
        }
    }
}

    public class MessageController : MonoBehaviour
    {
        private IFSMEntityManager _entityManager;
        private MessageDispatcher _messageDispatcher;

        private void Awake()
        {
            DontDestroyOnLoad(gameObject);
        }

        private void Start()
        {
            _entityManager = ServiceProvider.Get<IFSMEntityManager>();
            _messageDispatcher = ServiceProvider.Get<MessageDispatcher>();
        }

        private void Update()
        {
            DispatchDelayedMessages();
        }

        private void DispatchDelayedMessages()
        {
            while (_messageDispatcher.PriorityQueue.Count > 0 && _messageDispatcher.PriorityQueue.Min.DispatchTime <= Time.time)
            {
                var telegram = _messageDispatcher.PriorityQueue.Min;
                var receiverEntity = _entityManager?.GetEntityFromID(telegram.Receiver);
                receiverEntity?.HandleMessage(telegram);
                _messageDispatcher.PriorityQueue.Remove(telegram);
            }
        }
    }
设计思路
  • 支持"5秒后通知我"这样的时序逻辑

  • 使用 SortedSet 保证 O(log n) 的插入和 O(1) 的最小值获取

  • 通过实体管理器获取实体,_entityManager.GetEntityFromID

实体管理

public class FSMEntity<TState, TOwner> : MonoBehaviour, IFSMEntity
    where TState : IState<TOwner>
    where TOwner : IFSMEntity
{
    protected FSM<TState, TOwner> _fsm;

    public int ID { get; set; }

    private void Update()
    {
        _fsm?.Update();
    }

    public void ChangeState(TState state)
    {
        _fsm?.ChangeState(state);
    }

    public void RevertToPreviousState()
    {
        _fsm?.RevertToPreviousState();
    }

    public virtual bool HandleMessage(Telegram msg)
    {
        return _fsm?.HandleMessage(msg) ?? false;
    }
}

public class FSMEntityManager : IFSMEntityManager
{
    private Dictionary<int, IFSMEntity> _entityMap = new Dictionary<int, IFSMEntity>();

    public FSMEntityManager() { }

    public void RegisterEntity(IFSMEntity entity)
    {
        if (entity != null)
        {
            _entityMap[entity.ID] = entity;
        }
    }

    public IFSMEntity GetEntityFromID(int id)
    {
        if (_entityMap.TryGetValue(id, out var entity))
        {
            return entity;
        }
        return null;
    }

    public void RemoveEntity(IFSMEntity entity)
    {
        if (entity != null)
        {
            _entityMap.Remove(entity.ID);
        }
    }
}
设计思路
  • 一个智能体要做的事情就是去拥有一个StateMachine类的实例,并且为了得到完全的FSM功能,实现一个方法来更新状态机。

  • 在实体管理器中注册,注销,查找实体。

一个简单的服务定位器

当然,你应该使用更加完善的服务定位器或依赖注入框架,而不是这个简易版。

单例的状态类,导致了状态内部不能存储与具体实例相关的数据,否则当多个实体共享同一个状态实例时,就会出现数据污染的问题。

 public class SimpleServiceContainer
 {
     private static SimpleServiceContainer instance;
     public static SimpleServiceContainer Instance
     {
         get
         {
             if (instance == null)
             {
                 lock (typeof(SimpleServiceContainer))
                 {
                     if (instance == null)
                     {
                         instance = new SimpleServiceContainer();
                     }
                 }
             }
             return instance;
         }
     }

     private readonly Dictionary<Type, Lazy<object>> _services = new Dictionary<Type, Lazy<object>>();

     public void Register<TService, TImplementation>() where TImplementation : TService, new()
     {
         _services[typeof(TService)] = new Lazy<object>(() => new TImplementation());
     }

     public void Register<TImplementation>() where TImplementation : new()
     {
         _services[typeof(TImplementation)] = new Lazy<object>(() => new TImplementation());
     }

     public T Get<T>() where T : class
     {
         var key = typeof(T);

         if (_services.TryGetValue(key, out var retInstance))
         {
             return retInstance.Value as T;
         }

         return null;
     }

     public void RegisterAllImplementations<TService>(params Assembly[] assemblies)
     {
         var serviceType = typeof(TService);

         var implementations = assemblies.SelectMany(a => a.GetTypes())
                 .Where(t => serviceType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && t.GetConstructors().Length > 0);

         foreach (var implementation in implementations)
         {
             _services[implementation] = new Lazy<object>(() => Activator.CreateInstance(implementation));
         }
     }
     // 自动注册泛型接口 IState<T> 的实现类
     public void AutoRegisterGenericInterfaces(params Assembly[] assemblies)
     {
         var types = assemblies.SelectMany(a => a.GetTypes())
             .Where(t => t.GetInterfaces()
                 .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IState<>)));

         foreach (var type in types)
         {
             _services[type] = new Lazy<object>(() => Activator.CreateInstance(type));
         }
     }
 }

 public static class ServiceProvider
 {
     public static T Get<T>() where T : class
     {
         return SimpleServiceContainer.Instance.Get<T>();
     }
 }
设计思路
  • Lazy:服务只在首次获取时才实例化,避免启动时的大量对象创建

  • 双检锁单例模式:确保线程安全的同时保持高性能

  • 自动注册机制:通过反射自动扫描并注册 IState 的实现

【信息科学与工程学】【运营科学】第二篇 C4信息与通信网络运营 (C4) ——数据中心网络运营04 典型值:λ1​∈[−0.02,−0.01], λ2​∈[−0.01,0], η1​,η3​∈[0.1,0.3], η2​,η4​∈[0.05,0.15]参数:δ1​∈[−0.8,−0.3], δ2​∈[−0.5,−0.2], δ3​∈[−0.4,−0.1], δ4​∈[−0.6,−0.2]参数:φ1​∈[0.1,0.3], φ2​∈[−0.2,−0.05], φ3​∈[−0.1,0.1], φ4​∈[−0.3,−0.1]参数:α∈[0.3,0.6], β∈[0.2,0.4], γ∈[0.01,0.03] 阅读详情

相关推荐

LVDS接口总结--(0)LVDS硬件电路接口

xilinx LVDS接口硬件总结

zidan1412的博客 9717

AI如何驱动机器人:从感知、决策到控制的系统架构与实战

机器人技术正从传统的程序化控制,迈向由人工智能驱动的自主智能体时代。其核心原理在于构建一个类似生物体的感知-决策-执行闭环系统。通过多传感器融合(如激光雷达、视觉、IMU)实现环境感知,利用同步定位与地图构建(SLAM)等技术确定自身位置。在决策层,任务规划与路径规划算法(如A*、DWA)负责高层任务分解与导航,而强化学习等AI方法正赋予机器人更强大的自适应学习能力。控制层则通过PID、模型预测控制(MPC)等算法,将规划指令精确转化为关节或轮子的运动。这一技术融合的价值在于让机器人能在复杂、动态的非结构化

weixin_34293141的博客 438

技术美术个人笔记(十三)——Houdini金属弯曲模拟

最近接收到使用Houdini自动解算破损/砸坏/扭曲汽车的需求,以此文记录制作金属弯曲自动解算工具时使用的部分节点解析;

小谁的博客 2148

AI 助手 2.0:多轮对话、卡片化与文章页感知

新增列表卡片实现无封面排版与AI摘要动态拉取,通过重构提示词、前端渲染处理及结构化数据输出,解决重复推荐、标签泄露、上下文混乱等问题。文章与友链共用卡片样式,支持多轮对话上下文管理,提升体验一致性。

Eucalyptus's blog 136

GitHub 新手使用手册

理论说太多容易忘,来实践实践吧。

2501_90226046的博客 1052

Aras Innovator系统配置-01对象类(一)

对象类配置-对象类属性说明

企业信息化(PLM,ERP.MES) 2032

“完成”是什么意思——Plan 步骤状态机的七个 Bug

本文剖析了Agent系统中“Plan步骤从pending到done”状态迁移的七个真实Bug,揭示其背后深层设计问题:状态机语义复杂、终态不闭合、状态与事件脱节、生命周期未递归终结等。教训聚焦于:终态应使用枚举而非布尔组合,同状态迁移需幂等,状态变更与事件发射必须原子化,父生命周期结束时须收口子状态,错误提示应引导模型行为。这些原则适用于任何多参与者、异步执行的智能工作流系统。

qinglongcn的博客 285

无痛安装及初始配置 PHP 8.0.3 + Apache 2.4 + MySQL 8.0

PHP 8.0.3 + Apache 2.4 + MySQL 8.0MySQL 8.0.23 安装下载安装配置环境变量初始化MySQLPHP8.0.3 安装下载安装配置环境变量Apache 2.4 安装下载安装启动 MySQL 8.0.23 安装 下载 到官方网站下载:https://dev.mysql.com/downloads/mysql/ 目前的最新版本是MySQL 8.0.23 在Windows上有两种安装方式,一个是zip压缩文件解压然后配置,另一个是图像化界面安装。 这里选择第一种方法安装:

Kuang Da's Blog 6208

Spring 中的 entity 和 DTO 是什么,有什么区别

Entity 是数据库表的映射,DTO 是接口间传输数据的对象,职责分明不可混用。 Entity 与表字段一一对应,包含敏感信息且随数据库结构变动;DTO 仅承载接口所需数据,可含派生字段、剔除敏感信息,避免泄露与耦合。直接返回 Entity 会导致信息暴露、结构僵化及懒加载异常。正确做法是通过工具(如 MapStruct)在层间转换,确保安全与灵活性。

马优晨 367

全志A527 T527 android13支持usb摄像头

我们发现usb摄像头在A527 android13上面并不能正常使用,需要支持相关的摄像头。

zhutoubenben的专栏 964

【PyTorch深度学习项目实战100例目录】项目详解 + 数据集 + 完整源码

大家好,我是阿光。本专栏整理了《深度学习100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。正在更新中~ ✨。

优快云 精品推荐 10万+

AI 编译器前端优化全景:9 个 Pass、两类优化、一张地图

页码内容1封面:AI 编译器系列·之前端优化 Summary2” 标题3分层架构图:Python → 框架 → Graph Optimizer → Ops Optimizer → Backend → 硬件4目录:8 项前端优化5目录高亮:左列 4 项标为神经网络相关的优化6目录新增算子替换 - OP Replace,左列变 5 项7右列 4 项标为代码/计算层面的优化8版权页也就是说,真正的信息只有两条前端有 9 个优化(4 + 5);神经网络相关与代码/计算层面。

喜欢打篮球的普通人 196

【小白深度教程 1.32】手把手教你从多视角图像进行 3D 重建(SfM 算法)

重建的目标是从一组照片中推导场景的几何结构,假设摄像机位置和内部参数是已知的或可以从图像集中猜测。 这是通过使用多个照片,在其中应用运动结构法(Structure from Motion,SfM)来解决像素级对应问题,部分地恢复3D信息来实现的。

2401_87064292的博客 2159

一颗芯片的旅程:规格、架构、RTL、GDSII 到硅片

一颗芯片从需求到硅片,历经七次抽象翻译:规格→架构→微架构→RTL→网表→版图→GDSII。每一阶段转换语言,交付特定格式数据,也引入专属错误风险。越早发现缺陷,修复成本越低。流程本质是层层验证与约束传递,最终确保设计可制造、可验证、可工作。

oscar999的专栏 266

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【从零理解 Spring AI · 05】让模型的回答变成结构化数据

前两篇把 ChatClient 用熟了,现在可以回答一个我最早遇到的现实问题了。

nice_lcj520的博客 349

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

前后端Long精度丢失的踩坑

那天前端同事跑过来跟我说,"你那个接口返回的订单ID,更新数据时接口没返回异常,但数据重新查又没更新成功"我当时第一反应是:不可能,后端查出来是什么就返回什么,肯定是你前端哪里处理出了问题。然后我打开浏览器F12一看,Network面板里接口返回的JSON里,orderId 的值是 1784523698741256200,而数据库里存的是 1784523698741256193,后端拿1784523698741256200来更新数据确实没报异常。好家伙,最后几位确实被改了。

欢醉 398

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

怎样设计一个好架构:八个评价维度、四层思考深度,以及 23 种设计模式各自解决什么问题

架构要解决的是变化、理解、故障和协作的成本。本文从耦合、可迁移、可适配、复杂度等八个维度讲怎么评价架构,讲维度之间怎么取舍、架构思考可以分几层,再用可编译的 C 代码过一遍 23 种设计模式。

weixin_52400878的博客 318

工作流 vs 自主 Agent:5 种设计模式与 3 种建模方式详解

本文基于 Anthropic《Building Effective Agents》指南,系统梳理了AI应用中的两类核心架构:Workflow(编排式)与Agent(自主智能体)。强调“能画流程图就不上Agent”,提出从简单提示链到复杂自主决策的5种可组合工作流模式,并给出选型口诀与工程实践建议。核心观点:优先用确定性、可审计的Workflow,仅在任务路径未知时启用Agent,避免盲目复杂化。

分享大数据、java相关的技术 154

设计模式 - 访问者模式

概述 访问者模式(Visitor Pattern)是一种行为型设计模式,它通过为对象结构中的每个元素添加一个访问操作,使得可以在不改变元素类的前提下定义作用于这些元素的新操作。

2601_96797912的博客 180

设计模式速查

本文梳理12个核心设计模式,涵盖创建型、结构型与行为型,提供简洁Java实现与面试要点。重点解析单例(静态内部类)、工厂(方法/抽象)、建造者(链式构建)等高频模式,强调代码可维护性与扩展性,附速记口诀与对比表格,助力开发与面试双提升。

ttlovet的博客 163

【第四部分:Agentic 智能体设计模式】17. Agent 路由模式详解:从意图识别到动态模型选择

本文介绍 Agent Routing(路由模式)的基本原理与工程实现,重点分析任务路由、模型路由、工具路由和专业 Agent 路由的区别及适用场景。结合 BaseMetas EKA 企业知识助手,介绍规则与模型混合路由、Java Router 实现、复合任务处理、低置信度兜底和误路由评估,并以 Jev 专用决策模型、动态模型选择及 Tool Search 等近期技术为例,说明如何在保证权限与执行安全的前提下,提高 Agent 任务分发的准确性和效率。

weixin_42258782的博客 628

设计模式与生活⑬:你坐几号线,谁来运营是另一回事

一到周五,小王的心情就会好一点。倒不是工作少了,而是想到这一天忙完,后面接着双休,心里就有盼头。地铁上,他抬头看标识,发现一件平时没留意的事:5 号线、10 号线常见的是“北京地铁”,4 号线却写着“京港地铁”。再想想,北京的机场也各有各的名字:首都机场、大兴机场。可在成都坐地铁,日常看到的统一称谓就是“成都地铁”。都是出行,名称和背后的主体并不完全一样。对乘客来说,常做的事还是那几样:查线路、进站、上车、到站下车。坐几号线是一条信息,谁来运营是另一条信息,不必揉成一团。同样是在北京坐地铁,线路和运营方并不

工一木子 222

设计模式与生活⑩:地铁没变,你换了个状态

周二,没什么特别。小王照旧早起、吃早餐、坐地铁,到公司做日常开发。中午吃完饭,再去公司旁边遛一圈。普通的牛马的一天。地铁上倒有两个小片段。刚上去没座,他一只手扶杆,一只手刷手机。等他反应过来,已经坐过了一站。后来有了座,他靠着窗户,没一会儿睡着了。还是那班地铁,人已经从刷手机切到了睡觉还是小王,还是“列车到站”这件事,可他的反应已经不一样:刷手机时没留意,坐着清醒时会看站名,睡着后又听不见报站。今天聊的就是这个思路:**状态模式(State)——对象的内部状态变了,处理同一件事的方式也跟着变。

工一木子 175

设计模式概述

设计模式的意义、作用、分类等

lzy15192536135的博客 398

设计模式与生活⑭:一栋楼,共用一份热

今天是立冬。天气预报说夜里有暴雪,白天最高也就 4 度。早上起来,屋里比外面强不了多少。窗玻璃上蒙着一层水汽,摸一把暖气片,凉的。物业群里发了通知:今天开始试供暖,暖气片会慢慢热起来,顶层记得排气,边户留意自家热不热。隔了会儿再摸,暖气片上热下凉,温温的,不烫手。再晚一点,整间屋子总算有了点热乎气。一天里最踏实的一件事,是知道这栋楼里不只自己一户在等热。热力站烧的是一锅水,顺着管网送到每家,各家只管拧自己那个阀门。试供暖第一天,暖气片温而不烫今天聊的。

工一木子 320

设计模式与生活⑪:地铁里被打扰,别总靠自己硬扛

周三的地铁上,小王本来想在脑子里过一遍今天要做的事。旁边一位大哥戴着耳机打电话。耳机里对方说什么,别人听不见;大哥自己的声音,却一句一句落在外面。声音不算大,也没有嚷,可就是不停。你刚把那件事想到一半,旁边又接上一句;刚想不去留意,下一句又钻进来了。最磨人的不是哪一句特别响,而是你拿不准这通电话还要多久。小王没说什么,站起来,往车厢另一头挪了挪。距离拉开,思绪才慢慢接了回去。耳机收住了电话另一端的声音,收不住说话人的声音这让他想起之前的另一次经历。

工一木子 155
上一篇: 【温故】设计原则:依赖倒置原则
下一篇: 行为树简介
ZZHSourceCode
博客等级 码龄9年 8粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值