43 — 测试是系统从第一天开始的 TDD一个测试读取世界的状态并断言某个属性成立。一个系统读取世界的状态并写入一个派生结果。两者在结构上是相同的。这不是一句口号。这是一个结构性的实事它允许本书中的每个其他规范无需转换地应用于测试。一个测试夹具是某个滴答时的世界。一个测试是一个写集合为空的系统或者其写集合是一个小的“报告”表。一个测试运行器是与运行模拟器相同的调度器针对世界执行测试的读集合。defno_creature_moves_too_far(pos_x_before:np.ndarray,pos_y_before:np.ndarray,pos_x_after:np.ndarray,pos_y_after:np.ndarray,max_step:float,)-np.ndarray:返回移动超过了 max_step 的生物的索引。 读集合四个位置数组max_step。 写集合空返回一个报告。dxpos_x_after-pos_x_before dypos_y_after-pos_y_before dist_sqdx*dxdy*dyreturnnp.where(dist_sqmax_step*max_step)[0]这是一个系统。读集合四个位置数组加上max_step。写集合一个报告数组。它在模拟器的表上运行。它通过成功时返回空数组、失败时返回非空数组来断言一个属性。相同的代码路径同时服务于测试和检查用途——在测试时断言assert result.size 0在之后运行在生产中一个检查系统可能会记录非空结果而不失败。三个益处复合对 numpy 列的属性测试自然形成。一个属性测试固定一个 RNG 种子运行模拟器 N 个滴答并断言某个属性在每个滴答都成立。如果属性是“每滴答没有生物移动超过max_step”那么断言就是上面的系统。如果属性是“种群数量保持有界”那么断言是world.n_active bound。每个都是一个系统。对事件日志的回放测试自然形成。一个回放测试通过第 37 节的三元存储加载一个记录的日志运行重放器并将结果世界与一个快照进行比较。“测试”就是比较比较是对两个世界的列的系统。集成测试不需要模拟。模拟的存在是因为测试无法执行真实的组件。来自第 35 节的边界即队列规则意味着模拟器内部没有外部组件——每个外部交互都通过队列进行。一个测试用合成输入填充输入队列运行模拟器对输出队列进行断言。没有unittest.mock没有monkeypatch没有“修补这个导入以返回那个假数据”——测试读取与模拟器读取相同的数据。Python 特定的校准pytest 很好。Pytest 是通用的 Python 测试工具它在本章没有涵盖的方面确实很好发现、报告、参数化、作为设置的夹具。使用 pytest。这里的教训不是反 pytest而是将你的断言编写为系统然后将它们放在一个 pytest 函数中以便 pytest 运行它们。系统形态和 pytest 的测试架是正交的。unittest.mock是 ECS 风格代码的错误工具。边界即队列规则消除了模拟存在所伪造的事物——没有需要修补的外部服务没有需要拦截的requests.get没有需要冻结的时钟。如果你发现自己需要mock.patch那么你正在测试的系统存在第 35 节的泄漏解决方法是通过队列连接泄漏的依赖项而不是模拟它。simlog 的 test_simlog.py713 行完全覆盖 simlog 的契约使用了零个模拟——每个测试都设置真正的 numpy 数组运行真正的log()调用并读回真正的.npz输出。基于属性的测试属于这里。hypothesis是 Python 生态系统中基于属性的测试库它生成输入并缩减失败案例。对于读集合是良好类型化的 numpy 列的系统hypothesis通过hypothesis-numpy干净地集成。模拟器的不变量“种群数量保持有界”、“能量非负”、“没有槽位有两个 ID”是完美的属性测试材料——让hypothesis生成世界状态对每个状态断言不变量。从第一天开始的 TDD从第 5 节开始本书中的每个概念都是测试优先的。最小的案例是什么最大的案例是什么对于np.uint8、np.uint32、10,000 个实体 ID答案应该是什么纸牌游戏练习从询问“对于 0 张牌、1 张牌、52 张牌这应该返回什么”开始。模拟器的练习询问“在 100 次滴答没有食物后种群数量应该是多少”测试先来实现跟随。这种规范在三个方面得到回报测试与代码一起增长。每个新系统都有其作为相邻函数的测试共享相同的读/写约定。测试重构与系统重构没有区别。检查和测试是相同的代码。来自第 13 节的检查系统模式与测试模式相同对所有表的只读访问输出一个报告。在生产中检查不存在或在--debug模式下运行在测试中它存在并进行断言。相同的源代码不同的调度。确定性使测试值得信赖。第 16 节的规则意味着测试是可重现的。使用种子0xCAFE失败的测试每次、每台机器上都会使用0xCAFE失败——只要你遵守第 16 节的配方没有原始集合迭代系统中没有挂钟一个带种子的 RNG。运行 8 个并行工作进程的 pytest-xdist 将会暴露单进程 pytest 不会暴露的集合迭代错误正如第 16 节练习 7 所预测的那样。本书即将结束四十三个概念十个阶段一个贯穿全文的模拟器。在最后阶段命名的规范——机制与策略、面向压缩的编程、你只能修复你写的东西、测试是系统——是将其余部分维系在一起的规则。它们不是新的架构。它们是前几章构建的架构如何保持可维护性的方法。一个尊重所有四十三个节点的模拟器其状态在 numpy 列中其转换是系统其滴答是纯函数其历史是日志其持久性是转置其测试是系统其依赖项是你睁大眼睛下的赌注。这就是数据导向的程序。这就是本书。练习作为系统的测试。采用正文中的no_creature_moves_too_far系统。在你的模拟器的 DAG 中在一个--test标志后面添加它。运行 100 个滴答。该系统应该报告零个可疑生物。一个属性测试。使用种子0xCAFE运行模拟器 1000 个滴答。断言world.n_active 2 * initial_n_active。使用相同的种子运行两次两次运行应该报告相同的结果在相同的滴答处通过或失败。一个回放测试。通过第 36 节的np.savez保存一次 100 滴答运行的输入队列。将它加载到一个新的模拟器中并回放。在 100 次滴答后对两个世界进行哈希。它们必须匹配。为新系统进行 TDD。选择一个你尚未构建的行为——比如说“能量超过 50 的生物生长更慢”。首先编写测试最小的案例是什么一个生物最大的呢一百万个然后编写系统。确认测试通过。阅读 simlog 测试。打开.archive/simlog/test_simlog.py。注意没有模拟。注意每个测试夹具都是在测试主体中设置的真实 numpy 数组。测试文件有 713 行对应一个 700 行的库——比例大约为 1:1这对于必须正常工作的代码来说是正确的比例。InspectionSystem 的连接。从练习 1 中取出测试和来自第 13 节的检查系统思想。论证为什么它们在结构上是相同的——相同的读集合相同的无写集合相同的调度槽位。pytest-xdist 作为确定性检查。转换你的测试套件使其在pytest -n 8并行工作进程下运行。任何在pytest下通过但在pytest -n 8下失败的测试都有一个非确定性泄漏通常是set迭代通常是挂钟。修复泄漏第 16 节的配方是补救措施。挑战一个就是模拟器调度器的测试运行器。实现一个微型的测试运行器它与模拟器调度器的唯一区别是它在 DAG 中包含哪些系统生产系统用于生产运行测试和检查系统用于测试运行。两个二进制文件共享大部分代码区别在于系统列表。接下来是什么你已经完成了主干。第 44 节——你构建了什么回顾了你构建的形态并提出了本书有意没有解决的问题。44 — 你构建了什么前面的四十三个章节是一次漫长的攀登。这一节是向下俯瞰。你构建了一个小型生态系统模拟器它确定性地运行从一百个生物扩展到流式处理工作负载并在每个滴答暴露其状态以供检查。你使用numpy数组和函数完成了这些——没有类层次结构没有 ORM没有框架没有异步运行时。使其工作的规范就是本书的全部内容。承载一切的形态三种模式无处不在表而不是对象。一个生物不是一个带有方法的字段的class。它是一个跨列的行通过索引保持对齐——pos_x[i]、pos_y[i]、energy[i]。每一列是一个 numpy 数组。这些列每个都有一个写入者它们步调一致地增长和收缩。没有任何容器将它们结合在一起——只有规范。系统而不是状态。行为是表上的函数。motion读取vel写入pos。apply_starve读取energy将 ID 推送到to_remove。每个系统都有一个名称、一个读集合、一个写集合。模拟器是按顺序组合的系统的 DAG。状态变化发生在滴答之间而不是滴答内部。机制与策略分离。内核暴露动词插入、移除、交换、推送到缓冲区、批量清理。规则存在于边缘生物何时死亡、食物何时生成、什么算作碰撞。相同的内核运行每种变体策略在不更改内核的情况下变化。这三个不是 Python 特有的。它们甚至不是 ECS 特有的。它们是数据导向设计所命名的内容。本书的其余部分——局部性、并行性、持久性、随时算法——都源于认真对待这三者。这种方法在 Python 中具体能买到什么默认速度快因为 numpy SoA 布局匹配机器并且内部循环脱离了解释器。回答了“Python 慢”的问题。当 Python 是内部循环时它是慢的。当 numpy 是内部循环而 Python 是编排时Python不慢——它正好是编排的适当抽象级别。无锁的确定性因为顺序是契约一旦你将工作分区到多进程 共享内存GIL 就不再起作用第 31 节。可测试性因为每个系统都是其输入的纯函数。没有unittest.mock没有猴子补丁没有特定于框架的魔法。易上手性因为数据是可见的。读者可以print(column[:10])查看任何列看到世界。重构廉价因为没有带有隐藏状态要迁移的对象没有含义取决于上下文的Optional[X]字段没有需要跟踪的继承链。这种方法付出了什么代价抽象较少。你感受到机器。有些人觉得这令人解放有些人觉得这令人疲惫。更多的规范。单写入者规则、变更缓冲、步调一致的排序——Python 不强制执行这些。你强制执行。借用检查器不会来拯救你。不太符合 Python 惯例。本书很少使用 Python 教程所教的内容没有类层次结构很少有装饰器没有Protocol没有pydantic没有 ORM。惯用 Python 看起来不同。接受标准惯例训练的工程师会发现这段代码令人惊讶这种惊讶正是关键。一个不同的思维模型。接受 OOP 训练的工程师不会自然地想到表。转换成本是真实的。本书没有解决的问题本书做出了选择。其他书做出了不同的选择。值得知道你的立场为什么不选择 Bevy或其他现有的 ECS 框架启动更快但更难看透。我们是故意选择慢速路线的。在第 43 节之后你可以阅读 Bevy 的 ECS 源代码或任何生产 ECS并判断它们的选择是否与你的匹配。一行真的比一个类好吗对于单个生物不——class Card(suit, rank)很好。对于一百万个是的——第 3 节的测量结果解决了这个问题。交叉点取决于你的工作负载本书命名了权衡但没有规定。这可能是用 Rust、Zig 或 C 实现的吗是的。这些想法与语言无关。Python 贡献了可访问性和 numpy 生态系统其余的是布局规范。本书的 Rust 版本面向希望获得编译时保证的读者而本书版本通过约定强制执行。类型、数据类、异步呢Python 最受欢迎的两个特性在主干的出现很少。typing和dataclass出现在边界函数签名、配置对象、命名引用如CreatureRef它们没有在热循环内部赢得一席之地。async根本没有出现——模拟器是 CPU 密集型和同步的async 适用于 I/O 密集型系统其编排真正在等待外部事件。未来的工作可能会探索这些中的每一个在 Python ECS 中在哪里物有所值——通常是在边缘CLI 解析、配置、边界的网络 I/O而不是内核。网络和回滚呢第 31-34 节涵盖了单机并发。将世界分布在多台机器上是另一本书——网络跳跃税第 39 节使其成为滴答率工作的错误默认选项只有在单机确实无法容纳工作负载时才考虑它。pandas、ORM、异步框架呢当工作负载真正适合它们的压缩时它们赢得一席之地第 41 节、第 42 节。对于一个数据是列式 SoA 且滴答是 CPU 密集型的模拟器它们都不适合。对于其他工作负载它们可能适合。规范是有意识地决定而不是默认选择流行的工具。接下来去哪里阅读 Mike Acton 的“Data-Oriented Design and C”CppCon 2014。四十五分钟你将找到这种方法最集中的论证。观看 Casey Muratori 的Handmade Hero关于网格存储和缓存局部性的剧集。另一条通往相同结论的路径。打开 Bevy 的bevy_ecscrateRust或任何你选择的语言的生产 ECS。你将识别出每种模式。名称会不同形态是相同的。阅读本书的 Rust 版本。相同的架构不同的强制执行。观察借用检查器执行本书要求你通过规范完成的工作是一种真正有用的校准。扩展模拟器。模拟器规范 中标出的遗传学和捕食者-猎物扩展在不离开你已经构建的框架的情况下开辟了新天地。将架构应用于模拟器之外。第 35 节 第 37 节是带有确定性归约器的事件源架构相同的模式适用于请求处理程序、控制循环、代理系统、任何需要随着负载演变的状态。模拟器是工作示例架构是课程。本书到此结束。模拟器不会——只要你保持规范它就会一直运行。
DeepSeek总结的使用实体-组件-系统和基于存在性处理进行Python编程43-44(完)
43 — 测试是系统从第一天开始的 TDD一个测试读取世界的状态并断言某个属性成立。一个系统读取世界的状态并写入一个派生结果。两者在结构上是相同的。这不是一句口号。这是一个结构性的实事它允许本书中的每个其他规范无需转换地应用于测试。一个测试夹具是某个滴答时的世界。一个测试是一个写集合为空的系统或者其写集合是一个小的“报告”表。一个测试运行器是与运行模拟器相同的调度器针对世界执行测试的读集合。defno_creature_moves_too_far(pos_x_before:np.ndarray,pos_y_before:np.ndarray,pos_x_after:np.ndarray,pos_y_after:np.ndarray,max_step:float,)-np.ndarray:返回移动超过了 max_step 的生物的索引。 读集合四个位置数组max_step。 写集合空返回一个报告。dxpos_x_after-pos_x_before dypos_y_after-pos_y_before dist_sqdx*dxdy*dyreturnnp.where(dist_sqmax_step*max_step)[0]这是一个系统。读集合四个位置数组加上max_step。写集合一个报告数组。它在模拟器的表上运行。它通过成功时返回空数组、失败时返回非空数组来断言一个属性。相同的代码路径同时服务于测试和检查用途——在测试时断言assert result.size 0在之后运行在生产中一个检查系统可能会记录非空结果而不失败。三个益处复合对 numpy 列的属性测试自然形成。一个属性测试固定一个 RNG 种子运行模拟器 N 个滴答并断言某个属性在每个滴答都成立。如果属性是“每滴答没有生物移动超过max_step”那么断言就是上面的系统。如果属性是“种群数量保持有界”那么断言是world.n_active bound。每个都是一个系统。对事件日志的回放测试自然形成。一个回放测试通过第 37 节的三元存储加载一个记录的日志运行重放器并将结果世界与一个快照进行比较。“测试”就是比较比较是对两个世界的列的系统。集成测试不需要模拟。模拟的存在是因为测试无法执行真实的组件。来自第 35 节的边界即队列规则意味着模拟器内部没有外部组件——每个外部交互都通过队列进行。一个测试用合成输入填充输入队列运行模拟器对输出队列进行断言。没有unittest.mock没有monkeypatch没有“修补这个导入以返回那个假数据”——测试读取与模拟器读取相同的数据。Python 特定的校准pytest 很好。Pytest 是通用的 Python 测试工具它在本章没有涵盖的方面确实很好发现、报告、参数化、作为设置的夹具。使用 pytest。这里的教训不是反 pytest而是将你的断言编写为系统然后将它们放在一个 pytest 函数中以便 pytest 运行它们。系统形态和 pytest 的测试架是正交的。unittest.mock是 ECS 风格代码的错误工具。边界即队列规则消除了模拟存在所伪造的事物——没有需要修补的外部服务没有需要拦截的requests.get没有需要冻结的时钟。如果你发现自己需要mock.patch那么你正在测试的系统存在第 35 节的泄漏解决方法是通过队列连接泄漏的依赖项而不是模拟它。simlog 的 test_simlog.py713 行完全覆盖 simlog 的契约使用了零个模拟——每个测试都设置真正的 numpy 数组运行真正的log()调用并读回真正的.npz输出。基于属性的测试属于这里。hypothesis是 Python 生态系统中基于属性的测试库它生成输入并缩减失败案例。对于读集合是良好类型化的 numpy 列的系统hypothesis通过hypothesis-numpy干净地集成。模拟器的不变量“种群数量保持有界”、“能量非负”、“没有槽位有两个 ID”是完美的属性测试材料——让hypothesis生成世界状态对每个状态断言不变量。从第一天开始的 TDD从第 5 节开始本书中的每个概念都是测试优先的。最小的案例是什么最大的案例是什么对于np.uint8、np.uint32、10,000 个实体 ID答案应该是什么纸牌游戏练习从询问“对于 0 张牌、1 张牌、52 张牌这应该返回什么”开始。模拟器的练习询问“在 100 次滴答没有食物后种群数量应该是多少”测试先来实现跟随。这种规范在三个方面得到回报测试与代码一起增长。每个新系统都有其作为相邻函数的测试共享相同的读/写约定。测试重构与系统重构没有区别。检查和测试是相同的代码。来自第 13 节的检查系统模式与测试模式相同对所有表的只读访问输出一个报告。在生产中检查不存在或在--debug模式下运行在测试中它存在并进行断言。相同的源代码不同的调度。确定性使测试值得信赖。第 16 节的规则意味着测试是可重现的。使用种子0xCAFE失败的测试每次、每台机器上都会使用0xCAFE失败——只要你遵守第 16 节的配方没有原始集合迭代系统中没有挂钟一个带种子的 RNG。运行 8 个并行工作进程的 pytest-xdist 将会暴露单进程 pytest 不会暴露的集合迭代错误正如第 16 节练习 7 所预测的那样。本书即将结束四十三个概念十个阶段一个贯穿全文的模拟器。在最后阶段命名的规范——机制与策略、面向压缩的编程、你只能修复你写的东西、测试是系统——是将其余部分维系在一起的规则。它们不是新的架构。它们是前几章构建的架构如何保持可维护性的方法。一个尊重所有四十三个节点的模拟器其状态在 numpy 列中其转换是系统其滴答是纯函数其历史是日志其持久性是转置其测试是系统其依赖项是你睁大眼睛下的赌注。这就是数据导向的程序。这就是本书。练习作为系统的测试。采用正文中的no_creature_moves_too_far系统。在你的模拟器的 DAG 中在一个--test标志后面添加它。运行 100 个滴答。该系统应该报告零个可疑生物。一个属性测试。使用种子0xCAFE运行模拟器 1000 个滴答。断言world.n_active 2 * initial_n_active。使用相同的种子运行两次两次运行应该报告相同的结果在相同的滴答处通过或失败。一个回放测试。通过第 36 节的np.savez保存一次 100 滴答运行的输入队列。将它加载到一个新的模拟器中并回放。在 100 次滴答后对两个世界进行哈希。它们必须匹配。为新系统进行 TDD。选择一个你尚未构建的行为——比如说“能量超过 50 的生物生长更慢”。首先编写测试最小的案例是什么一个生物最大的呢一百万个然后编写系统。确认测试通过。阅读 simlog 测试。打开.archive/simlog/test_simlog.py。注意没有模拟。注意每个测试夹具都是在测试主体中设置的真实 numpy 数组。测试文件有 713 行对应一个 700 行的库——比例大约为 1:1这对于必须正常工作的代码来说是正确的比例。InspectionSystem 的连接。从练习 1 中取出测试和来自第 13 节的检查系统思想。论证为什么它们在结构上是相同的——相同的读集合相同的无写集合相同的调度槽位。pytest-xdist 作为确定性检查。转换你的测试套件使其在pytest -n 8并行工作进程下运行。任何在pytest下通过但在pytest -n 8下失败的测试都有一个非确定性泄漏通常是set迭代通常是挂钟。修复泄漏第 16 节的配方是补救措施。挑战一个就是模拟器调度器的测试运行器。实现一个微型的测试运行器它与模拟器调度器的唯一区别是它在 DAG 中包含哪些系统生产系统用于生产运行测试和检查系统用于测试运行。两个二进制文件共享大部分代码区别在于系统列表。接下来是什么你已经完成了主干。第 44 节——你构建了什么回顾了你构建的形态并提出了本书有意没有解决的问题。44 — 你构建了什么前面的四十三个章节是一次漫长的攀登。这一节是向下俯瞰。你构建了一个小型生态系统模拟器它确定性地运行从一百个生物扩展到流式处理工作负载并在每个滴答暴露其状态以供检查。你使用numpy数组和函数完成了这些——没有类层次结构没有 ORM没有框架没有异步运行时。使其工作的规范就是本书的全部内容。承载一切的形态三种模式无处不在表而不是对象。一个生物不是一个带有方法的字段的class。它是一个跨列的行通过索引保持对齐——pos_x[i]、pos_y[i]、energy[i]。每一列是一个 numpy 数组。这些列每个都有一个写入者它们步调一致地增长和收缩。没有任何容器将它们结合在一起——只有规范。系统而不是状态。行为是表上的函数。motion读取vel写入pos。apply_starve读取energy将 ID 推送到to_remove。每个系统都有一个名称、一个读集合、一个写集合。模拟器是按顺序组合的系统的 DAG。状态变化发生在滴答之间而不是滴答内部。机制与策略分离。内核暴露动词插入、移除、交换、推送到缓冲区、批量清理。规则存在于边缘生物何时死亡、食物何时生成、什么算作碰撞。相同的内核运行每种变体策略在不更改内核的情况下变化。这三个不是 Python 特有的。它们甚至不是 ECS 特有的。它们是数据导向设计所命名的内容。本书的其余部分——局部性、并行性、持久性、随时算法——都源于认真对待这三者。这种方法在 Python 中具体能买到什么默认速度快因为 numpy SoA 布局匹配机器并且内部循环脱离了解释器。回答了“Python 慢”的问题。当 Python 是内部循环时它是慢的。当 numpy 是内部循环而 Python 是编排时Python不慢——它正好是编排的适当抽象级别。无锁的确定性因为顺序是契约一旦你将工作分区到多进程 共享内存GIL 就不再起作用第 31 节。可测试性因为每个系统都是其输入的纯函数。没有unittest.mock没有猴子补丁没有特定于框架的魔法。易上手性因为数据是可见的。读者可以print(column[:10])查看任何列看到世界。重构廉价因为没有带有隐藏状态要迁移的对象没有含义取决于上下文的Optional[X]字段没有需要跟踪的继承链。这种方法付出了什么代价抽象较少。你感受到机器。有些人觉得这令人解放有些人觉得这令人疲惫。更多的规范。单写入者规则、变更缓冲、步调一致的排序——Python 不强制执行这些。你强制执行。借用检查器不会来拯救你。不太符合 Python 惯例。本书很少使用 Python 教程所教的内容没有类层次结构很少有装饰器没有Protocol没有pydantic没有 ORM。惯用 Python 看起来不同。接受标准惯例训练的工程师会发现这段代码令人惊讶这种惊讶正是关键。一个不同的思维模型。接受 OOP 训练的工程师不会自然地想到表。转换成本是真实的。本书没有解决的问题本书做出了选择。其他书做出了不同的选择。值得知道你的立场为什么不选择 Bevy或其他现有的 ECS 框架启动更快但更难看透。我们是故意选择慢速路线的。在第 43 节之后你可以阅读 Bevy 的 ECS 源代码或任何生产 ECS并判断它们的选择是否与你的匹配。一行真的比一个类好吗对于单个生物不——class Card(suit, rank)很好。对于一百万个是的——第 3 节的测量结果解决了这个问题。交叉点取决于你的工作负载本书命名了权衡但没有规定。这可能是用 Rust、Zig 或 C 实现的吗是的。这些想法与语言无关。Python 贡献了可访问性和 numpy 生态系统其余的是布局规范。本书的 Rust 版本面向希望获得编译时保证的读者而本书版本通过约定强制执行。类型、数据类、异步呢Python 最受欢迎的两个特性在主干的出现很少。typing和dataclass出现在边界函数签名、配置对象、命名引用如CreatureRef它们没有在热循环内部赢得一席之地。async根本没有出现——模拟器是 CPU 密集型和同步的async 适用于 I/O 密集型系统其编排真正在等待外部事件。未来的工作可能会探索这些中的每一个在 Python ECS 中在哪里物有所值——通常是在边缘CLI 解析、配置、边界的网络 I/O而不是内核。网络和回滚呢第 31-34 节涵盖了单机并发。将世界分布在多台机器上是另一本书——网络跳跃税第 39 节使其成为滴答率工作的错误默认选项只有在单机确实无法容纳工作负载时才考虑它。pandas、ORM、异步框架呢当工作负载真正适合它们的压缩时它们赢得一席之地第 41 节、第 42 节。对于一个数据是列式 SoA 且滴答是 CPU 密集型的模拟器它们都不适合。对于其他工作负载它们可能适合。规范是有意识地决定而不是默认选择流行的工具。接下来去哪里阅读 Mike Acton 的“Data-Oriented Design and C”CppCon 2014。四十五分钟你将找到这种方法最集中的论证。观看 Casey Muratori 的Handmade Hero关于网格存储和缓存局部性的剧集。另一条通往相同结论的路径。打开 Bevy 的bevy_ecscrateRust或任何你选择的语言的生产 ECS。你将识别出每种模式。名称会不同形态是相同的。阅读本书的 Rust 版本。相同的架构不同的强制执行。观察借用检查器执行本书要求你通过规范完成的工作是一种真正有用的校准。扩展模拟器。模拟器规范 中标出的遗传学和捕食者-猎物扩展在不离开你已经构建的框架的情况下开辟了新天地。将架构应用于模拟器之外。第 35 节 第 37 节是带有确定性归约器的事件源架构相同的模式适用于请求处理程序、控制循环、代理系统、任何需要随着负载演变的状态。模拟器是工作示例架构是课程。本书到此结束。模拟器不会——只要你保持规范它就会一直运行。