当前位置: 首页 > 后端技术 > Java

CleanCodeNotes

时间:2023-04-01 13:59:53 Java

@TOC在线阅读:Bookstack:https://www.bookstack.cn/read...每章都会做自己的总结,并为本章做一个重要的注释性参考分数,满分五明星(仅从个人角度)。如果想尽快了解一些代码规范,最好看看阿里代码规范,idea也可以安装阿里代码规范插件。阿里开发手册是实践,书籍本身更多的是作者理念的指导。概念的引导,证据和一些看似多余的句子是少不了的,但这就是我们站在今天的开发环境下的结果论,因为所有的技术文章都具有很强的时效性。Chapter1CleanCode(3stars)第一章主要介绍了本书的背景和意图,总结了cleancode的概念。有的章节可以直接跳过,但是看完你就知道为什么了,当然知道是什么了?WhyNeatCode负面证据:糟糕代码的坏处每个团队各司其职,代码是coder的责任。我们要积极维护它,说服那些阻碍我们优化的人。..?什么是干净的代码?每个人对清洁的定义都不一样。以下是作者所代表的一些想法。学会独立思考。明确意图:只做一件事,提高表达能力。重复代码,包含尽可能少的实体,如类、方法、函数等。正确性:能通过所有测试,反映系统中的所有设计理念。阅读和写作的时间比例超过10:1。代码阅读的重要性对自己很重要负责干净代码的读者需要从头开始。成功的案例并不能使你成功。您只能与他人分享成功的过程。TipsChapter2MeaningfulNames(3stars)Clearsemanticsandclearcontext,noredundancyandclearsemantics:最好通过变量名来解释变量的含义,而不是注释。例如:magic值是一个没有明确含义的常量,以免误导:例如:缩写不明确;专有名词同名;命名类型不准确;都是多余的。例如:Table这个词永远不应该出现在表名中使用可读的名称使用可搜索的名称:单字母名称和数字常量仅用于短方法中的局部变量,最好不要用于避免思维导图:你不觉得,其他人会认为类名和对象名应该是名词或名词短语,而不是动词。方法名称应该是动词或动词短语。重载构造函数时,使用描述参数的静态工厂方法名称。例如,ComplexfulcrumPoint=Complex.FromRealNumber(23.0);通常优于ComplexfulcrumPoint=newComplex(23.0);您可以考虑将相应的构造函数设置为私有以强制执行此命名方法。不要双关语使用程序员熟悉的术语,或命名有问题的问题域添加有意义的上下文:向字段添加说明第3章函数(3颗星)本章是关于编写好的函数的技巧函数第一个经验法则是保持小功能应该只做好一件事。每个函数一个抽象级别使代码读起来像一系列自上而下的TO开头段落是保持抽象级别一致的有效技术。状态实现,保证每一个开关都埋在较低的抽象层次中,绝不重复例子:所有员工都有相同的流程,无论是发薪日、工资计算、工资支付,但不同类型的员工都有特定的流程动作不同publicMoneycalculatePay(Employeee)throwsInvalidEmployeeType{switch(e.type){caseCOMMISSIONED:returncalculateCommissionedPay(e);案例HOURLY:returncalculateHourlyPay(e);SALARIED案例:返回calculateSalariedPay(eth);默认值:新的InvalidEmployeeType(e.type);}}多态实现:改完之后,增加employee类型,每个employee只需要做自己的事情,改方法的时候不需要把所有类型都改,只需要改一下实现工厂实现类publicabstractclassEmployee{publicabstractbooleanisPayday();公共抽象货币计算支付();publicabstractvoiddeliverPay(Moneypay);}----------------publicinterfaceEmployeeFactory{publicEmployeemakeEmployee(EmployeeRecordr)throwsInvalidEmployeeType;}----------------publicclassEmployeeFactoryImplimplementsEmployeeFactory{publicEmployeemakeEmployee(EmployeeRecordr)throwsInvalidEmployeeType{switch(r.type){caseCOMMISSIONED:returnnewCommissionedEmployee(r);HOURLY案例:返回新的HourlyEmployee(r);caseSALARIED:returnnewSalariedEmploye(r);默认值:抛出新的InvalidEmployeeType(r.type);}}}使用描述性的名字不要怕长名字,不要怕花时间名字和命名方式要保持一致函数参数:理想的参数个数为零函数没有副作用:不要把这样的逻辑不是强相关,强调可重用性单独的指令和查询:避免混淆使用异常而不是返回错误代码.deleteKey(page.name.makeKey())==E_OK){logger.log("页面已删除");}else{logger.log("configKey未删除");}}else{logger.log("从注册表中删除引用失败");}}else{logger.log("删除失败");returnE_ERROR;}另一方面,如果你使用异常而不是返回错误代码,那么错误处理代码可以从快乐路径代码中分离出来并且可以简化:使用异常而不是返回错误代码,错误处理代码可以与主路径代码分离并简化:Try{deletePage(page);registry.deleteReference(page.name);configKeys.deleteKey(page.name.makeKey());}catch(Exceptione){logger.log(e.getMessage());}重复可能是软件中万恶之源好的代码需要慢慢打磨,精简,优化(这是我喜欢的过程,百看不厌)第4章评论备注(2星)毕竟作者是站在英文母语的基础上,大家还是要考虑自己的环境评论不能美化烂代码,注释不能成为烂代码的代言人,代码就是不要误导核心,不要胡说八道,及时整理TODO和注释代码块第5章格式化格式(1星)几乎不需要纵横格式看:代码缩进第6章对象和数据结构对象和数据结构(4星)对象暴露行为,隐藏数据使得添加新的对象类型而不修改现有行为变得容易,也很难添加新的对现有对象的行为。数据结构在没有明显行为的情况下公开数据。向现有数据结构添加新行为很容易,但向现有功能添加新数据结构也很困难。数据抽象:对象的数据封装、隐藏特定行为数据、反对称过程代码(使用数据结构的代码),便于在不改变现有数据结构的情况下增加新的功能。面向对象的代码使得在不改变现有功能的情况下添加新类变得容易。Demeter法则不应调用任何函数返回的对象的方法以下代码违反了Demeter法则(以及其他规则),因为它调用了返回值的getScratchDir()函数getScratchDir()的返回值的getAbsolutePath()方法再次被调用。finalStringoutputDir=ctxt.getOptions().getScratchDir().getAbsolutePath();第7章错误处理错误处理(4星)在本章中,编写既干净又健壮的代码——优雅地处理错误代码一些关于干净代码的提示和想法可读性强,但也健壮。可读与强不冲突。如果你把错误处理隔离开来,独立于主要逻辑,你可以写出强大而干净的代码使用异常而不是返回码,使用统一的异常处理来处理异常在写可能抛出异常的代码时,先写Try-Catch-finally语句使用不可控异常,可控异常的代价是违反开闭原则对异常发生的环境给出解释。根据调用者的需要定义异常类。当我们在应用程序中定义异常类时,最重要的考虑应该是它们是如何捕获的,然后根据捕获规则优化异常捕获不要返回空值不要传递空值第9章单元测试单元测试(3星)保持测试干净,测试代码和生产代码一样重要清洁测试,最重要的元素是可读性每个测试一个断言测试应该有以下规则:快速,独立,可重复,自包含验证,适时的第10章类(5颗星)更高级别的代码组织-类将系统的构造与使用单独类的组织相结合,遵循标准的Java约定,即类应从一组变量列表开始。如果有公共静态常量,它应该排在第一位。然后是私有静态变量和私有实体变量。很少会有公共变量。公共函数应遵循变量列表。包装类应该很小对于函数,我们通过计算代码行数来衡量大小。对于班级,我们使用不同的衡量标准,即计算责任。从命名规范开始,类的名称应描述其职责。单一职责原则(SRP)认为一个类或模块应该有一个且只有一个理由被修改。一个系统应该由许多小类组成,而不是几个大类。每个小类都封装了一个职责,只有一个修改的理由,并与少数其他类合作,以实现期望的系统行为。内聚性:类应该只有少量的实体变量。类中的每个方法都应该对这些变量中的一个或多个进行操作。一般来说,一个方法操作的变量越多,它就越依赖于类。可扩展性:需求改变,所以代码改变。具体类包含实现细节(代码),而抽象类只提供概念。当细节发生变化时,依赖于特定细节的客户端类将面临风险。我们可以借助接口和抽象类来隔离这些细节的影响。依赖倒置原则(DependencyInversionPrinciple,DIP),DIP认为类应该依赖于抽象而不是具体的细节。单一权限和责任、内聚都是度值,保证了它们的平衡性、逻辑内聚性、权责解耦。这个不简单,SRP也充分考虑了代码的可扩展性。第11章系统(5颗星)本章讨论如何在更高的抽象级别(系统级别)保持事物的清洁,无论是设计系统还是单个模块,并且不要忘记使用可能有效的最简单的解决方案。将系统的构建和使用分开(编译和运行,java环境下的Spring已经通过DependencyInjection(DI),InversionofControl(IoC)为我们做好了)。惰性初始化的好处:这种方法在DI中也有它的作用。首先,大多数DI容器在需要时才构造对象。其次,这些容器中的许多都提供了调用工厂或构建代理的机制,可用于惰性分配或类似的优化。AOP:在AOP中,称为方面的模块化构造指定系统中哪些点的行为要以一致的方式修改以支持特定场景。该规范是使用一些简明的声明或编程机制完成的。Proxy:使用代理,代码量和复杂度是代理的两大弱点,很难创建干净的代码!此外,代理不提供在系统范围内指定执行点的机制,而这正是纯JavaAOP框架对真正的AOP解决方案所要求的:在bean工厂内部,每个bean就像一个嵌套的“俄罗斯套娃”。”,每个由数据访问器对象(DAO)代理(包装)的Bank都有一个域对象,bean本身由JDBC驱动程序数据源代理。通过XML/annotation减少代码入侵,只留下纯POJO。AspectJASPECTS方面AspectJ的:然而,AspectJ提供了一组丰富而强大的切面划分工具。测试驱动系统架构:前期大设计(BDUF)-系统架构。最佳系统架构由模块化的关注域组成,每个关注域都使用纯Java(或其他语言)对象实现。使用侵入性最小的方面或类似方面的工具在不同领域之间进行集成。这种架构是测试驱动的,就像代码一样。最优决策:关注点的模块化和分割使得去中心化的管理和决策成为可能。具有模块化关注点的POJO系统提供的敏捷性使我们能够根据最新知识做出优化的、适时的决策。决策的复杂性也降低了。选择正确的架构——标准系统需要领域特定语言:领域特定语言(DSL)。DSL是一种用标准语言编写的小型单一脚本语言或API,领域专家可以使用它来编写读起来像组织良好的散文的代码。领域特定语言允许应用程序中的所有抽象级别和所有领域,从高级策略到低级细节,都可以使用POJO来表达。第十二章涌现本章所描述的实践来自于作者数十年经验的浓缩总结。遵循简单设计的实践,开发者不需要多年的学习就可以掌握好的原则和模式。提高内聚性、减少耦合、分离关注点、模块化系统关注点、减少函数和类大小、选择更好的名称等等。这也是简单设计的最后三个规则适用的地方:消除重复、保持表现力以及最小化类和方法的数量。通过迭代设计实现清洁,KentBeck的简单设计四法则根据Kent的说法,只要遵循以下规则,设计就可以“简单”,按重要性降序排列:运行所有测试;重复;表达程序员的意图;最小化类和方法的数量;第13章Concurrency并发编程(5星)《对象是进程的抽象,线程是调度的抽象》。——JamesO这一章主要讲的是了解并发编程的根源、优缺点,以及如何避免和解决并发错误的方法和方向?为什么要并发?并发是一种解耦策略。解耦目的和时序可以显着提高应用程序的吞吐量和结构。并发在性能和编写额外代码方面增加了一些开销;正确的并发是复杂的,即使对于简单的问题也是如此;并发错误并不总是可重现的,因此经常被视为事件而不是真正的缺陷;并发通常需要从根本上改变设计策略。并发防御原则单一职责原则单一职责原则(SRP)指出方法/类/组件应该只有一个修改原因。并发设计本身很复杂,足以成为修改的理由,因此也应该与其他代码分开。不幸的是,并发实现细节通常直接嵌入到其他生产代码中。需要考虑的事项:与并发相关的代码有自己的开发、修改和调优生命周期;开发相关代码有其自身的挑战,这些挑战与非并发相关代码不同,而且往往更困难;负担,编写糟糕的并发代码可能出错的方式数量已经足够具有挑战性了。建议:将与并发相关的代码与其他代码分开。限制数据范围两个线程修改共享对象的同一个字段可能会相互干扰,从而导致意外行为。一种解决方案是使用synchronized关键字来保护使用共享对象的代码的关键部分。限制关键部分的数量很重要。更新共享数据的地方越多,就越有可能:牢记数据封装;严格限制对可能共享的数据的访问。避免共享数据的最佳方法之一是首先避免共享数据。线程应该尽可能独立,让每个线程都存在于自己的世界中,不与其他线程共享数据。了解JavaLibraries了解类库,了解基本算法。理解类库提供的类似于底层算法的解题特性。理解执行模型学习这些底层算法并理解它们的解决方案。Producer-ConsumerProducer-ConsumerModelReaders-WritersReader-AuthorModelDiningPhilosophersDiningModel注意同步方法之间的依赖关系避免使用一个共享对象的多个方法有时你必须使用一个共享对象的多个方法,有3种解决方案:客户端-基于锁定——客户端代码在调用第一个方法之前锁定服务器,确保锁定范围覆盖调用最后一个方法的代码;基于服务器的锁定——在服务器内创建一个锁服务器端的方法调用所有方法然后解锁。让客户端代码调用新方法;调整服务器-创建一个执行锁定的中间层。这是一个不修改原始服务器代码的基于服务器的锁定示例。尽量缩小同步区以尽快考虑关机问题,使其尽快正常工作。测试线程代码编写在不同编程配置、系统配置和负载条件下频繁运行的潜在揭示测试。如果测试失败,跟踪错误。不要仅仅因为后面的测试通过了后面的运行就忽略失败。有一大堆问题需要考虑。这里有一些简洁的建议:将虚假故障视为可能的线程问题,不要将系统错误归因于使非线程代码首先工作的偶发事件,并且不要同时跟踪非线程和线程错误。确保您的代码在线程之外工作。编写可插入的线程代码,使其可以在不同的配置下运行。编写可调线程代码通常需要反复试验才能获得良好的线程平衡。最初,监视不同配置环境中的系统性能。允许的线程数是可调的。线程可以在系统运行时改变。允许线程根据吞吐量和系统使用情况进行自我调整。运行的线程多于处理器,当系统切换任务时会发生一些事情。要强制进行任务切换,请运行比处理器或处理器内核更多的线程。交换任务的频率越高,就越有可能找到遗漏关键部分或导致死锁的代码。不同平台安装试错代码,强制报错:安装代码有两种方式:硬编码,自动化第十五章JUnit内部结构JUnitInsider(2星)本章介绍JUnit的一些简单模块第十六章重构SerialDate(4星))本章详细讲解了重构和简化org.jfree.date库中SerialDate日期类的过程。增加了测试覆盖率,修复了一些错误,澄清并缩小了代码。第17章气味和启发式(3星)本章回顾了作者之前列出的一些坏习惯,并将这些与没有遵循一组规则编写的难闻的干净代码进行比较。附录A并发编程II(4星)一些关于并发编程的扩展信息,以及更多示例。在本章中,我们讨论并发更新,以及清理和避免同步的过程。我们讨论了线程如何提高I/O相关系统的吞吐量,并展示了实现这种改进的巧妙技术。我们讨论了死锁和彻底避免死锁的过程。最后,我们讨论了通过设备代码暴露并发问题的策略。死锁的发生需要4个条件:互斥:不能同时被多个线程使用;数量有限制此类资源的常见示例是数据库连接、为写入而打开的文件、记录锁或信号量。锁定并等待:当一个线程获取资源时,它不会释放资源,直到它获取所有其他需要的资源并完成它的工作。无抢占机制:线程无法抢占其他线程的资源。当一个线程持有资源时,其他线程要获得该资源的唯一途径就是等待该线程释放资源。循环等待:这也称为“厄运拥抱”。假设有两个线程T1和T2,以及两个资源R1和R2。T1拥有R1,T2拥有R2。T1需要R2,T2需要R1。这4个条件都是死锁的必要条件。只要其中一个不满足,就不会发生死锁。避免死锁的一种策略是规避互斥条件。您可以:使用允许同时使用的资源;将资源数量增加到等于或大于竞争线程的数量;在获取资源之前检查可用性。没有锁定和等待来满足抢占没有循环等待隔离解决方案的线程相关部分,然后进行调整和试验,是获得确定最佳策略所需洞察力的正确方法。干净的总结有经验值和定点。它不是按照一套规则编写的。看英文原文的时候突然想到:英语多是resulttheory,喜欢陈述事实,就像罪犯的对话