当前位置: 首页 > news >正文

《重构》读书笔记【第1章 重构,第一个示例,第2章 重构原则】

文章目录

    • 第1章 重构,第一个示例
      • 1.1 重构前
      • 1.2 重构后
    • 第2章 重构原则
      • 2.1 何谓重构
      • 2.2 两顶帽子
      • 2.3 为何重构
      • 2.4 何时重构
      • 2.5 重构和开发过程

第1章 重构,第一个示例

我这里使用的IDE是IntelliJ IDEA

1.1 重构前

  • plays.js
export const plays = {"hamlet": {"name": "Hamlet", "type": "tragedy"},"as-like": {"name": "As You Like It", "type": "comedy"},"othello": {"name": "Othello", "type": "tragedy"}
};
  • invoice.js
export const invoice = {"customer": "BigCo","performances": [{"playID": "hamlet","audience": 55},{"playID": "as-like","audience": 35},{"playID": "othello","audience": 40}]
}
  • statement.js
import {plays} from "./plays.js";
import {invoice} from "./invoice.js";function statement(invoice, plays) {let totalAmount = 0;let volumeCredits = 0;let result = `Statement for ${invoice.customer}\n`;const format = new Intl.NumberFormat("en-US",{style: "currency", currency: "USD",minimumFractionDigits: 2}).format;for (let perf of invoice.performances) {const play = plays[perf.playID];let thisAmount = 0;switch (play.type) {case "tragedy":thisAmount = 40000;if (perf.audience > 30) {thisAmount += 1000 * (perf.audience - 30);}break;case "comedy":thisAmount = 30000;if (perf.audience > 20) {thisAmount += 10000 + 500 * (perf.audience - 20);}thisAmount += 300 * perf.audience;break;default:throw new Error(`unknown type: ${play.type}`);}// add volume creditsvolumeCredits += Math.max(perf.audience - 30, 0);// add extra credit for every ten comedy attendeesif ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);// print line for this orderresult += ` ${play.name}: ${format(thisAmount / 100)} (${perf.audience} seats)\n`;totalAmount += thisAmount;}result += `Amount owed is ${format(totalAmount / 100)}\n`;result += `You earned ${volumeCredits} credits\n`;return result;
}let res = statement(invoice, plays);
console.log(res);
  • package.json
{"name": "untitled","version": "1.0.0","type": "module","dependencies": {}
}

运行结果

Statement for BigCoHamlet: $650.00 (55 seats)As You Like It: $580.00 (35 seats)Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits

1.2 重构后

  • plays.js
export const plays = {"hamlet": {"name": "Hamlet", "type": "tragedy"},"as-like": {"name": "As You Like It", "type": "comedy"},"othello": {"name": "Othello", "type": "tragedy"}
};
  • invoice.js
export const invoice = {"customer": "BigCo","performances": [{"playID": "hamlet","audience": 55},{"playID": "as-like","audience": 35},{"playID": "othello","audience": 40}]
}
  • package.json
{"name": "untitled","version": "1.0.0","type": "module","dependencies": {}
}
  • createStatementData.js
class PerformanceCalculator {constructor(aPerformance, aPlay) {this.performance = aPerformance;this.play = aPlay;}get volumeCredits() {return Math.max(this.performance.audience - 30, 0);}get amount() {throw new Error("subclass responsibility");}
}class TragedyCalculator extends PerformanceCalculator {get amount() {let result = 40000;if (this.performance.audience > 30) {result += 1000 * (this.performance.audience - 30);}return result;}
}class ComedyCalculator extends PerformanceCalculator {get amount() {let result = 30000;if (this.performance.audience > 20) {result += 10000 + 500 * (this.performance.audience - 20);}result += 300 * this.performance.audience;return result;}get volumeCredits() {return super.volumeCredits + Math.floor(this.performance.audience / 5);}
}function createPerformanceCalculator(aPerformance, aPlay) {switch (aPlay.type) {case "tragedy":return new TragedyCalculator(aPerformance, aPlay);case "comedy":return new ComedyCalculator(aPerformance, aPlay);default:throw new Error(`unknown type: ${aPlay.type}`);}
}export function createStatementData(invoice, plays) {const statementData = {};statementData.customer = invoice.customer;statementData.performances = invoice.performances.map(enrichPerformances);statementData.totalAmount = totalAmount(statementData);statementData.totalVolumeCredits = totalVolumeCredits(statementData);return statementData;function enrichPerformances(aPerformance) {const calculator = createPerformanceCalculator(aPerformance, playFor(aPerformance));const result = Object.assign({}, aPerformance);result.play = calculator.play;result.amount = calculator.amount;result.volumeCredits = calculator.volumeCredits;return result;}function playFor(aPerformance) {return plays[aPerformance.playID];}function totalAmount(data) {return data.performances.reduce((total, p) => total + p.amount, 0);}function totalVolumeCredits(data) {return data.performances.reduce((total, p) => total + p.volumeCredits, 0);}
}
  • statement.js
import {plays} from "./plays.js";
import {invoice} from "./invoice.js";
import {createStatementData} from "./createStatementData.js";function statement(invoice, plays) {return renderPlainText(createStatementData(invoice, plays));
}function renderPlainText(data) {let result = `Statement for ${data.customer}\n`;for (let perf of data.performances) {result += ` ${perf.play.name}: ${usd(perf.amount)} (${perf.audience} seats)\n`;}result += `Amount owed is ${usd(data.totalAmount)}\n`;result += `You earned ${(data.totalVolumeCredits)} credits\n`;return result;
}function htmlStatement (invoice, plays) {return renderHtml(createStatementData(invoice, plays));
}
function renderHtml (data) {let result = `<h1>Statement for ${data.customer}</h1>\n`;result += "<table>\n";result += "<tr><th>play</th><th>seats</th><th>cost</th></tr>";for (let perf of data.performances) {result += ` <tr><td>${perf.play.name}</td><td>${perf.audience}</td>`;result += `<td>${usd(perf.amount)}</td></tr>\n`;}result += "</table>\n";result += `<p>Amount owed is <em>${usd(data.totalAmount)}</em></p>\n`;result += `<p>You earned <em>${data.totalVolumeCredits}</em> credits</p>\n`;return result;
}function usd(aNumber) {return new Intl.NumberFormat("en-US",{style: "currency", currency: "USD",minimumFractionDigits: 2}).format(aNumber / 100);
}let res = statement(invoice, plays);
console.log(res);
let assert_res = "Statement for BigCo\n" +" Hamlet: $650.00 (55 seats)\n" +" As You Like It: $580.00 (35 seats)\n" +" Othello: $500.00 (40 seats)\n" +"Amount owed is $1,730.00\n" +"You earned 47 credits\n"console.log(res === assert_res)

第2章 重构原则

2.1 何谓重构

重构(名词):在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构(动词):使用重构手法,在不改变软件可观察行为的前提下,调整其结构。

重构的过程中,代码必须保持可用。如果重构导致代码不可用,那么它不可以称之为重构。

重构与性能优化的对比

重构性能优化
都修改代码,都不改变系统功能都修改代码,都不改变系统功能
为了可读性,为了可扩展性为了提升系统性能

2.2 两顶帽子

  • 添加新功能:不应该修改已有代码,只关注新功能。增加新测试,通过测试衡量工作进度
  • 重构:只改变程序内部结构,不应该添加测试(存在遗漏),不修改测试(除非接口发生变化)
  • 软件开发在这两者之间切换

2.3 为何重构

  • 改进软件设计:程序的设计在没有重构的情况下逐渐腐败变质,功能的增加或者修改可能使代码越来越难以理解。
  • 软件更容易理解:提高代码可读性。
  • 帮助找出bug:这个是建立在代码容易理解之上的。
  • 提高编程速度:良好设计降低开发和理解成本。

请添加图片描述

2.4 何时重构

  • 事不过三,三则重构:重复性问题若出现三次,就应该考虑重构。

见机行事重构

  • 预备性重构:最佳时机是在添加新功能之前进行,磨刀不误砍柴工。
  • 阅读时重构:遇到难以理解的代码时,考虑是否可以通过重构使其更清晰。
  • 人的思考资源宝贵:重构就是把理解转移到代码中,沉淀知识。
  • 捡垃圾式重构:“童子军军规”——至少让营地比你来时更干净。

有计划的重构

  • 日常编程中的重构:重构应是为了自己,而非单独排期。

  • 长期重构:大型重构应由整个团队共同参与,逐步推进。

  • CodeReview时的重构:考虑他人的理解,提高代码和设计的可读性。

  • 添加功能时重构:一方面可能是需要理解需要修改的代码,另一方面是使增加新特性更加容易。

  • 修补错误时重构:出现bug的时候,难以找出问题所在的时候,很有可能是代码不清晰导致查找bug的困难。

何时不应重构

  • 不需人理解的抽象代码:不需人常常修改,可放任自流。
  • 重写成本低于重构:若从头开始更经济,无需重构。

2.5 重构和开发过程

重构中不断集成,基于主干开发,保证自测试用例的完整性,CI(持续集成)、自动化测试和重构是不可分割的三位一体。

相关文章:

《重构》读书笔记【第1章 重构,第一个示例,第2章 重构原则】

文章目录 第1章 重构&#xff0c;第一个示例1.1 重构前1.2 重构后 第2章 重构原则2.1 何谓重构2.2 两顶帽子2.3 为何重构2.4 何时重构2.5 重构和开发过程 第1章 重构&#xff0c;第一个示例 我这里使用的IDE是IntelliJ IDEA 1.1 重构前 plays.js export const plays {&quo…...

学会整理电脑,基于小白用户(无关硬件升级)

如果你不想进行硬件升级&#xff0c;就要学会进行整理维护电脑 基于小白用户&#xff0c;每一个操作点我都会在后续整理出流程&#xff0c;软件推荐会选择占用小且实用的软件 主要从三个角度去讨论【如果有新的内容我会随时修改&#xff0c;也希望有补充告诉我&#xff0c;我…...

使用ioDraw,AI绘图只需几秒钟!

只需几秒钟&#xff0c;就能将文字或图片转化为精准的思维导图、流程图、折线图、柱状图、饼图等各种图表&#xff01; 思维导图 思维导图工具使用入口 文字转思维导图 将文本大纲或想法转换成可视化的思维导图&#xff0c;以组织和结构化您的想法。 图片转思维导图 从现有…...

Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)

1、什么是WebSocket? websocket的目标是通过一个长连接实现与服务器全双工&#xff0c;双向的通信。是一种在单个TCP连接上进行全双工通信的协议&#xff0c;使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在 js中创建websocket…...

Foxit Reader(福昕阅读器)详细安装和使用教程

第一部分&#xff1a;Foxit Reader简介和基本信息 1.1 什么是Foxit Reader&#xff1f; Foxit Reader&#xff08;福昕阅读器&#xff09;是一款功能强大的PDF阅读和编辑软件&#xff0c;以其快速、轻巧和丰富的功能而闻名。它不仅支持常规的PDF阅读功能&#xff0c;还提供了…...

c++静态成员变量和静态成员函数

1&#xff09;C入门级小知识&#xff0c;分享给将要学习或者正在学习C开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成…...

视频共享融合赋能平台LntonCVS统一视频接入平台数字化升级医疗体系

医疗健康事关国计民生&#xff0c;然而&#xff0c;当前我国医疗水平的地区发展不平衡、医疗资源分布不均和医疗信息系统老化等问题&#xff0c;制约了整体服务能力和水平的提升。视频融合云平台作为推动数字医疗的关键工具&#xff0c;在医疗领域的广泛应用和普及&#xff0c;…...

Gin框架基础

1、一个简单的Gin示例 下载并安装Gin: go get -u github.com/gin-gonic/gin1.1 一个简单的例子 package mainimport ("net/http""github.com/gin-gonic/gin" )func main() {// 创建一个默认的路由引擎r : gin.Default()// 当客户端以GET方式访问 /hello…...

用GPT-4纠错GPT-4 OpenAI推出CriticGPT模型

根据OpenAI周四&#xff08;6月27日&#xff09;发布的新闻稿&#xff0c;该公司新推出了一个基于GPT-4的模型——CriticGPT&#xff0c;用于捕获ChatGPT代码输出中的错误。CriticGPT的作用相当于让人们用GPT-4来查找GPT-4的错误。该模型可以对ChatGPT响应结果做出批评评论&…...

SQL CASE WHEN语句的使用技巧

SQL CASE WHEN语句的使用技巧 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在SQL查询中&#xff0c;经常需要根据不同的条件进行分支处理&#xff0c;这时就…...

虹科技术丨跨越距离障碍:PCAN系列网关在远程CAN网络通信的应用潜力

来源&#xff1a;虹科技术丨跨越距离障碍&#xff1a;PCAN系列网关在远程CAN网络通信的应用潜力 原文链接&#xff1a;虹科技术 | 跨越距离障碍&#xff1a;PCAN系列网关在远程CAN网络通信的应用潜力 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; #PCAN #网关 #CA…...

【UE 网络】RPC远程过程调用 入门篇

目录 0 引言1 RPC基本概念1.1 定义1.2 分类 2 RPC的使用2.1 Client RPC2.2 Server RPC2.3 Multicast RPC &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xff1a;【UE 网络】RPC远程过程调用 入门篇❣️ 寄语…...

安装maven与nexus

安装maven与nexus Maven官网下载地址&#xff1a;http://maven.apache.org cd /data/software/wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.8-bin.tar.gz# 解压 tar xf apache-maven-3.8.1-bin.tar.gz -C /opt/[rooth…...

如何用DCA1000持续采集雷达数据

摘要&#xff1a;本文介绍一下如何通过mmwave studio软件&#xff0c;搭配DCA1000数据采集卡&#xff0c;对AWR1843BOOST进行不间断的数据采集。本文要求读者已经掌握了有关基础知识。 本文开放获取&#xff0c;无需关注。 到SensorConfig页面下&#xff0c;一步步操作&#xf…...

怎么用JavaScript写爬虫

随着互联网技术的不断发展&#xff0c;爬虫&#xff08;web crawler&#xff09;已经成为当前最热门的爬取信息方式之一。通过爬虫技术&#xff0c;我们可以轻松地获取互联网上的数据&#xff0c;并用于数据分析、挖掘、建模等多个领域。而javascript语言则因其强大的前端开发工…...

Leetcode 3203. Find Minimum Diameter After Merging Two Trees

Leetcode 3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路2. 代码实现 题目链接&#xff1a;3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路 这一题的话算是一个拓扑树的题目&#xff1f;总之就是从树的叶子节点不断向上遍历&#xff…...

【抽代复习笔记】24-群(十八):循环群的两道例题

例1&#xff1a;证明&#xff1a; &#xff08;1&#xff09;三次交错群A3是循环群&#xff0c;它与(Z3,)同构&#xff0c;其中Z3 {[0],[1],[2]}&#xff1b; &#xff08;2&#xff09;G {1,i,-1,-i}&#xff0c;G上的代数运算是数的乘法&#xff0c;则G是一个循环群&…...

Linux常见操作问题

1、登录刚创建的用户&#xff0c;无法操作。 注&#xff1a;etc/passwd文件是Linux操作系统中存储用户账户信息的文本文件&#xff0c;包含了系统中所有用户的基本信息&#xff0c;比如用户名、用户ID、用户组ID、用户家目录路径。 注&#xff1a;etc: 这个目录存放所有的系统…...

鲁工小装载机-前后桥传动轴油封更换记录

鲁工装载机 因前后桥大量漏齿轮油&#xff0c;故拆开查看、更换油封 一&#xff1a; 如图圈起来的地方是螺丝和钢板相别&#xff0c;用200的焊接电流用电焊机点开一个豁口后拆除螺丝。 转轴是拆除传动轴后的样子。 这就是拆下来的样子&#xff0c;这玩意插上边那图&…...

商城自动化测试实战 —— 登录+滑块验证

hello大家好&#xff0c;我是你们的小编&#xff01; 本商城测试项目采取PO模型和数据分离式架构&#xff0c;采用pytestseleniumjenkins结合的方式进行脚本编写与运行&#xff0c;项目架构如下&#xff1a; 1、创建项目名称&#xff1a;code_shopping&#xff0c;创建所需项目…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...