《重构》读书笔记【第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,创建所需项目…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...