关于java:代码整洁之道Clean-Code笔记

47次阅读

共计 8029 个字符,预计需要花费 21 分钟才能阅读完成。

@TOC

在线浏览:书栈网:https://www.bookstack.cn/read…

每个章节都会做一个本人的总结,并为这个章节打一个重要性的参考分数,满分五星(仅集体的角度)。

如果想尽快的理解一些代码的标准,最难看一下阿里代码标准,idea 也能够装置阿里代码标准插件。

阿里开发手册是实际,这本书自身更多的是作者理念疏导。理念的疏导必然少不了佐证、和一些看似冗余的语句,但这都是咱们站在现在开发环境上的后果论,因为所有技术性文章都是具备很强的时效性。

第 1 章 Clean Code 整洁代码(3 星)

第一章次要介绍这本书的背景和用意,以及总结下整洁代码的理念

有的章节几节能够间接越过,然而看完的话既知所以然,又知其然

?为什么要整洁的代码

反证:蹩脚代码的害处

团队中各司其职,代码是代码人应有的责任,要被动保护,去压服那些妨碍咱们优化的。。。

?什么叫做整洁代码

每个人对于整洁的定义都不同,这里只是作者代表的一些理念,要学会自我思考

  • 用意明确:只做一件事,进步表达力
  • 扩展性:提前构建小规模、简略形象
  • 简洁:缩小反复代码,包含尽量少的实体,比方类、办法、函数等
  • 正确性:能通过所有测试
  • 体现零碎中的全副设计理念
  • 读与写破费工夫的比例超过 10:1,代码浏览重要性,要对本人的读者负责
  • 整洁代码须要从点滴做起

胜利的案例并不能让你成为成功者,只能分享给他人胜利的过程,技巧

第 2 章 Meaningful Names 有意义的命名(3 星)

语义明确,语境明确,不冗余

  • 语义明确:最好通过变量名解说变量的意义,而不是正文。例如:魔法值,就是不能明确意义的常量
  • 防止误导:例如:缩写不明确;专有名词同名;命名类型不精确;类似命名放一起辨别,明确正文;类似数字字母不明确
  • 有意义的辨别,废话都是冗余。比方:Table 一词永远不该当呈现在表名中
  • 应用能够读的进去的名称
  • 应用能够搜寻的名字:单字母名称和数字常量仅用在短办法中的本地变量,最好不要用
  • 防止思维映射:不要你感觉,他人也会这样感觉
  • 类名和对象名应该是名词或名词短语,不该当是动词
  • 办法名该当是动词或动词短语

重载结构器时,应用形容了参数的动态工厂办法名。例如,

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

通常好于

Complex fulcrumPoint = new Complex(23.0);

能够思考将相应的结构器设置为 private,强制应用这种命名伎俩。

  • 别用双关语
  • 应用程序员相熟的术语,或者所涉问题畛域命名
  • 增加有意义的语境:对字段进行补充阐明

第 3 章 Functions 函数(3 星)

本章所讲述的是无关编写良好函数的机制

  • 函数的第一规定是要短小
  • 函数应该只做好这一件事
  • 每个函数一个形象层级

让代码读起来像是一系列自顶向下的 TO 起头段落是放弃形象层级协调一致的无效技巧

  • switch

利用多态来实现,确保每个 switch 都埋藏在较低的形象层级,而且永远不反复

例子:所有的员工都有一样的流程,是否发薪日、计算薪水、发薪水,然而不同类型的员工具体流程动作不一样

public Money calculatePay(Employee e)
        throws InvalidEmployeeType {switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
            throw new InvalidEmployeeType(e.type);
    }
}

多态实现:更改之后再减少职员类型,每种职工只须要做本人的事件,不须要所有的类型当办法都更改,只需更改实现工厂实现类

public abstract class Employee {public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliverPay(Money pay);
}
-----------------
public interface EmployeeFactory {public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
-----------------
public class EmployeeFactoryImpl implements
        EmployeeFactory {public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {switch (r.type) {
            case COMMISSIONED:
                return new CommissionedEmployee(r);
            case HOURLY:
                return new HourlyEmployee(r);
            case SALARIED:
                return new SalariedEmploye(r);
            default:
                throw new InvalidEmployeeType(r.type);
        }
    }
}
  • 应用描述性的名称

别胆怯长名称、别胆怯花工夫取名字、命名形式要保持一致

  • 函数参数: 最现实的参数数量是零
  • 无副作用函数:不要把相关性不强的逻辑放进来,强调复用性
  • 分隔指令与询问:防止凌乱
  • 应用异样代替返回错误码
if (deletePage(page) == E_OK) {if (registry.deleteReference(page.name) == E_OK) {if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {logger.log("page deleted");        } else {logger.log("configKey not deleted");        }    } else {logger.log("deleteReference from registry failed");    }} else {logger.log("delete failed");    return E_ERROR;}

On the other hand, if you use exceptions instead of returned error codes, then the error processing code can be separated from the happy path code and can be simplified:

另一方面,如果应用异样代替返回错误码,错误处理代码就能从主门路代码中分离出来,失去简化:

 复制代码 try {deletePage(page);    registry.deleteReference(page.name);    configKeys.deleteKey(page.name.makeKey());} catch (Exception e) {logger.log(e.getMessage());}
  • 反复可能是软件中所有邪恶的本源
  • 好的代码须要缓缓打磨,精简,优化(这正是我喜爱的过程,乐此不疲)

第 4 章 Comments 正文(2 星)

作者毕竟站在英语母语的根底上,还是要思考下咱们本人的环境

  • 正文不能丑化蹩脚的代码,正文不能成为蹩脚代码的发言人,代码才是外围
  • 别误导,别废话,适时整顿 TODO、正文的代码块

第 5 章 Formatting 格局(1 星)

简直不必看

  • 垂直、横向格局:代码缩进

第 6 章 Objects and Data Structures 对象和数据结构(4 星)

对象曝露行为,暗藏数据。便于增加新对象类型而无需批改既有行为,同时也难以在既有对象中增加新行为。

数据结构曝露数据,没有显著的行为。便于向既有数据结构增加新行为,同时也难以向既有函数增加新数据结构。

  • 数据抽象:数据封装,暗藏具体行为
  • 数据、对象的反对称性

过程式代码(应用数据结构的代码)便于在不改变既有数据结构的前提下增加新函数。面向对象代码便于在不改变既有函数的前提下增加新类。

  • 得墨忒耳律

办法不应调用由任何函数返回的对象的办法

下列代码违反了得墨忒耳律(除了违反其余规定之外),因为它调用了 getOptions()返回值的 getScratchDir()函数,又调用了 getScratchDir()返回值的 getAbsolutePath()办法。

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

第 7 章 Error Handling 错误处理(4 星)

在本章中,要列出编写既整洁又强固的代码——优雅地解决错误代码的一些技巧和思路

整洁代码是可读的,但也要强固。可读与强固并不抵触。如果将错误处理隔离对待,独立于次要逻辑之外,就能写出强固而整洁的代码

  • 应用异样而非返回码,用对立异样解决解决异样
  • 在编写可能抛出异样的代码时,先写 Try-Catch-Finally 语句
  • 应用不可控异样,可控异样的代价就是违反凋谢 / 闭合准则
  • 给出异样产生的环境阐明
  • 依调用者须要定义异样类

咱们在应用程序中定义异样类时,最重要的思考应该是它们如何被捕捉,而后依据捕捉法则去优化异样捕捉

  • 别返回 null 值
  • 别传递 null 值

第 9 章 Unit Tests 单元测试(3 星)

  • 放弃测试整洁,测试代码和生产代码一样重要
  • 整洁的测试,最重要的因素是可读性
  • 每个测试一个断言
  • 测试应该有以下规定:疾速、独立、可反复、自足验证、及时

第 10 章 Classes 类(5 星)

代码组织的更高层面——Classes

  • 将零碎的结构与应用离开
  • 类的组织

遵循规范的 Java 约定,类应该从一组变量列表开始。如果有公共动态常量,应该先呈现。而后是公有动态变量,以及公有实体变量。很少会有公共变量。公共函数应跟在变量列表之后。封装

  • 类应该短小

对于函数,咱们通过计算代码行数掂量大小。对于类,咱们采纳不同的掂量办法,计算权责(responsibility)。类的名称该当形容其权责,从命名开始标准。

繁多权责准则(SRP)认为,类或模块应有且只有一条加以批改的理由。零碎应该由许多短小的类而不是大量微小的类组成。每个小类封装一个权责,只有一个批改的起因,并与多数其余类一起协同达成冀望的零碎行为。

内聚:类应该只有大量实体变量。类中的每个办法都应该操作一个或多个这种变量。通常而言,办法操作的变量越多,就越黏聚到类上。

扩展性:需要会扭转,所以代码也会扭转。具体类蕴含实现细节(代码),而抽象类则只出现概念。依赖于具体细节的客户类,当细节扭转时,就会有危险。咱们能够借助接口和抽象类来隔离这些细节带来的影响。依赖倒置准则(Dependency Inversion Principle,DIP),DIP 认为类该当依赖于形象而不是依赖于具体细节。

繁多权责和内聚都是水平值,保障的他们均衡 ,逻辑内聚,权责解耦,这并不简略,SRP 也充分考虑到了代码扩展性。

第 11 章 Systems 零碎(5 星)

本章将探讨如何在较高的形象层级——零碎层级——上放弃整洁

无论是设计零碎或独自的模块,别忘了应用大略可工作的最简略计划。

  • 将零碎的结构与应用离开(编译和运行,java 的环境中 Spring 通过依赖注入(Dependency Injection,DI),管制反转(Inversion of Control,IoC)曾经帮咱们把这件事做了)。延后初始化的益处:这种伎俩在 DI 中也有其作用。首先,少数 DI 容器在须要对象之前并不结构对象。其次,许多这类容器提供调用工厂或结构代理的机制,而这种机制可为提早赋值或相似的优化解决所用。
  • AOP:在 AOP 中,被称为方面(aspect)的模块结构指明了零碎中哪些点的行为会以某种统一的形式被批改,从而反对某种特定的场景。这种阐明是用某种简洁的申明或编程机制来实现的。
  • 代理:应用代理,代码量和复杂度是代理的两大弱点,创立整洁代码变得很难!另外,代理也没有提供在零碎范畴内指定执行点的机制,而那正是真正的 AOP 解决方案所必须的
  • 污浊的 Java AOP 框架:Bean 工厂内,每个 bean 就像是嵌套“俄罗斯套娃”中的一个,每个由数据存取器对象(DAO)代理(包装)的 Bank 都有个域对象,而 bean 自身又是由 JDBC 驱动程序数据源代理。通过 XML/ 注解的形式缩小对代码的入侵,只留下纯 POJO。
  • AspectJ ASPECTS AspectJ 的方面:AspectJ 却提供了一套用以切分关注面的丰盛而强有力的工具。
  • 测试驱动零碎架构:大设计(Big Design Up Front,BDUF)——零碎架构。最佳的零碎架构由模块化的关注面畛域组成,每个关注面均用纯 Java(或其余语言)对象实现。不同的畛域之间用最不具备侵害性的方面或类方面工具整合起来。这种架构能测试驱动,就像代码一样。
  • 优化决策:模块化和关注面切分成就了分散化治理和决策。领有模块化关注面的 POJO 零碎提供的麻利能力,容许咱们基于最新的常识做出优化的、机会 刚好的决策。决策的复杂性也升高了。
  • 抉择适合的架构——规范
  • 零碎须要畛域特定语言:畛域特定语言(Domain-Specific Language,DSL)。DSL 是一种独自的小型脚本语言或以规范语言写就的 API,领域专家能够用它编写读起来像是组织谨严的散文个别的代码。畛域特定语言容许所有形象层级和应用程序中的所有畛域,从高级策略到底层细节,应用 POJO 来表白。

第 12 章 Emergence 迭进

本章中写到的实际来自于本书作者数十年教训的简练总结。遵循简略设计的实际伎俩,开发者不用经年学习就能把握好的准则和模式。

晋升内聚性,升高耦合度,切分关注面,模块化系统性关注面,放大函数和类的尺寸,选用更好的名称,如此等等。这也是利用简略设计后三条规定的中央:打消反复,保障表达力,尽可能减少类和办法的数量。

通过迭进设计达到整洁目标,Kent Beck 对于简略设计的四条规定,据 Kent 所述,只有遵循以下规定,设计就能变得“简略”,以下规定按其重要水平降序排列:

  • 运行所有测试;
  • 不可反复;
  • 表白了程序员的用意;
  • 尽可能减少类和办法的数量;

第 13 章 Concurrency 并发编程(5 星)

“对象是过程的形象。线程是调度的形象。”——James O

这个章节次要讲述了并发编程的起源、劣势和劣势,以及如何防止、解决并发谬误的办法和方向

? 为什么要并发

  • 并发是一种解耦策略。
  • 解耦目标与机会能显著地改良应用程序的吞吐量和构造。
  • 并发会在性能和编写额定代码上减少一些开销;
  • 正确的并发是简单的,即使对于简略的问题也是如此;
  • 并发缺点并非总能重现,所以常被看做偶发事件而疏忽,未被当做真的缺点对待;
  • 并发经常须要对设计策略的根本性批改。

并发进攻准则

  • 繁多权责准则

繁多权责准则(SRP)认为,办法 / 类 / 组件该当只有一个批改的理由。并发设计本身足够简单到成为批改的理由,所以也该从其余代码中分离出来。可怜的是,并发实现细节经常间接嵌入到其余生产代码中。

须要思考的问题:

并发相干代码有本人的开发、批改和调优生命周期;

开发相干代码有本人要凑合的挑战,和非并发相干代码不同,而且往往更为艰难;

即使没有周边应用程序减少的累赘,写得不好的并发代码可能的出错形式数量也曾经足具挑战性。

倡议:拆散并发相干代码与其余代码。

  • 限度数据作用域

两个线程批改共享对象的同一字段时,可能相互烦扰,导致未预期的行为。解决方案之一是采纳 synchronized 关键字在代码中爱护一块应用共享对象的临界区(critical section)。限度临界区的数量很重要。更新共享数据的中央越多,就越可能:

谨记数据封装;严格限度对可能被共享的数据的拜访。

防止共享数据的好办法之一就是一开始就防止共享数据。

线程应尽可能地独立,让每个线程在本人的世界中存在,不与其余线程共享数据。

理解 Java 库

学习类库,理解根本算法。了解类库提供的与根底算法相似的解决问题的个性。

理解执行模型

学习这些根底算法,了解其解决方案。

  • Producer-Consumer 生产者 - 消费者模型
  • Readers-Writers 读者 - 作者模型
  • Dining Philosophers 哲学家用餐模式

警觉同步办法之间的依赖

  • 防止应用一个共享对象的多个办法
  • 有时必须应用一个共享对象的多个办法,有 3 种应答伎俩:
  • 基于客户端的锁定——客户端代码在调用第一个办法前锁定服务端,确保锁的范畴笼罩了调用最初一个办法的代码;
  • 基于服务端的锁定——在服务端内创立锁定服务端的办法,调用所有办法,而后解锁。让客户端代码调用新办法;
  • 适配服务端——创立执行锁定的中间层。这是一种基于服务端的锁定的例子,但不批改原始服务端代码。

尽可能减小同步区域

尽早思考敞开问题,尽早令其工作失常。

测试线程代码

编写有后劲曝露问题的测试,在不同的编程配置、系统配置和负载条件下频繁运行。如果测试失败,跟踪谬误。别因为起初测试通过了起初的运行就疏忽失败。

有一大堆问题要思考。上面是一些简练的倡议:

  • 将伪失败看作可能的线程问题,不要将零碎谬误归咎于偶发事件
  • 先使非线程代码可工作, 不要同时追踪非线程缺点和线程缺点。确保代码在线程之外可工作。
  • 编写可插拔的线程代码,这样就能在不同的配置环境下运行。
  • 编写可调整的线程代码,要取得良好的线程均衡,经常须要试错。一开始,在不同的配置环境下监测零碎性能。要容许线程数量可调整。在零碎运行时容许线程产生变动。容许线程根据吞吐量和零碎使用率自我调整。
  • 运行多于处理器数量的线程,零碎在切换工作时会产生一些事。为了促使工作替换的产生,运行多于处理器或处理器外围数量的线程。工作替换越频繁,越有可能找到错过临界区或导致死锁的代码。
  • 在不同平台上运行
  • 安装试错代码,并强制谬误产生:有两种安装代码的办法:硬编码、自动化

第 15 章 JUnit Internals JUnit 底细(2 星)

本章介绍了 JUnit 的一些简略的模块

第 16 章 重构 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 种条件都是死锁所必须的。只有其中一个不满足,死锁就不会产生。

防止死锁的一种策略是躲避互斥条件。你能够:

  • 应用容许同时应用的资源;
  • 减少资源数量,使其等于或大于竞争线程的数量;
  • 在获取资源之前,查看是否可用。
  • 不上锁及期待
  • 满足领先机制
  • 不做循环期待

将解决方案中与线程相干的局部分隔进去,再加以调整和试验,是取得判断最佳策略所需的洞见的邪道。

总结

洁净有经验值,也有固定分,不是通过遵循一组规定来编写的,须要的是迭进,不须要钻牛角尖。

读英文原文的时候忽然想到:英语大多是后果论,喜爱陈说事实,就如同罪犯的对白

正文完
 0