《重构》读书笔记【第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,创建所需项目…...
别再裸发ROS图像了!手把手教你用image_transport优化带宽(附压缩参数配置)
机器人视觉开发者的带宽救星:深度解析ROS image_transport图像压缩实战 在机器人视觉应用开发中,高分辨率图像的实时传输常常成为性能瓶颈。当你的SLAM系统在Wi-Fi环境下频繁丢帧,或者目标检测算法因为图像延迟而失效时,问题的根源…...
探索OpenBoardView:硬件工程师的PCB分析利器
探索OpenBoardView:硬件工程师的PCB分析利器 【免费下载链接】OpenBoardView View .brd files 项目地址: https://gitcode.com/gh_mirrors/op/OpenBoardView 在现代电子硬件开发与维修领域,面对复杂的电路板设计文件,工程师们常常需要…...
离线语音技术如何重塑智能照明:从核心原理到产品实战
1. 从“在线”到“离线”:智能照明交互的范式转变作为一名在智能家居领域摸爬滚打了十来年的从业者,我亲眼见证了智能照明从最初的手机APP遥控,到后来的智能音箱联动,再到如今离线语音技术的兴起。每次技术迭代,都不仅…...
基于Cadence Virtuoso的gm/ID曲线仿真与参数扫描实战指南
1. 从零理解gm/ID设计方法学 在模拟电路设计领域,随着工艺节点不断缩小,我们这些工程师遇到了一个尴尬的现实:教科书里的那些经典公式越来越不灵了。记得我第一次用28nm工艺设计运放时,按照传统方法计算的增益和实测结果差了近40%…...
终极指南:如何用BookGet快速下载全球50+图书馆古籍资源
终极指南:如何用BookGet快速下载全球50图书馆古籍资源 【免费下载链接】bookget bookget 数字古籍图书下载工具。 项目地址: https://gitcode.com/gh_mirrors/bo/bookget BookGet是一款强大的数字古籍图书下载工具,支持全球50多个知名数字图书馆的…...
Cursor智能体学习工具:构建专属AI编程知识库的完整指南
1. 项目概述:一个为开发者量身定制的Cursor智能体学习工具如果你是一名开发者,并且最近正在尝试使用Cursor这款AI编程工具,那么你很可能和我一样,经历过一个既兴奋又有点迷茫的阶段。Cursor的强大毋庸置疑,它能理解上下…...
Obsidian Projects:开源文本项目管理的终极解决方案
Obsidian Projects:开源文本项目管理的终极解决方案 【免费下载链接】obsidian-projects Plain text project planning in Obsidian 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-projects 在当今信息爆炸的时代,高效的项目管理工具已成…...
Transformer在CV领域的新秀:拆解TransWeather如何用‘天气查询’一招解决多任务难题
Transformer在CV领域的新秀:拆解TransWeather如何用‘天气查询’一招解决多任务难题 计算机视觉领域正经历一场由Transformer架构引领的革命。从最初的图像分类任务到如今的复杂场景理解,Transformer以其强大的全局建模能力不断刷新着各项基准。而在天气…...
基于Agen项目构建个人AI代理:从LLM原理到邮件处理实战
1. 项目概述:从“Agen”看个人化AI代理的构建思路最近在GitHub上看到一个名为“Agen”的项目,作者是Anjuan555。这个项目名本身就很值得玩味——“Agen”,很容易让人联想到“Agent”(代理),但又少了一个“t…...
ARM Cortex-A72 GICv3中断处理机制与优化实践
1. ARM Cortex-A72 GIC CPU接口架构概述在ARMv8-A架构中,通用中断控制器(GIC)作为中断管理的核心组件,其CPU接口承担着处理器核心与中断源之间的桥梁作用。Cortex-A72处理器实现了GICv3架构规范,相较于前代GICv2,主要引入了以下关…...
