设计模式之美-设计原则、规范与重构_第1页
设计模式之美-设计原则、规范与重构_第2页
设计模式之美-设计原则、规范与重构_第3页
设计模式之美-设计原则、规范与重构_第4页
设计模式之美-设计原则、规范与重构_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、设计模式之美(一)时时设计原则、规范与重构是极客时间上的一个代码学习系列,在学习之后特在此做记录和总结。、设计原则单一职责原则(,)是指一个类或者模块只负责完成一个职责(或者功能),模块可看作比类更加粗粒度的代码块,模块中包含多个类,多个类组成一个模块。个类包含了两个或者两个以上业务不相干的功能,那就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。判断类的职责是否足够单一,需要根据具体的应用场景和阶段需求,例如。(1如果在社交产品中,用户的地址信息只是单纯地用来展示,那可包含地址信息。(2如果社交产品中添加了电商模块,用户的地址信息还会用在电商物流中,那最好将地址信息从中拆分出

2、来。由此可知,评价一个类的职责是否足够单一,并没有一个非常明确的、可以量化的标准。下面这几条拆分判断原则,要更有指导意义、更具有可执行性:(1类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,行数最好不超过行,函数个数及属性个数都最好不超过个。(2类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想。(3私有方法过多,就要考虑能否将私有方法独立到新的类中,设置为方法,提高代码的复用性。(4比较难给类起一个合适名字,很难用一个业务名词概括,这就说明类的职责定义得可能不够清晰。(5类中大量的方法都是集中操作类中的某几个属性,那就可以考虑将这几个属性和对应的方法拆分

3、出来。2)OCP开闭原则(,)是指添加一个新的功能,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。注意,没必要纠结某个代码改动是修改还是扩展,更没必要太纠结它是否违反开闭原则。只要没有破坏原有代码和单元测试的正常运行,就可以说,这是一次合格的代码改动。不过,有些修改是在所难免的,是可以被接受的。尽量让修改操作更集中、更少、更上层,尽量让最核心、最复杂的那部分逻辑代码满足开闭原则。偏向顶层的指导思想:多花点时间思考,这段代码未来可能有哪些需求变更、如何设计代码结构,事先留好扩展点,以便在未来需求变更的时候,不改动代码整体结构、做到最小代码改动的情况下

4、,新的代码能够很灵活地插入到扩展点上,做到对扩展开放、对修改关闭。实际上,多态、依赖注入、基于接口而非实现编程,以及抽象意识,说的都是同一种设计思路:提升代码扩展性,只是从不同的角度、不同的层面来阐述而已。基于接口而非实现编程的设计初衷是,将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。要遵从该原则,需要做到下面这点。(1函数的命名不能暴露任何实现细节。比如,就不符合要求,改为更加抽象的命名方式:。(2封装具体的实现细节。例如对上传(或下载)流程进行封装,对外提供一个包裹所有上传(或下载)细节的方法,给调用者使用。(3为实现类定义抽象的接口。使用者依赖接口,而不是具体的实现类来编程。如何

5、在项目中灵活运用:(1对于一些比较确定的、短期内可能就会扩展,或者需求改动对代码结构影响比较大的情况,或者实现成本不高的扩展点,在编写代码的时候,就可以事先做些扩展性设计。(2但对于一些不确定未来是否要支持的需求,或者实现起来比较复杂的扩展点,可以等到有需求驱动的时候,再通过重构代码的方式来支持扩展的需求。里式替换原则(P是指子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。里式替换原则就是子类完美继承父类的设计初衷,并做了增强。与多态的区别:多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法和代码实现的思路。里式替换是一种设计原则,用来指

6、导继承关系中子类该如何设计的。按照协议来设计:子类在设计的时候,要遵守父类的行为约定(或者叫协议)。父类定义了函数的行为约定,那子类可以改变函数的内部实现逻辑,但不能改变函数原有的行为约定。这里的行为约定包括:a、函数声明要实现的功能;b、对输入、输出、异常的约定;c、甚至包括注释中所罗列的任何特殊说明。实际上,定义中父类和子类之间的关系,也可以替换成接口和实现类之间的关系。ISP接口隔离原则(InterfaceSegregationPrinciple,ISP)是指接口的调用者或使用者不应该强迫依赖它不需要的接口。接口可理解为下面三种东西:一组API接口集合。例如将删除接口单独放到另外一个接口

7、RestrictedUserService中,而不是UserService中,只打包提供给后台管理系统来使用。单个API接口或函数。函数的设计要功能单一,不要将多个不同的功能逻辑在一个函数中实现。OOP中的接口概念。例如设计一个功能单一的接口:Updater。ScheduledUpdater只依赖Updater这个跟热更新相关的接口,不需要被强迫去依赖不需要的Viewer接口。与单一职责原则的区别:单一职责原则针对的是模块、类、接口的设计。接口隔离原则相对于单一职责原则,一方面它更侧重于接口的设计,另一方面它的思考角度不同。它提供了一种判断接口是否职责单一的标准:通过调用者如何使用接口来间接地

8、判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。DIP依赖倒置原则(DependencyInversionPrinciple,DIP)高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。调用者属于高层,被调用者属于低层。在平时的业务代码开发中,高层模块依赖底层模块是没有任何问题的。这条原则主要还是用来指导框架层面的设计。控制反转(InversionOfControl,IOC)“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整

9、个程序的执行流程可以通过框架来控制。流程的控制权从程序员“反转”到了框架。框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。控制反转并不是一种具体的实现技巧,而是一个比较笼统的设计思想,一般用来指导框架层面的设计。依赖注入(DependencyInjection,DI)不通过new()的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。KISS和丫AGNIKISS原则的英文描述有好几个版本。Kee

10、pItSimpleandStupid.KeepItShortandSimple.KeepItSimpleandStraightforward.它们要表达的意思其实差不多,翻译成中文就是:尽量保持简单。代码足够简单,也就意味着很容易读懂,bug比较难隐藏。即便出现bug,修复起来也比较简单。指导如何开发出KISS原则的方法论:代码行数越少并不是就越“简单”。代码逻辑复杂不违背KISS原则。如何写出满足KISS原则:不要使用同事可能不懂的技术来实现代码。例如例子中的正则表达式,还有一些编程语言中过于高级的语法等。_(2)不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出b

11、ug的概率会更高,维护的成本也比较高。不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。注意,在做开发的时候,一定不要过度设计,不要觉得简单的东西就没有技术含量。实际上,越是能用简单的方法解决复杂的问题,越能体现一个人的能力。YAGNI(YouAintGonnaNeedIt)是指不要去设计当前用不到的功能;不要去编写当前用不到的代码。其核心思想就是:不要做过度设计。例如不要在项目中提前引入不需要依赖的开发包,在未用到ZooKeeper之前没必要提前编写这部分代码。KISS原则讲的是“如何做

12、的问题(尽量保持简单),而YAGNI原则说的是“要不要做的问题(当前不需要的就不要做)。DRYDRY(DontRepeatYourself)的定义非常简单,三种典型的代码重复情况:(1)实现逻辑重复将isValidUserName()和isValidPassword()两个函数中的重复代码合并到isValidUserNameOrPassword()函数,负责两件事情,违反了“单一职责原则”和“接口隔离原则”。虽然从代码实现逻辑上看起来它们是重复的,但是从语义上并不重复。所谓“语义不重复”指的是:从功能上来看,这两个函数干的是完全不重复的两件事情,一个是校验用户名,另一个是校验密码。尽管在目前的

13、设计中,两个校验逻辑是完全一样的,但如果按照第二种写法,将两个函数的合并,那就会存在潜在的问题。功能语义重复在同一个项目代码中有两个函数:isValidIp()和checklflpValid()。尽管两个函数的命名不同,实现逻辑不同,但功能是相同的,都是用来判定IP地址是否合法的。在这个例子中,尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,可以认为它违反了DRY原则。代码执行重复在这个例子中,既没有逻辑重复,也没有语义重复,但仍然违反了DRY原则。这是因为代码中存在“执行重复”。在login()函数中,email的校验逻辑被执行了两次。一次是在调用checkIfUserExiste

14、d()函数的时候,另一次是调用getUserByEmail()函数的时候。除此之外,代码中还有一处比较隐蔽的执行重复,login()函数并不需要调用checkIfUserExisted()函数,只需要调用一次getUserByEmail()函数,从数据库中获取到用户的email、password等信息,然后跟用户输入的email、password信息做对比,依次判断是否登录成功。三个概念:代码复用(CodeResue)表示一种行为:在开发新功能的时候,尽量复用已经存在的代码。代码复用性(CodeReusability)表示一段代码可被复用的特性或能力:在编写代码的时候,让代码尽量可复用。DRY

15、原则是一条原则:不要写重复的代码。区分:首先,“不重复”并不代表“可复用”。在一个项目代码中,可能不存在任何重复的代码,但也并不表示里面有可复用的代码,不重复和可复用完全是两个概念。其次,“复用”和“可复用性咲注角度不同。代码“可复用性”是从代码开发者的角度来讲的,“复用”是从代码使用者的角度来讲的。比如,A同事编写了一个UrlUtils类,代码的“可复用性”很好。B同事在开发新功能的时候,直接复用”A同事编写的UrlUtils类。尽管复用、可复用性、DRY原则这三者从理解上有所区别,但实际上要达到的目的都是类似的,都是为了减少代码量,提高代码的可读性、可维护性。“复用”这个概念不仅可以指导细

16、粒度的模块、类、函数的设计开发,实际上,一些框架、类库、组件等的产生也都是为了达到复用的目的。比如,Spring框架、GoogleGuava类库、UI组件等等。提高代码复用性7个方法:(1)减少代码耦合。对于高度耦合的代码,当希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数的时候,往往会发现牵一发而动全身。满足单一职责原则。越细粒度的代码,代码的通用性会越好,越容易被复用。模块化。可将模块理解为单个类、函数。独立的模块就像一块一块的积木,更加容易复用,可以直接拿来搭建更加复杂的系统。业务与非业务逻辑分离。越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越

17、难复用。通用代码下沉。从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。为了避免交叉调用导致调用关系混乱,只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。继承、多态、抽象、封装。利用继承,可以将公共的代码抽取到父类,子类复用父类的属性和方法。利用多态,可以动态地替换一段代码的部分逻辑,让这段代码可复用。越抽象、越不依赖具体的实现,越容易复用。代码封装成模块,隐藏可变的细节、暴露不变的接口,就越容易复用。应用模板等设计模式。模板模式利用了多态来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。实际上,除非有非常明确的复用需求,否

18、则,为了暂时用不到的复用需求,花费太多的时间、精力,投入太多的开发成本,并不是一个值得推荐的做法。这也违反之前讲到的YAGNI原则。除此之外,有一个著名的原则,叫作RuleofThree”。第一次编写代码的时候,不考虑复用性;第二次遇到复用场景的时候,再进行重构使其复用。LOD迪米特法则(LawofDemeter,LOD)也叫最小知识原则(TheLeastKnowledgePrinciple),是指每个模块只应该了解那些与它关系密切的模块的有限知识。或者说,每个模块只和自己的朋友说话”,不和陌生人“说话”。换句话说,就是不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要

19、的接口(也就是定义中的“有限知识”)。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。单一职责原则、接口隔离原则、基于接口而非实现编程和迪米特法则,目的都是实现高内聚低耦合,但是出发的角度不一样,单一职责是从自身提供的功能出发,迪米特法则是从关系出发,针对接口而非实现编程是使用者的角度,殊途同归。高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。放到同一个类中,修改会比较集中,代码容易维护。松耦合,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少

20、导致依赖类的代码改动。积分系统合理地将功能划分到不同模块。为了避免业务知识的耦合,让下层系统更加通用,一般来讲,不希望下层系统(也就是被调用的系统)包含太多上层系统(也就是调用系统)的业务信息,但是,可以接受上层系统包含下层系统的业务信息。比如,订单系统、优惠券系统、换购商城等作为调用积分系统的上层系统,可以包含一些积分相关的业务信息。但是,反过来,积分系统中最好不要包含太多跟订单、优惠券、换购等相关的信息。设计模块与模块之间的交互关系。交互方式有两种,一种是同步接口调用,另一种是利用消息中间件异步调用。第一种方式简单直接,第二种方式的解耦效果更好。比如,用户下订单成功之后,订单系统推送一条消

21、息到消息中间件,营销系统订阅订单成功消息,触发执行相应的积分兑换逻辑。这样订单系统就跟营销系统完全解耦,订单系统不需要知道任何跟积分相关的逻辑,而营销系统也不需要直接跟订单系统交互。设计模块的接口、数据库、业务模型。数据库和接口的设计非常重要,一旦设计好并投入使用之后,这两部分都不能轻易改动。改动数据库表结构,需要涉及数据的迁移和适配;改动接口,需要推动接口的使用者作相应的代码修改。a、数据库的设计比较简单。实际上,只需要一张记录积分流水明细的表就可以了。b、为了兼顾易用性和性能,可以借鉴facade(外观)设计模式,在职责单一的细粒度接口之上,再封装一层粗粒度的接口给外部使用。c、将它跟营销

22、系统放到一个项目中开发部署,只要做好代码的模块化和解耦即可。为什么要分MVC三层开发?(1)分层能起到代码复用的作用。同一个Repository可能会被多个Service来调用,同一个Service可能会被多个Controller调用。分层能起到隔离变化的作用。基于接口而非实现编程的设计思想,Service层使用Repository层提供的接口,并不关心其底层依赖的是哪种具体的数据库。当需要替换数据库的时候,只需要改动Repository层的代码。分层能起到隔离关注点的作用。Repository层只关注数据的读写。Service层只关注业务逻辑,不关注数据的来源。Controller层只关注与

23、外界打交道,数据校验、封装、格式转换,并不关心业务逻辑。分层能提高代码的可测试性。Repsitory层的代码通过依赖注入的方式供Service层使用,当要测试包含核心业务逻辑的Service层代码的时候,可以用mock的数据源替代真实的数据库,注入到Service层代码中。分层能应对系统的复杂性。拆分有垂直和水平两个方向。水平方向基于业务来做拆分,就是模块化;垂直方向基于流程来做拆分,就是这里说的分层。统计系统对于这样一个通用的框架的开发,还需要考虑很多非功能性的需求。易用性,框架是否易集成、易插拔、跟业务代码是否松耦合、提供的接口是否够灵活等等。性能,一方面,希望它低延迟,即统计代码不影响或

24、很少影响接口本身的响应时间;另一方面,希望框架本身对内存的消耗不能太人。扩展性,从框架使用者的角度来说,可以在不修改框架源码的情况下,为框架扩展新的功能,类似给框架开发插件。容错性,不能因为框架本身的异常导致接口请求出错。通用性,除了接口统计这样一个需求,还可以适用到其他哪些场景中,比如SQL请求时间的统计信息、业务统计信息(比如支付成功率)等。框架设计:对于稍微复杂系统的开发,很多人觉得不知从何开始。可以借鉴TDD(测试驱动开发)和Prototype(最小原型)的思想,先聚焦于一个简单的应用场景,基于此设计实现一个简单的原型。把整个框架分为四个模块:数据采集、存储、聚合统计、显示。接下来,就

25、按照之前讲的面向对象设计的几个步骤,来重新划分、设计类。划分职责进而识别出有哪些类。MetricsCollector类、Metricsstorage接口、Aggregator类、ConsoleReporter类和EmailReporter类。定义类及类与类之间的关系。先在IDE中创建好这几个类,然后开始试着定义它们的属性和方法。在设计类、类与类之间交互的时候,不断地用之前学过的设计原则和思想来审视设计是否合理。比如,是否满足单一职责原则、开闭原则、依赖注入、KISS原则、DRY原则、迪米特法则,是否符合基于接口而非实现编程思想,代码是否高内聚、低耦合,是否可以抽象出可复用代码等等。将类组装起来

26、并提供执行入口。有两个执行入口:一个是MetricsCollector类,提供了一组API来采集原始数据;另一个是ConsoleReporter类和EmailReporter类,用来触发统计显示。二、规范与重构重构重构目的(why)重构是一种对软件内部结构的改善,目的是在不改变软件可见行为的情况下,使其更易理解,修改成本更低。即在保持功能不变的前提下,利用设计思想、原则、模式、编程规范等理论来优化代码,修改设计上的不足,提高代码质量。重构对象(what)可以笼统地分为大规模高层次重构(以下简称为大型重构”)和小规模低层次的重构(以下简称为小型重构”)。大型重构指的是对顶层代码设计的重构,包括:

27、系统、模块、代码结构、类与类之间的关系等的重构,重构的手段有:分层、模块化、解耦、抽象可复用组件等等。这类重构的工具就是学习过的那些设计思想、原则和模式。小型重构指的是对代码细节的重构,主要是针对类、函数、变量等代码级别的重构,比如规范命名、规范注释、消除超大类或函数、提取重复代码等等。小型重构更多的是利用后面要讲到的编码规范。重构时机(when)提倡的重构策略是持续重构。平时没有事情的时候,可以看看项目中有哪些写得不够好的、可以优化的代码,主动去重构一下。或者在修改、添加某个功能代码的时候,也可以顺手把不符合编码规范、不好的设计重构一下。重构方法(how)在进行大型重构的时候,要提前做好完善

28、的重构计划,有条不紊地分阶段来进行。每个阶段完成一小部分代码的重构,然后提交、测试、运行,发现没有问题之后,再继续进行下一阶段的重构,保证代码仓库中的代码一直处于可运行、逻辑正确的状态。每个阶段,都要控制好重构影响到的代码范围,考虑好如何兼容老的代码逻辑,必要的时候还需要写一些兼容过渡代码。除了人工去发现低层次的质量问题,还可以借助很多成熟的静态代码分析工具(比如CheckStyle、FindBugs、PMD),来自动发现代码中的问题,然后针对性地进行重构优化。单元测试单元测试(UnitTesting)由研发工程师自己来编写,用来测试自己写的代码的正确性。单元测试的好处:单元测试能有效地帮你发

29、现代码中的bug。写单元测试能帮你发现代码设计上的问题。比如没有使用依赖注入、大量使用静态函数、全局变量、代码高度耦合等。单元测试是对集成测试的有力补充。大部分异常情况都比较难在测试环境中模拟,比如除数未判空、网络超时。写单元测试的过程本身就是代码重构的过程。相当于对代码的一次自我CodeReview。阅读单元测试能帮助你快速熟悉代码。单元测试是测试驱动开发(Test-DrivenDevelopment,TDD)可落地执行的改进方案。编写单元测试的经验总结包括以下几点:尽管单元测试的代码量可能是被测代码本身的12倍,写的过程很繁琐,但并不是很耗时。对单元测试代码的质量可以放低一些要求。命名稍微

30、有些不规范,代码稍微有些重复,也都是没有问题的。不管覆盖率的计算方式如何高级,将覆盖率作为衡量单元测试质量的唯一标准是不合理的。单元测试不要依赖被测试函数的具体实现逻辑,它只关心被测函数实现了什么功能。团队内部需要统一单元测试框架。单元测试为何难落地执行?方面,写单元测试本身比较繁琐,技术挑战不大,很多程序员不愿意去写;另一方面,国内研发比较偏向快、糙、猛”,容易因为开发进度紧,导致单元测试的执行虎头蛇尾。最后,关键问题还是团队没有建立对单元测试正确的认识,觉得可有可无,单靠督促很难执行得很好。可测试性所谓代码的可测试性,就是针对代码编写单元测试的难易程度。对于一段代码,如果很难为其编写单元测

31、试,或者单元测试写起来很费劲,需要依靠单元测试框架中很高级的特性,那往往就意味着代码设计得不够合理,代码的可测试性不好。依赖注入是编写可测试性代码的最有效手段。通过依赖注入,在编写单元测试的时候,可以通过mock的方法依赖外部服务。注意,只往里写入数据,并不读取数据,不参与业务逻辑的执行,不会影响代码逻辑的正确性,这些对象没有必要mock。除此之外,些只是为了存储数据的值对象,比如String、Map、UseVo,也没必要通过依赖注入的方式来创建,直接在类中通过new创建就可以了。常见的测试不友好的代码有下面这5种:代码中包含未决行为逻辑,即代码的输出是随机或者说不确定的,比如,跟时间、随机数

32、有关的代码。滥用可变全局变量。滥用静态方法,因为静态方法很难mock。使用复杂的继承关系,如果父类需要mock某个依赖对象才能进行单元测试,那么底层子类要一个一个mock很多依赖对象。高度耦合的代码,在编写单元测试的时候,可能需要mock这十几个依赖的对象。解耦代码是否需要解耦?间接的衡量标准有很多,比如,看修改代码会不会牵一发而动全身。还有一个直接的衡量标准,那就是把模块与模块之间、类与类之间的依赖关系画出来,根据依赖关系图的复杂性来判断是否需要解耦重构。如何进行解耦:封装与抽象,有效地隐藏实现的复杂性,隔离实现的易变性,给依赖的模块提供稳定且易用的抽象接口。中间层,简化模块或类之间的依赖关

33、系,并能起到过渡的作用,让开发和重构同步进行,不互相干扰。a、第一阶段:引入一个中间层,包裹老的接口,提供新的接口定义。b、第二阶段:新开发的代码依赖中间层提供的新接口。c、第三阶段:将依赖老接口的代码改为调用新接口。d、第四阶段:确保所有的代码都调用新接口之后,删除掉老的接口。模块化,将系统划分成各个独立的模块,让不同的人负责不同的模块,这样即便在不了解全部细节的情况下,管理者也能协调各个模块,让整个系统有效运转。其他设计思想和原则,单一职责原则,基于接口而非实现编程,依赖注入,多用组合少用继承,迪米特法则。编码规范命名以能准确达意为目标,利用上下文简化命名,命名要可读、可搜索。对于接口的命

34、名,一种是加前缀T,另一种是加后缀“Impl,;对于抽象类的命名,一种是带上前缀Abstract,另一种是不带前缀。注释注释的内容主要包含这样四个方面:做什么、为什么、怎么做、怎么用。对一些边界条件、特殊情况进行说明,以及对函数输入、输出、异常进行说明。写一些总结性的说明、特殊情况的说明。对于逻辑比较复杂的代码或者比较长的函数,借助总结性的注释来让代码结构更清晰、更有条理。类和函数一定要写注释,而且要写得尽可能全面、详细,而函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码的可读性。类、函数规模当一个类的代码读起来让你感觉头大了,实现某个功能时不知道该用

35、哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,这就说明类的行数过多了。对于函数代码行数的最大限制,网上有一种说法,那就是不要超过一个显示屏的垂直高度。一行代码的长度行代码最长不能超过IDE显示的宽度。需要滚动鼠标才能查看一行的全部代码,显然不利于代码的阅读。善用空行分割单元块在类的成员变量与函数之间、静态成员变量与普通成员变量之间、各函数之间、甚至各成员变量之间,都可以通过添加空行的方式,让这些不同模块的代码之间,界限更加明确。缩进Java语言倾向于两格缩进,PHP语言倾向于四格缩进。大括号是否要另起一行PHP程序员喜欢另起一

36、行,Java程序员喜欢跟上一条语句放到一起。类中成员的排列顺序在Java类文件中,先要书写类所属的包名,然后再罗列import引入的依赖类。在Google编码规范中,依赖类按照字母顺序从小到大排列。在类中,成员变量排在函数的前面。成员变量之间或函数之间,都是按照先静态(静态函数或静态成员变量)、后普通(非静态函数或非静态成员变量)”的方式来排列的。成员变量之间或函数之间,还会按照作用域范围从大到小的顺序来排列,先写public成员变量或函数,然后是protected的,最后是private的。把代码分割成更小的单元块要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节,让阅读代码的人不至于迷失在细节中。避免函数参数过多函数包含3、4个参数的时候还是能接受的。针对参数过多的情况,一般有2种处理方法。a、考虑函数是否职责单一,是否能通过拆分成多个函数的方式来减少参数。b、将函数的参数封装成对象。勿用函数参数来控制逻辑不要在函数中使用布尔类型的标识参数来控制内部逻辑,true的时候走这块逻辑,false的时候走另一块逻辑。这明显违背了单一职责原则和接口隔离原则,建议将其拆成两个函数。函数设计要职责单一相对于类和模块,函数的粒度比较小,代码行数少,所以在应用单一职责原则的时候,没有像应用到类

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论