Jtoss Jtoss
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档

Jason Huang

后端程序猿
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档
  • 数据结构与算法

  • 代码规范与质量

  • 设计模式

    • 编写高质量代码概述
      • 1. 评判代码质量的标准
      • 2. 面向对象
      • 3. 设计原则
      • 4. 规范与重构
      • 5. 设计模式
      • 6. 编程能力
        • 如何考察编程能力?
        • 如何提高编程能力?
      • 7. 应对大型复杂项目开发方法论
    • 面向对象
    • 设计原则
    • 规范与重构
    • 设计模式 - 创建型
    • 设计模式 - 结构型
    • 设计模式 - 行为型(上)
    • 设计模式 - 行为型(下)
    • 浅析 JDK 源码中的设计模式
    • 浅析 Google Guava
    • 浅析 Spring 框架
    • 浅析 Mybatis 框架
    • 实现一个支持各种算法的限流框架
    • 设计实现一个通用的接口幂等框架
    • 设计实现一个支持自定义规则的灰度发布组件
  • 编程基础
  • 设计模式
Jason
目录

编写高质量代码概述

# 编写高质量代码概述

来源:极客时间《设计模式之美》 (opens new window)专栏笔记

面向对象、设计原则、设计模式、编程规范、代码重构五者之间的联系:

  • 面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式等编码实现的基础。
  • 设计原则是指导我们代码设计的一些经验总结,对于某些场景下,是否应该应用某种设计模式,具有指导意义。比如,“开闭原则”是很多设计模式(策略、模板等)的指导原则。
  • 设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。应用设计模式的主要目的是提高代码的可扩展性。从抽象程度上来讲,设计原 则比设计模式更抽象。设计模式更加具体、更加可执行。
  • 编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。
  • 重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编码规范这些理论。

# 1. 评判代码质量的标准

  1. 代码质量的高低 代码质量的评价有很强的主观性,描述代码质量的词汇也有很多,比如可读性、可维护性、灵活、优雅、简洁等,这些词汇是从不同的维度去评价代码质量的。它们之间有互相作用,并不是独立的,比如,代码的可读性好、可扩展性好就意味着代码的可维护性好。代码质量高低是一个综合各种因素得到的结论。我们并不能通过单一的维度去评价一段代码的好坏。
  2. 最常用的评价标 最常用到几个评判代码质量的标准是: 可维护性(maintainability)、可读性(readability)、可扩展性(extensibility)、灵活性(flexibility)、简洁性(simplicity)、可复用性(reusability)、可测试性(testability)。其中,可维护性、可读性、可扩展性又是提到最多的、最重要的三个评价标准。
  3. 高质量的代码 要写出高质量代码,需要掌握一些更加细化、更加能落地的编程方法论,这就包含面向对象设计思想、设计原则、设计模式、编码规范、重构技巧等等。

design8

# 2. 面向对象

主流的编程范式或编程风格有三种,分别是:面向过程、面向对象、函数式编程。其中面向对象是最主流的。面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式编码实现的基础。

design9

# 3. 设计原则

经典的设计原则包括,SOLID、KISS、YAGNI、DRY、LOD等。

design10

# 4. 规范与重构

对于项目来言,重构可以保持代码质量持续处于一个可控状态,不至于腐化到无可救药的地步。

design11

# 5. 设计模式

23 种经典设计模式共分为 3 种类型,分别是创建型、结构型和行为型。

创建型设计模式主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。它包括:单例模式、工厂模式、建造者模式、原型模式

结构型模式主要解决类或对象的组合。它包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式。

行为型设计模式主要解决的就是“类或对象之间的交互”问题。它包括:观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式、访问者模式、备忘录模式、命令模式、解释器模式、中介模式。

# 6. 编程能力

所谓编程能力,指的是把“逻辑”(或者说“功能”“业务”)翻译成代码的能力。所谓编程能力强,指的是,不仅能编写正确的代码,而且编写代码的速度很快,写出来的代码 bug 很少、性能很好、质量很高。

更加具体点讲,一个编程能力强的人,能熟练使用编程语言、开发类库等工具,思路清晰,面对复杂的逻辑,能够编写出 bug free 的代码,能够合理地利用数据结构和算法编写高效的代码,能够灵活地使用设计思想、原则和模式,编写易读、易扩展、易维护、可复用的高质量代码。

相反,一个编程能力差的人,往往逻辑思维能力比较差,面对复杂的逻辑,编写代码的速度很慢,而且容易考虑不周,写出来的代码 bug 很多,更没有性能意识,不懂得如何分析代码的时间复杂度、空间复杂度,更不懂得如何借助现成的数据结构和算法来优化代码性能。除此之外,写代码的时候,几乎不考虑代码的可读性、可扩展性等质量问题,只追求能运行就可以。

偏底层的开发(基础架构、框架、中间件等开发)考验程序员在某一细分领域的技术深度,偏业务的开发更加考验程序员的能力,比如沟通能力、分析问题解决问题能力、权衡取舍能力、架构能力等,毕竟业务多种多样,问题千奇百怪,单一细分领域的经验很难应对所有问题。

业务系统的开发难度一般来自两个方面:高性能要求和业务复杂。

解决性能问题需要具备一定的架构能力,有一定的技术广度,需要对各种基础架构、框架、中间件都有所了解。光了解还不够,还要有一定的技术深度,最好能对原理甚至是源码有所研究。除此之外,还要有一定的使用经验。广度、深度、经验三者配合,这样才能做到恰到好处组合这些技术搭建架构,解决性能问题,并且在出现问题之后才能快速地解决。

应对大型项目的业务复杂性,要想让项目代码一直在掌控范围内,需要有很强的业务建模能力、复杂逻辑的抽象能力、代码翻译能力等。对于一个人的基本素质、基础能力的要求要更高。实际上,对于复杂业务系统来说,对业务的熟悉也能成为竞争壁垒,成为升职加薪的砝码。低级别的晋升靠技术,高级别的晋升靠业务。

# 如何考察编程能力?

主要包含这样三个方面:编程语言,数据结构和算法,设计思想、原则和模式。

考察编程能力,那就离不开写代码。所以在面试中一般都会出道编程题,让候选人写段代码看看,也就是所谓的“白板编程”。这是最直接、最有效检验这个人基本技术素养的途径。重点考察两个方面:数据结构和算法,设计思想、原则和模式。

对于数据结构和算法的考察,不建议面试题目涉及需要记忆的算法,比如被很多人诟病的面试题:写个快排。也不建议面试题目涉及特殊解题方法或技巧,比如需要用到线段树、并查集等高级数据结构。毕竟在工作中不常用到这些知识,不知道也正常。

那种不依赖很强的算法背景和特殊的解题技巧的题目,比较适合用来面试。比如像下面这样的面试题就比较好:“写一个函数将 IPv4 地址字符串(仅包含数字、点、空格)转化成 32 位整数。另外,数字和点之间的空格是合法的,其他情况均为非法地址,要求输出合法地址的 32 位整型结果。”

# 如何提高编程能力?

《数据结构与算法之美》专栏教你如何写出高性能代码,《设计模式之美》专栏教你如何编写易读、易扩展、易维护的高质量代码。

对于数据结构和算法的练习,推荐多刷 LeetCode 上的题目。刷题不仅仅能加强对数据结构和算法的掌握,还能锻炼逻辑思维能力、写出 bug free 代码的能力、快速实现复杂逻辑的能力,也能锻炼你的性能意识。所以刷题不只是为了面试,刷题对这些能力的锻炼,都有助于你在平时的业务开发中写出好的代码。

对于设计思想原则和模式的练习,要在平时的开发中进行练习。比如拿到一个功能需求的时候,先去思考一下如何设计,而不是上来就写代码。写代码时时刻思考代码是否遵循了经典的设计思想、设计原则,比如是否足够可扩展、是否满足 SOLID 原则、可读性如何等等。写完代码之后再思考一下,代码是否有进一步优化的空间。做 Code Review 的时候,看到别人的优秀的代码就去思考一下,有哪些值得借鉴的地方。

# 7. 应对大型复杂项目开发方法论

  • 从设计原则和思想的角度来看,如何应对庞大而复杂的项目开发?
  • 从研发管理和开发技巧的角度来看,如何应对庞大而复杂的项目开发?
  • 聚焦在 Code Review 上来看,如何通过 Code Reviwe 保持项目的代码质量?
  1. 封装与抽象

    抽象和封装能有效控制代码复杂性的蔓延,将复杂性封装在局部代码中,隔离实现的易变性,提供简单、统一的访问接口,让其他模块来使用,其他模块基于抽象的接口而非具体的实现编程,代码会更加稳定。

  2. 分层与模块化

    将系统划分成各个独立的模块。不同的模块之间通过接口来进行通信,模块之间耦合很小,每个小的团队聚焦于一个独立的高内聚模块来开发,最终像搭积木一样,将各个模块组装起来,构建成一个超级复杂的系统。

    面对复杂系统的开发要善于应用分层技术,把容易复用、跟具体业务关系不大的代码,尽量下沉到下层,把容易变动、跟具体业务强相关的代码,尽量上移到上层。

  3. 基于接口通信

    不同的层之间、不同的模块之间的通信通过接口调用。在设计模块(module)或者层(layer)要暴露的接口的时候,要学会隐藏实现,接口从命名到定义都要抽象一些,尽量少涉及具体的实现细节。

  4. 高内聚、松耦合

    高内聚、松耦合是一个比较通用的设计思想,内聚性好、耦合少的代码,使得在修改或者阅读代码的时候,聚集到在一个小范围的模块或者类中,不需要了解太多其他模块或类的代码,让我们的焦点不至于太发散,也就降低了阅读和修改代码的难度。而且,因为依赖关系简单,耦合小,修改代码不会牵一发而动全身,代码改动比较集中,引入 bug 的风险也就减少了很多。

    比如封装、抽象、分层、模块化、基于接口通信,都能有效地实现代码的高内聚、松耦合。反过来,代码的高内聚、松耦合,也就意味着,抽象、封装做到比较到位、代码结构清晰、分层和模块化合理、依赖关系简单,那代码整体的质量就不会太差。即便某个具体的类或者模块设计得不怎么合理,代码质量不怎么高,影响的范围也是非常有限的。可以聚焦于这个模块或者类做相应的小型重构。而相对于代码结构的调整,这种改动范围比较集中的小型重构的难度就小多了。

  5. 为扩展而设计

    越是复杂项目,越要在前期设计上多花点时间。提前思考项目中未来可能会有哪些功能需要扩展,提前预留好扩展点,以便在未来需求变更的时候,在不改动代码整体结构的情况下,轻松地添加新功能。做到代码可扩展,需要代码满足开闭原则。

    除了满足开闭原则,做到代码可扩展,比如封装和抽象,基于接口编程等。识别出代码可变部分和不可变部分,将可变部分封装起来,隔离变化,提供抽象化的不可变接口,供上层系统使用。当具体的实现发生变化的时候,只需要基于相同的抽象接口,扩展一个新的实现,替换掉老的实现即可,上游系统的代码几乎不需要修改。

  6. KISS 首要原则

    简单清晰、可读性好,是任何大型软件开发要遵循的首要原则。只要可读性好,即便扩展性不好,顶多就是多花点时间、多改动几行代码的事情。但是,如果可读性不好,连看都看不懂,那就不是多花时间可以解决得了的了。如果对现有代码的逻辑似懂非懂,抱着尝试的心态去修改代码,引入 bug 的可能性就会很大。

    不管是自己还是团队,在参与大型项目开发的时候,要尽量避免过度设计、过早优化,在扩展性和可读性有冲突的时候,或者在两者之间权衡,模棱两可的时候,应该选择遵循 KISS 原则,首选可读性。

  7. 最小惊奇原则

    “最小惊奇原则”,英文是“The Least Surprise Principle”。实际上这个原则等同于“遵守开发规范”,意思是,在做设计或者编码的时候要遵守统一的开发规范,避免反直觉的设计。

    遵从统一的编码规范,所有的代码都像一个人写出来的,能有效地减少阅读干扰。在大型软件开发中,参与开发的人员很多,如果每个人都按照自己的编码习惯来写代码,那整个项目的代码风格就会千奇百怪,这个类是这种编码风格,另一个类又是另外一种风格。在阅读的时候,要不停地切换去适应不同的编码风格,可读性就变差了。所以对于大型项目的开发来说,要特别重视遵守统一的开发规范。

  8. 吹毛求疵般地执行编码规范

    严格执行代码规范,可以使一个项目乃至整个公司的代码具有完全统一的风格,就像同一个人编写的。而且,命名良好的变量、函数、类和注释,也可以提高代码的可读性。编码规范不难掌握,关键是要严格执行,细节决定成败,代码规范的严格执行极为关键。

  9. 编写高质量的单元测试

    单元测试是最容易执行且对提高代码质量见效最快的方法之一。高质量的单元测试不仅仅要求测试覆盖率要高,还要求测试的全面性,除了测试正常逻辑的执行之外,还要重点、全面地测试异常下的执行情况。毕竟代码出问题的地方大部分都发生在异常、边界条件下。

    对于大型复杂项目,集成测试、黑盒测试都很难测试全面,因为组合爆炸,穷举所有测试用例的成本很高,几乎是不可能的。单元测试就是很好的补充。它可以在类、函数这些细粒度的代码层面,保证代码运行无误。底层细粒度的代码 bug 少了,组合起来构建而成的整个系统的 bug 也就相应的减少了。

  10. 不流于形式的 Code Review

    Code Review 的价值:践行“三人行必有我师”、能摒弃“个人英雄主义”、能有效提高代码可读性、是技术传帮带的有效途径、能保证代码不止一个人熟悉、能打造良好的技术氛围、是一种技术沟通方式、能提高团队的自律性。 Review 考虑的也无外乎这样几个常见的方面:代码结构是否合理、代码是否容易理解、业务是否正确、异常考虑是否全面、是否有隐藏的 bug、线程是否安全、性能是否满足业务需求、是否符合编码规范等等

    对于比较复杂的 PR 一般建议要写好文档,或者通过类似 Jira 这样的项目工具,详细描述 PR 的前因后果、上下文背景。这样代码审查者就能一眼看懂代码包含的设计意图。对于争议比较多的 PR 建议直接当面沟通,这样也更加有效率。对于一些新手的 PR,因为他们对编码规范等不熟练,可能来来回回要改好几次,才能满足要求,但这个过程是每个新人都要经历的,多改几次就好了。

    Google 官方公布的 Code Review 最佳实践 (opens new window)

  11. 开发未动,文档先行

    一般来讲,在开发某个系统或者重要模块或者功能之前,应该先写技术文档,然后发送给同组或者相关同事审查,在审查没有问题的情况下再开发。这样能够保证事先达成共识,开发出来的东西不至于走样。而且当开发完成之后,进行 Code Review 的时候,代码审查者通过阅读开发文档,也可以快速理解代码。

    除此之外,对于团队和公司来讲,文档是重要的财富。对新人熟悉代码或任务的交接等,技术文档很有帮助。而且,作为一个规范化的技术团队,技术文档是一种摒弃作坊式开发和个人英雄主义的有效方法,是保证团队有效协作的途径。

  12. 持续重构、重构、重构

    优秀的代码或架构不是一开始就能设计好的,就像优秀的公司或产品也都是迭代出来的。随着系统的演进,重构是不可避免的。持续的小重构还是比较提倡的,它也是时刻保证代码质量、防止代码腐化的有效手段。换句话说,不要等到问题堆得太多了再去解决,要时刻有人对代码整体质量负责任,平时没事就改改代码。千万不要觉得重构代码就是浪费时间,不务正业!

  13. 对项目与团队进行拆分

    面对大型复杂项目,不仅仅需要对代码进行拆分,还需要对研发团队进行拆分。把大团队拆成几个小团队,每个小团队对应负责一个小的项目(模块、微服务等),这样每个团队负责的项目包含的代码都不至于很多,也不至于出现代码质量太差无法维护的情况。

#设计模式#编写高质量代码
上次更新: 2024-08-19
《重构》笔记
面向对象

← 《重构》笔记 面向对象→

最近更新
01
开始
01-09
02
AI工具分享
01-09
03
AI 导读
01-07
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Jason Huang | 闽ICP备2025088096号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式