目录

背景规则

OOP实现:

分析OOP代码的设计缺陷

Entity-Component-System(ECS)架构

ECS介绍

ECS架构分析

ECS架构改造

背景规则

现在公司用户中心提出一个需求,需要根据用户的会员等级实行不同的程度的打折,会员等级越高打折力度越大。其具体规则如下:

  • 青铜会员,折扣是9.9折;
  • 黄金会员,折扣是8.8折;
  • 铂金会员,折扣是6.6折;
  • 钻石会员,折扣是5折。

OOP实现:

对于熟悉Object-Oriented Programming的同学,一个比较简单的实现是通过类的继承关系(此处省略部分非核心代码):

public abstract class Member {public abstract double discount(double sourcePrice);
}public class BronzeMember extends Member {@Overridepublic double discount(double sourcePrice) {return sourcePrice * 9.9 / 10;}
}public class SilverMember extends Member {@Overridepublic double discount(double sourcePrice) {return sourcePrice * 8.8 / 10;}
}public class GoldMember extends Member {@Overridepublic double discount(double sourcePrice) {return sourcePrice * 6.6 / 10;}
}public class DiamondMember extends Member {@Overridepublic double discount(double sourcePrice) {return sourcePrice * 5 / 10;}
}

然后是简单的单元测试:

 public void discountTest() {Member bronzeMember = new BronzeMember();Member silverMember = new SilverMember();Member goldMember = new GoldMember();Member diamondMember = new DiamondMember();double sourcePrice = 10D;Assert.assertEquals (bronzeMember.discount(sourcePrice), 9.9, 0);Assert.assertEquals (silverMember.discount(sourcePrice), 8.8, 0);Assert.assertEquals (goldMember.discount(sourcePrice), 6.6, 0);Assert.assertEquals (diamondMember.discount(sourcePrice), 5, 0);}

上述代码比较简单,就不做过多的阐述了。

分析OOP代码的设计缺陷

知识级和操作级对象混用

知识级对象定义了对操作级对象的合法配置,根据相应的规则进行不同的约束;

操作级对象定义了模型常变化的部分。

如上述的例子中,抽取知识级公共对象rule作为规则父类,具体规则:

  • 折扣规则,抽取为DiscountRule;
  • 积分规则,抽取为PointRule;

构建模型的规则:抽离可变因素,减少模型的变动,尽量将变化集中在更少的地方。

对象继承导致代码强依赖父类逻辑,违反开闭原则Open-Closed Principle(OCP)

开闭原则(OCP)规定“对象应该对于扩展开放,对于修改封闭“,继承虽然可以通过子类扩展新的行为,但因为子类可能直接依赖父类的实现,导致一个变更可能会影响所有对象。在这个例子里,我们需要给我们的会员增加一个积分的属性,根据不同的等级制度,在不同等级的人在购买商品之后赠送额外的积分。

然后我们需要修改代码,包括:

  • Member中添加积分(point)属性;

  • 然后修改所有会员实现类,在实现中添加赠送积分的方法;
public class BronzeMember extends Member {@Overridepublic double discount(double sourcePrice) {return sourcePrice * 9.9 / 10;}@Overridepublic int addPoint() {return this.point + 10;}
}

在一个复杂的软件中为什么会建议“尽量”不要违背OCP?最核心的原因就是一个现有逻辑的变更可能会影响一些原有的代码,导致一些无法预见的影响。这个风险只能通过完整的单元测试覆盖来保障,但在实际开发中很难保障单测的覆盖率。OCP的原则能尽可能的规避这种风险,当新的行为只能通过新的字段/方法来实现时,老代码的行为自然不会变。

继承虽然能Open for extension,但很难做到Closed for modification。所以今天解决OCP的主要方法是通过Composition-over-inheritance,即通过组合来做到扩展性,而不是通过继承。

多对象行为类似,导致代码重复

当我们有不同的对象,但又有相同或类似的行为时,OOP会不可避免的导致代码的重复。就像上面例子中,我们每增加一个统一的行为,都需要在所有继承了Member类的实现类中重写这个方法,导致每个类都需要去改动。

问题总结

在这个案例里虽然从直觉来看OOP的逻辑很简单,但如果你的业务比较复杂,未来会有大量的业务规则变更时,简单的OOP代码会在后期变成复杂的一团浆糊,逻辑分散在各地,缺少全局视角,各种规则的叠加会触发bug。有没有感觉似曾相识?对的,电商体系里的优惠、交易等链路经常会碰到类似的坑。而这类问题的核心本质在于:

  • 业务规则的归属到底是对象的“行为”还是独立的”规则对象“?
  • 业务规则之间的关系如何处理?
  • 通用“行为”应该如何复用和维护?

Entity-Component-System(ECS)架构

ECS介绍

ECS架构模式是其实是一个很老的游戏架构设计,最早应该能追溯到《地牢围攻》的组件化设计,但最近因为Unity的加入而开始变得流行(比如《守望先锋》就是用的ECS)。要很快的理解ECS架构的价值,我们需要理解一个游戏代码的核心问题:

  • 性能:游戏必须要实现一个高的渲染率(60FPS),也就是说整个游戏世界需要在1/60s(大概16ms)内完整更新一次(包括物理引擎、游戏状态、渲染、AI等)。而在一个游戏中,通常有大量的(万级、十万级)游戏对象需要更新状态,除了渲染可以依赖GPU之外,其他的逻辑都需要由CPU完成,甚至绝大部分只能由单线程完成,导致绝大部分时间复杂场景下CPU(主要是内存到CPU的带宽)会成为瓶颈。在CPU单核速度几乎不再增加的时代,如何能让CPU处理的效率提升,是提升游戏性能的核心。
  • 代码组织:如同第一章讲的案例一样,当我们用传统OOP的模式进行游戏开发时,很容易就会陷入代码组织上的问题,最终导致代码难以阅读,维护和优化。
  • 可扩展性:这个跟上一条类似,但更多的是游戏的特性导致:需要快速更新,加入新的元素。一个游戏的架构需要能通过低代码、甚至0代码的方式增加游戏元素,从而通过快速更新而留住用户。如果每次变更都需要开发新的代码,测试,然后让用户重新下载客户端,可想而知这种游戏很难在现在的竞争环境下活下来。

而ECS架构能很好的解决上面的几个问题,ECS架构主要分为:

  • Entity:用来代表任何一个游戏对象,但是在ECS里一个Entity最重要的仅仅是他的EntityID,一个Entity里包含多个Component
  • Component:是真正的数据,ECS架构把一个个的实体对象拆分为更加细化的组件,比如位置、素材、状态等,也就是说一个Entity实际上只是一个Bag of Components。
  • System(或者ComponentSystem,组件系统):是真正的行为,一个游戏里可以有很多个不同的组件系统,每个组件系统都只负责一件事,可以依次处理大量的相同组件,而不需要去理解具体的Entity。所以一个ComponentSystem理论上可以有更加高效的组件处理效率,甚至可以实现并行处理,从而提升CPU利用率。

ECS的一些核心性能优化包括将同类型组件放在同一个Array中,然后Entity仅保留到各自组件的pointer,这样能更好的利用CPU的缓存,减少数据的加载成本,以及SIMD的优化等。

ECS架构分析

组件化

在软件系统里,我们通常将复杂的大系统拆分为独立的组件,来降低复杂度。比如网页里通过前端组件化降低重复开发成本,微服务架构通过服务和数据库的拆分降低服务复杂度和系统影响面等。但是ECS架构把这个走到了极致,即每个对象内部都实现了组件化。通过将一个游戏对象的数据和行为拆分为多个组件和组件系统,能实现组件的高度复用性,降低重复开发成本。

行为抽离

这个在游戏系统里有个比较明显的优势。如果按照OOP的方式,一个游戏对象里可能会包括移动代码、战斗代码、渲染代码、AI代码等,如果都放在一个类里会很长,且很难去维护。通过将通用逻辑抽离出来为单独的System类,可以明显提升代码的可读性。另一个好处则是抽离了一些和对象代码无关的依赖,比如上文的delta,这个delta如果是放在Entity的update方法,则需要作为入参注入,而放在System里则可以统一管理。在第一章的有个问题,到底是应该Player.attack(monster) 还是 Monster.receiveDamage(Weapon, Player)。在ECS里这个问题就变的很简单,放在CombatSystem里就可以了。

数据驱动

即一个对象的行为不是写死的而是通过其参数决定,通过参数的动态修改,就可以快速改变一个对象的具体行为。在ECS的游戏架构里,通过给Entity注册相应的Component,以及改变Component的具体参数的组合,就可以改变一个对象的行为和玩法,比如创建一个水壶+爆炸属性就变成了“爆炸水壶”、给一个自行车加上风魔法就变成了飞车等。在有些Rougelike游戏中,可能有超过1万件不同类型、不同功能的物品,如果这些不同功能的物品都去单独写代码,可能永远都写不完,但是通过数据驱动+组件化架构,所有物品的配置最终就是一张表,修改也极其简单。这个也是组合胜于继承原则的一次体现。

ECS架构改造

定义规则

public class Rule {private String name;
}public class DiscountRule extends Rule {private double discount;public double getDiscount() {return discount;}
}

定义entity

public class Member {private Map<String, Handler> components = new HashMap<>(16); //存储实体规则对应的具体处理器private MemberId memberId;private class MemberId {}public Map<String, Handler> getComponents() {return components;}
}

处理器

public interface Handler {double discountHandle(double sourcePrice, Rule rule);
}//折扣处理器
public class DiscountHandler implements Handler {@Overridepublic double discountHandle(double sourcePrice, Rule rule) {if (rule instanceof DiscountRule) {return sourcePrice * ((DiscountRule) rule).getDiscount();}return sourcePrice;}
}

装配规则器

public class Assember {public List<Handler> matchHandler(Rule rule, Map<String, Handler> handlerMap) {List<Handler> handlers = new ArrayList<>();rule.getRuleKeys().forEach(e -> {if (handlerMap.get(e) != null) {handlers.add(handlerMap.get(e));}});return handlers;}
}

打折系统

public class DiscountSystem {List<Handler> handlers;public double discount(double sourcePrice, Rule rule) {double targetPrice = sourcePrice;for (Handler h : handlers) {targetPrice = h.discountHandle(targetPrice, rule);}return targetPrice;}
}

采用这种方式的架构,我们需要增加积分赠送的需求,现在就只需要增加积分规则和增加积分处理的类就行了。无需改动原来的类,将模型可变都集中在一起,最小化模型的可变部分

架构设计之从OOP到ECS架构演进相关推荐

  1. 插件式架构设计实践:插件式系统架构设计简介

    本系列博文将使用微软RIA技术解决方案Silverlight以及扩展性管理框架Managed Extensibility Framework(MEF),以插件式架构设计为导线,分享本人在从事基于微软S ...

  2. 阿里云域名解析网络和服务架构设计(四) 之阿里云ECS服务器Nginx代理实践

    一.回顾 阿里云域名解析网络和服务架构设计总概览(一)_飞鸽FlyGo的博客-CSDN博客云解析DNS.负载均衡SLB.阿里云ECS服务器.阿里云ECS服务器Nginx代理https://flygo. ...

  3. 架构设计实践思路:什么是架构,怎么画架构图?

    点击蓝色字体"肉眼品世界",关注公众号 改变,从一点一滴积累开始 作者丨胡斌 策划丨小智 本文是架构设计实践五部曲系列文章的第一篇,架构与架构图.本文将对架构作深入的阐释,并教你什 ...

  4. 【企业架构设计实战】5 大数据架构设计

    数据架构概述 什么是数据? 一个企业的数字化核心是数据,数据化的价值依赖于数据的标准和质量,数据对一个企业来说至关重要,它也是整个信息化建设及企业架构的核心.数据具有多样性,有结构化的.非结构化的,与 ...

  5. php项目分布式架构设计,【转】互联网项目架构之基于服务的分布式架构

    以前一直找一篇通俗易懂的文章,今天终于找到了,记录下. 文章来源:赵小忠 文章地址:https://www.cnblogs.com/1315925303zxz/p/6371550.html 随着互联网 ...

  6. 系统架构设计笔记(15)—— 网络架构与协议

    网络架构是指计算机网络的各层及其协议的集合.计算机之间要交换数据,就必须遵守一些事先约定好的规则,用于规定信息的格式及如何发送和接收信息的一套规则就称为网络协议.为了减少网络协议设计的复杂性,网络设计 ...

  7. 《mysql性能调优与架构设计》笔记: 一mysql 架构组成

    2019独角兽企业重金招聘Python工程师标准>>> 2.1mysql物理文件组成 2.1.1日志文件: 1,查看mysql配置文件:mysql --verbose --help ...

  8. MYSQL性能调优及架构设计学习笔记-基础篇MYSQL架构组成

    为什么80%的码农都做不了架构师?>>>    目录 小结 MYSQL物理文件组成 MYSQL系统架构 MYSQL自带工具使用介绍 小结 本章主要从逻辑层与物理层两个方面介绍了MYS ...

  9. 高老师的架构设计_隽语集(DD_2101)

    前言:使用"框架的插件管理器" 管理好业务逻辑插件,包括:插件定义.插件创建.插件配对.插件Callback(含同步与异步)等等.然后,让 HTML5幕后的WebView事件能传递 ...

最新文章

  1. java MDC_log4j MDC实现日志追踪
  2. Android http 的使用
  3. 【STM32】FreeRTOS任务挂起和恢复示例
  4. 任女尔(1990-),女,北京卡达克数据技术中心软件业务本部助理工程师,主要研究方向为大数据、云计算。...
  5. 最全面的MySQL面试题大全-二
  6. D3、EChart、HighChart绘图demol
  7. 7000块招不了一个工人
  8. SparkSQL错误:Could not find uri with key [dfs.encryption.key.provider.uri] to create a keyProvider...
  9. 通信光缆故障检测如何进行检测?
  10. 软件测试思想者 - 再识王阳明
  11. mysql数据库压缩_Mysql压缩解决方案
  12. Bootstrap的160个小图标和使用
  13. Tensorflow Serving部署模型
  14. PDF文件如何提取页面,PDF提取页面的小技巧
  15. Transformer详解
  16. Linux加密文件系统eCryptfs介绍
  17. VMware View Client 连接服务器,提示身份验证失败,连接服务器时
  18. InvocationException: GraphViz‘s executables not found【BUG已解决】
  19. 风速传感器的安装要求是什么?安装要注意这三点
  20. H264编码基本原理(一)

热门文章

  1. 数据库中的datatime的长度怎么设定_《龙珠》中超级赛亚人的设定是怎么有的?...
  2. java 枚举 接口_java枚举接口
  3. 在Windows, Mac, Linux三种系统中分别获取wifi密码的方式
  4. html + css + javascript 制作时间轴
  5. s7五杀大数据英雄_S7总决赛大数据分析,UZI成击杀王!
  6. LT来电报号 v3.5.0 官方安卓版
  7. python --while 练习2
  8. 从傅立叶级数到傅立叶变换
  9. Git与华为云(上传代码到华为云)
  10. 电脑的文件删除了怎么恢复?就怕你不会