《重构》读书笔记【第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章 重构,第一个示例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 {&quo…...

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

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

Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)
1、什么是WebSocket? websocket的目标是通过一个长连接实现与服务器全双工,双向的通信。是一种在单个TCP连接上进行全双工通信的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 js中创建websocket…...
Foxit Reader(福昕阅读器)详细安装和使用教程
第一部分:Foxit Reader简介和基本信息 1.1 什么是Foxit Reader? Foxit Reader(福昕阅读器)是一款功能强大的PDF阅读和编辑软件,以其快速、轻巧和丰富的功能而闻名。它不仅支持常规的PDF阅读功能,还提供了…...

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

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

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周四(6月27日)发布的新闻稿,该公司新推出了一个基于GPT-4的模型——CriticGPT,用于捕获ChatGPT代码输出中的错误。CriticGPT的作用相当于让人们用GPT-4来查找GPT-4的错误。该模型可以对ChatGPT响应结果做出批评评论&…...
SQL CASE WHEN语句的使用技巧
SQL CASE WHEN语句的使用技巧 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在SQL查询中,经常需要根据不同的条件进行分支处理,这时就…...

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

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

安装maven与nexus
安装maven与nexus Maven官网下载地址: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持续采集雷达数据
摘要:本文介绍一下如何通过mmwave studio软件,搭配DCA1000数据采集卡,对AWR1843BOOST进行不间断的数据采集。本文要求读者已经掌握了有关基础知识。 本文开放获取,无需关注。 到SensorConfig页面下,一步步操作…...
怎么用JavaScript写爬虫
随着互联网技术的不断发展,爬虫(web crawler)已经成为当前最热门的爬取信息方式之一。通过爬虫技术,我们可以轻松地获取互联网上的数据,并用于数据分析、挖掘、建模等多个领域。而javascript语言则因其强大的前端开发工…...
Leetcode 3203. Find Minimum Diameter After Merging Two Trees
Leetcode 3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路2. 代码实现 题目链接:3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路 这一题的话算是一个拓扑树的题目?总之就是从树的叶子节点不断向上遍历ÿ…...
【抽代复习笔记】24-群(十八):循环群的两道例题
例1:证明: (1)三次交错群A3是循环群,它与(Z3,)同构,其中Z3 {[0],[1],[2]}; (2)G {1,i,-1,-i},G上的代数运算是数的乘法,则G是一个循环群&…...

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

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

商城自动化测试实战 —— 登录+滑块验证
hello大家好,我是你们的小编! 本商城测试项目采取PO模型和数据分离式架构,采用pytestseleniumjenkins结合的方式进行脚本编写与运行,项目架构如下: 1、创建项目名称:code_shopping,创建所需项目…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
第22节 Node.js JXcore 打包
Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...