花点心思写一段高质量的代码,比写100段凑活能用的代码,对你的代码能力提高更有帮助。
什么是优美的代码?
可维护性:在不破坏原有代码设计的情况下,能够快速的修改添加代码。不能在修改代码时要冒着极大的引入新bug的风险。代码的可维护性是由很多因素协同作用的结果。代码分层清晰、模块化好、高内聚低耦合、基于接口编程等就能使得代码的可维护性好。 可读性:代码是否符合编码规范、命名注释是否达意、模块划分是否清晰、是否符合高内聚低耦合。 可扩展性:指的是代码应对未来需求变化的能力。我们可以在原有代码预留扩展点,抽象出很多底层可以服用的模块,接口可以满足不同的需求。 简洁性:思从深而行从简。 可复用性:尽量减少重复代码的编写。
【测试驱动开发】:先设计测试用例,再考虑如何实现功能。 【最小原型】:先聚焦简单的场景
接到需求后,先进行模块划分。然后在每个模块中,把需求拆分成小的功能点,一条条地罗列下来,得到功能点列表。然后再划分职责,将操作同样属性的功能点划分为一个类。再根据需求中的动名词定义类的属性和方法。最后定义类之间的交互关系,将类组装起来并提供执行入口。 每写一个小模块,或是引入依赖,都可以去测试一次。
- 如何写出优美的代码
面向对象
四大特性:封装、抽象、继承、多态。
设计原则
设计原则的最终目的就是提高代码的可读性、易扩展性、复用性、可维护性。
单一职责原则: 一个类/模块只负责一个功能,单一职责需要根据具体场景来评判。在日常开发中,我们可以先设计一个粗粒度的类,等类的粒度越来越粗,再拆分成几个细粒度的类,即持续重构。 那么在重构时,如果发现此类依赖的其他类过多过杂,则需要拆分;类中的大多数方法都是来操控某几个属性,则需要拆分;类的名字不好起、属性函数太多不便于维护,则需要拆分。 开闭原则: 什么叫开闭?如何做到开闭?如何在实现开闭、追求代码扩展性的同时不影响代码的可读性 开闭:添加一个新功能,应该是在已有代码的基础上扩展代码,而非修改已有代码。 实例:对于多参方法、且每个参数对应一个if方法,我们可以把多参封装成一个类,把if方法封装成一个抽象类的抽象方法。后期如果需要增加参数,只需要新增多参类中的属性与setter,然后增加一个抽象类的实现类即可. 我们在增加新功能的过程中,难免会修改类。我们要尽量保证不修改核心类,不破坏核心类的原有代码和单元测试,而且对于要修改的类,要做到重在扩展。
代码重构
基于接口开发:多去感知变化点,不要在接口方法名上暴露细节。对于具体的实现细节或特殊的实现要封装成一个方法,且特定的实现方法不要定义在接口中。可仅仅使用接口,那么在代码中也要不断修改new出的不同子类。我们可以使用工厂模式+反射+配置文件实现,然后修改配置文件中的类名即可。但继承层次太深的话,还是要用接口组合代替继承。
用基于充血模型的DDD代替基于贫血模型的MVC。DDD相比于传统MVC,它依旧保持了MVC三层架构,但是在Service层它采用domain和service,domain中既有数据也有业务逻辑。 贫血模型:像Service和BO都属于Service层,但它们把Service的数据和业务完全分离开了,破坏了封装特性,属于面向过程的编程风格(Entity、VO同理) 充血模型:数据和其对应的业务逻辑封装在同一个类中,是面向对象的编程风格。 为什么DDD呢?在我们平时的开发中,大多数开发流程是面向SQL编程,即接到接口需求后先去数据库中看表,然后编写SQL,之后就是定义Entity、BO、VO,去Repository Service Controller中套。但这样有一个问题,我们把我们的业务聚焦于SQL,Service层做的事情很少。而SQL都是针对特定业务功能编写的,复用性差。但如果我们基于DDD,那开发流程就完全8一样了。我们在DDD中,需要事先理清楚所有的业务,定义业务中间层(领域模型)所包含的属性和方法,那么后续的新需求开发,都可以基于之前定义好的业务中间层进行复用。 所以对于Entity的充血模型,就是将“对于Entity类属性 且 无关于业务的操作”封装在Entity内部,使其充血,让Service依赖于Entity。 对于业务通用接口,我们在入参和出参上进行可扩展的设计,比如使用继承、组合的方式,在保证入参模型基本不变的情况下可以支撑不同的业务,这样一来,新业务接入的时候,就不需要更改接口,只需要扩展入参对象就行。 分层!ddd(不完全ddd,eshopcontainer 阿里ddd系列) 依赖注入 事件驱动 cqrs 充血模型 防腐层 bbf 策略模式 组装
代码规范
要学会换位思考,假设自己不熟悉这块代码,从代码的阅读者角度去考量命名是否直观。对于作用域小的变量我们可以使用缩写,作用域大的变量还是使用全程。而且给接口和实现类命名时要考虑它未来的使用场景,也不能暴露太多细节。所以可以通过接口多重继承来组合,实现最大细化。 在类或函数的上下文中,要借助上下文语义命名(User类中name属性而非userName属性) 类和函数一定要写注释 一定不要用tab键缩进,因为不同的ide中tab缩进方式可能不同 尽量不要以函数形参作为判断逻辑(buy(a,isVip)应该改成buy(a)和buyAsVip(a)),尽量不要以函数形参作为isNull判断的逻辑,要把业务逻辑拆出一个个小函数。 变量不应该重复使用,对于spilt字符串应该拆出专门的方法. 代码中的魔法数应该声明在函数外面 对于共用的下层模块(积分模块),我们不要让他包含任何上游模块,避免业务耦合。 上下层模块直接采用同步调用,同层模块之间采用异步调用。
日志
往往不同请求的日志都会在日志系统里错杂交汇,所以我们在打印日志时可以为这一次请求附带上他的唯一标识,那么这一次请求的唯一标识就存在Servlet线程的ThreadLocal中就好。
业务常见错误(spring错误在idea中)
下订单锁商品库存产生死锁:下单时对商品进行排序,再CAS加锁。 对于很重的任务,使用独立的线程池来做 HTTP框架默认超时是否合理、重试是否保证幂等
设计模式
1.动作放在接口中,动作具体实现放在各个业务类中,业务类的创建和选择交给工厂。
电商英文单词
奖品:commodity 优惠券:CouponCommodity 实物商品:GoodsCommodity 第三方兑换卡:CardCommodity
代码设计
长链路串行执行: 一个指令执行失败会导致整条链路请求失败,稳定性差。我们可以对非关键指令异步执行或降级执行。 对于更新不频繁的数据进行缓存,采用LRU动态更新。指令之间相互独立,可能会导致额外查询数据库,可以采用TransmittableThreadLocal进行数据传递,减少数据库访问。
接口设计: 1.服务无状态:避免服务维护跨服务的上下文。 2.重入幂等:保证服务的操作幂等。 3.超时保护:提供超时时间控制。 4.快速失败:发生错误立即返回,避免服务调用超时。 5.乱序可容忍:保证有序或对顺序不敏感。
最终当你在接一个产品需求时,开始思考程序数据结构的设计、核心功能的算法逻辑实现、整体服务的设计模式使用、系统架构的搭建方式、应用集群的部署结构,那么也就是的编程能力真正提升的时候 就像你现在做的开发中,你的代码有哪些是经常变化的,有哪些是固定通用的,有哪些是负责逻辑拼装的、有哪些是来做核心实现的。那么现在如果你的核心共用层做了频繁变化的业务层包装,那么肯定的说,你的代码即将越来越乱,甚至可能埋下事故的风险! 你的代码用上了定义接口吗、接口继承接口吗、接口由抽象类实现吗、类继承的类实现了接口方法吗,而这些操作都是为了让你的程序逻辑做到分层、分区、分块,把核心逻辑层和业务封装层做好隔离,当有业务变化时候,只需要做在业务层完成装配,而底层的核心逻辑服务并不需要频繁变化,它们所增加的接口也更原子化,不具备业务语意 在你写的程序开发中,你有为一个类名、方法名、属性名,反复斟酌吗?代码格式间隔大小、编写方式、注释描述不断的提升吗?你有为一个功能逻辑的实现不断的重构吗?我有,我一直都有,为了能写好一块代码,甚至会忘记时间从上午到下午,当能实现完成后,会欣赏似的看待自己的代码,也根本不舍得把他交给别人!
对线上系统的建议和批评 技术影响力体现于实实在在写的代码 熟练使用项目的警告日志,能解决问题,定位线上问题 项目提20个疑惑问题,提3个改进问题 不要全是别人的目标
写文档:每一部分的代码为什么要这么写,用什么样的方案,中间件选型的思路。 方案优劣势、优化思路给的明确,业务和技术上的思考给的比较多
做的事情及时向Leader汇报进度,有什么问题及时同步给他,主动讨论项目后期的安排、规划、风险点 总的来说,技术站在他的角度去思考,让他对你完全放心,觉得你靠谱不需要操心。
活要会干,要干的漂亮。还能让ld知道你干的漂亮 让大家都认识你,知道你这人比较nice,还知道惹你你是有手段收拾回来的 你得知道ld在想什么,在为难什么,什么事情搞定了他会很happy
所有上级都喜欢正能量的人,团队开会积极响应,群里下发指令秒回 关注自己晋升的指标和要求,时刻按时你的ld有机会晋升我愿意上
写文档、记录一定不要是给的资料的拆分整合,一定要有自己的思考。