1.代码质量差的根本原因1.代码质量差的表现我们可以列举很多代码质量差的性能现象,比如名字确实不是说,超大的类,超大的方法,重复的代码,难理解的代码,难修改的代码……最影响代码质量的两个表现是命名不准确,逻辑扩展性差。逻辑不对,让人觉得很迷茫。这种现象在正常工作中并不少见;二是逻辑可扩展性差。一个新的业务需求提出后,发现需要改动的地方很多,需要返回的业务逻辑也更多,导致研发效率低下。2.问题总结对第1节中提到的现象进行总结整理,大致整理出6类问题,并分别进行说明。命名问题:命名问题是一个很头疼的问题。要选择一个名副其实、通俗易懂的名字并不是那么容易。说到变量命名、方法命名和类命名,常见的命名问题有两种:一是看不懂;二是难以理解。另一个是错误的名字。这个名字是不可理解的,因为一个人乍一看不知道它是什么意思。根本原因是他没有想到合适的词汇来抽象问题;这个名字是不准确的,因为名字的意思和实际的逻辑是不一样的。这样的名称会产生误导。人们。代码结构问题:一个人初看项目代码,还没有深入看代码逻辑的时候,从模块划分、类划分、方法划分等整体来感受代码质量。如果一个类有几千行代码,一个方法就有几百行。这样的逻辑相信没有多少人愿意看,而且复杂度比较高。好的代码层次非常清晰,就像读一本漂亮的书一样赏心悦目。编程范式问题:编程范式有表模式、事务脚本模式、领域设计模式三种。事务脚本模式是大家用的最多的。这种模式最符合人们做事的方式。步步为营,这种模式最大的问题就是承担了不该自己承担的责任。看起来比较合乎逻辑,其实问题很多。通常,人们喜欢称它为“面码”。可读性问题:代码除了要实现业务功能外,还必须具备良好的可读性。有些代码没有任何注释;部分代码格式不统一;不是说Lambda表达式不好,关键是要控制层级的深度),这样的代码看起来简洁,但可读性不是很好。可扩展性问题:可扩展性问题是一个老掉牙的问题。实现良好的可扩展性并不是那么容易。一般不存在抽象问题。比如店铺在店铺中显示Tab,面条类的代码是直接定义一个List,然后在里面添加一个Tab对象,如果需要再添加一个Tab怎么办?通常,不满足开闭原则。无设计问题:整个代码看起来平淡无奇,别人看了也学不到东西。一般这种问题是没有深入分析问题,只解决问题,没有考虑如何更好地解决问题,比如重复处理过程的工作是否可以抽象成一个通用的模板类,是否可以通过工厂类来处理不同的处理类。获取具体的策略,是否可以使用事件方式处理异步处理,是否可以通过自动注册发现新增的能力……3.根本原因分析接下来分析为什么会出现代码错误。这个问题有外因也有内因。外部原因主要有:项目进度紧迫,设计时间不多;资源短缺,人手不足,只能尽快做;紧急问题修复,快速处理临时解决方案...内部原因主要有:自身技能低,没有掌握Lamda表达式、常用工具、框架高级用法等技能;没有追求极致的精神,满足需求就好,稳定性、扩展性、性能、数据一致性等等都没有考虑……笔者认为最关键的是内部问题,根本原因有两个:低自我需求;没有反馈渠道。如果对自己的要求不高,只需要满足开发完成,就很难写出高质量的代码。另外,如果没有外界的反馈,你的技能也很难提升。作者之前的主管很严格,对大家写的代码审查的比较仔细。一个变量名,一个有逻辑的写法,反复修改,其实是提升技能最快的方法。2.提高代码质量的方法作者喜欢用三种方法来提高代码质量:领域建模、设计原则和设计模式。我主要讲一下如何使用它们。分析阶段:当你拿到一个需求的时候,不要纠结这个功能怎么实现,很容易陷入事务脚本的模式。分析什么?需要分析需求的目的是什么,需要哪些实体来完成功能。这一步的核心是找到实体。就拿上面进入商店Tab的例子来说,它有两个关键实体:导航栏和Tab,导航栏又包含了几个Tab。设计阶段:分析实体后,分析如何将职责分配给具体实体。这需要使用一些设计原则来指导。GRASP提到了一些责任分配的原则。有兴趣的同学可以去详细看看。回到上面的例子,一个Tab的职责主要有两个:一个是Tab能不能展示,这是它自己的职责,比如上面newTab展示的逻辑是店铺有新的商品上30天内上架;另一个责任是Tab规范信息的构建也是它自己的责任。导航栏有两个职责:一是接受Tab注册;另一个是显示。职责分配不合理,不满足高内聚低耦合的特点。打磨阶段:在这个阶段,选择合适的模式来实施。大家看到图案就明白它的作用了。例如,当他们看到模板类时,他们就会知道如何处理一般的业务流程。具体的变化在子类中处理。上面的例子中使用了两种设计模式:一种是订阅者模式,Tab自动注册的过程;另一种是模板方式,先判断Tab是否可以显示,然后构建Tab规范信息。流程虽然简单,但也可以将一般的流程抽象出来,子类只需要简单的重写2个方法即可。3.领域模型的作用领域建模的入门门槛比较高,包含了一些比较难的概念。本文将不介绍如何建模(可以私信交流)。笔者发现,让大家接受领域建模远比知道如何建模重要。学习的方式。下面通过笔者亲身经历的一些实际案例进行讲解,让大家听起来不那么空洞。1.简单理解笔者在工作一年后加入了一家财务公司。那时,他对金融一无所知。当他接触到标的物、债权、债权转让、融资性担保、非融资性担保等术语时,他一度感到手足无措,每天都要学习很多新内容。两个月后,我的主管给我们做了一个分享,我拿了个ppt讲了,里面有领域里面的实体,实体之间的关系,一下子就知道整个业务是怎么回事了。乐趣。模型的作用是简化人们对事物的理解。如果一开始就卡在代码的细节中,很难看清业务的全貌,而代码就是实现业务能力的。了解业务后,再看代码。快多了。2、统一认识在公司里,有研发、有产品、有运营、有测试……大家在一起交流的时候,大家默认的语言是不统一的。开发经常讲怎么操作这个数据库表,产品经常讲业务。模式...这就导致大家的理解不一致。那是一个晚上,在和互动同学确认了互动过程后,她突然问了一个问题:让卖家把相似的页面移动到同一个文件夹,这容易实现吗?我听了之后告诉他们,这不可能。互动的同学一听有道理,为什么不能实现呢?我开始给她讲现有的系统流程,发现她一头雾水,马上就发现了问题。本来是用开发的语言描述问题的,立马换了个方式。我找来一支笔和一张纸,给互动的同学们画了我们的领域模型是什么,业务实体之间的交互是什么样的。讲完后,互动的同学们顿时明白为什么不能实现了。3.引导设计有同学觉得领域建模比较空洞,比较空洞。事实上,除了简化和统一理解之外,领域建模还可能指导代码设计。Modeling是设计出来的,虽然是一个小需求,但并不妨碍领域建模的使用。下图中可以清楚的看到导航栏包含了几个tab,其中一个tab包含了规格信息和点击操作信息。画完这个业务模型,相应的代码也会有上面的概念。现实和代码之间存在映射关系,模型就是代码,代码就是模型。如果你的模型不能反映现实,那么这个模块只能算是作秀。樊纲先生总结了三句话:现实是什么,对应的对象是什么;真实事物的行为是什么,对应对象的方法是什么;真实的事物有什么联系,与对应的对象是什么关系。四、设计原则底层逻辑1、SOLID对于设计原则,我们一般讲SOLID,它包含五个设计原则:单一职责原则:一个类应该有一个,而且只有一个,改变的理由,一个类只能被修改因为某种原因。开闭原则:实体应该对扩展开放,对修改关闭,对扩展开放,对修改关闭。里氏替换原则:使用基类引用指针的函数必须能够在不知情的情况下使用派生类的对象,子类可以替代父类。接口隔离原则:不应该强制客户端实现它不使用的接口。不应强迫客户端实现它不使用的接口。接口要拆的越小越好。依赖倒置原则:抽象不应该依赖于细节。细节应该依赖于抽象,抽象不依赖于细节,细节依赖于抽象。2、为什么要有设计原则?SOLID原则我们基本都听过或者了解过,但是为什么会有这些设计原则呢?为了回答这个问题,我们从目标开始。软件开发的目标是高内聚和低耦合。这句话很难衡量,比如回答:什么是高内聚?什么是低耦合?高内聚有多高?低耦合到底有多低?这四个问题并不容易回答。反过来想想,如果我们的代码不是高内聚低耦合的呢?即低内聚高耦合场景。如果代码低内聚高耦合,修改一个逻辑会导致多次代码修改,这不是我们希望看到的,尤其是在修改原有逻辑的时候,很容易出现bug,比如笔者之前修改一个问题,换个地方改规则貌似没问题,但是影响到业务端。这就是为什么开闭原则提出关闭修改。修改原有逻辑是有风险的。理想的情况是修改只限于某个局部范围,这样影响的范围就有限了,所以我们要求逻辑是单一的,不包含多个职责。进一步思考:为什么我们需要修改它?除了原有逻辑上的bug修复和代码重构外,一个重要的原因是需求发生了变化,正是这种变化导致我们修改了原有的逻辑。如果没有修改场景,就没有所谓的高内聚低耦合。因此,设计原则的底层逻辑是让软件更好地响应变化,降低成本,提高效率。3.如何实践设计原则只是一个指导方针,要付诸实践还有很长的路要走。就像有的同学说,设计原理我都懂了,但是我还是不会去应用。其实这个问题的本质是没有理解设计原则的底层逻辑,没有洞察变化的重点。如何解决这个问题呢?设计模式给出的答案:发现变化,封装变化。五、设计模式的本质六、案例实践当调用的接口有不同的实现时(入参、出参、接口不同),需要抽象出一层防腐层。如何实现?下面我们分别来看两个案例。这两种情况的侧重点不同,一种是偏于行为的抽象,一种是偏于结构的抽象。1.店铺品牌查询店铺需要查询店铺品牌信息,Lazada和AE的接口是不一样的。防腐层如何抽象?首先,最简单的方案很容易想到,就是定义一个接口,然后有两个实现。它的优点是层次简单,大家看完就明白了。它的缺点也很明显。两个实现类中,职责并不单一,承担了两个职责:一是实现店铺品牌的查询,二是数据的转换。根据方案一提到的不足,很容易想到使用适配器模式将前面的类拆分为两个类:一个类是调用对应的品牌服务;另一类用于数据适配和转换。但是,此时这种方法还有一个缺点。在国际化场景下,必须考虑多租户之间的隔离。例如,Lazada有多个站点。如何实现更细粒度的差异?选项3基于这些考虑。第三种方案是引入多租户框架,可以支持多租户场景。2、店铺优惠券查询“一刀切”的开发模式:组装参数、调用接口、解析响应结果。你会发现这种模式太通用了,适用于所有场景。这种开发模式也被称为“交易脚本模式”或“意大利面条代码”。在优惠券查询的情况下,使用领域建模模型,首先思考有哪些实体。优惠券查询的本质:通过xx条件查询返回满足条件的优惠券集合,对于优惠券来说,有两类信息很关键,一是优惠券的规格信息,如优惠券名称、优惠金额、有效期等;二是限制条件查询时,是查询店铺优惠券,还是查询粉丝优惠券,还是查询商品优惠券……所以分离出两个抽象的优惠券:一个是优惠券查询请求;一个是优惠券规范实体。这种设计,有个缺点就是业务方会增加理解的复杂度,是底层实现,不好用。因此,在底层实现之上,对产品组件进行了抽象,使得业务端使用起来相对简单。
