CH06_第一组重构(下)

news2024/11/29 8:43:35

封装变量(Encapsulate Variable | 132)

曾用名:自封装字段(Self-Encapsulate Field)

曾用名:封装字段(Encapsulate Field)

在这里插入图片描述

let defaultOwner = {firstName: "Martin", lastName: "Fowler"};
let defaultOwnerData = {firstName: "Martin", lastName: "Fowler"};
export function defaultOwner() {return defaultOwnerData;}
export function setDefaultOwner(arg) {defaultOwnerData = arg;}

动机

重构的作用就是调整程序中的元素。函数相对容易调整一些,因为函数只有一种用法,就是调用。在改名或搬移函数的过程中,总是可以比较容易地保留旧函数作为转发函数(即旧代码调用旧函数,旧函数再调用新函数)。这样的转发函数通常不会存在太久,但的确能够简化重构过程。

如果数据的可访问范围变大,重构的难度就会随之增大,这也是说全局数据是大麻烦的原因。

如果想要搬移一处被广泛使用的数据,最好的办法往往是先以函数形式封装所有对该数据的访问。

封装能提供一个清晰的观测点,可以由此监控数据的变化和使用情况,还可以轻松地添加数据被修改时的验证或后续逻辑。数据的作用域越大,封装就越重要。处理遗留代码时,一旦需要修改或增加使用可变数据的代码,可以借机把这份数据封装起来,从而避免继续加重耦合一份已经广泛使用的数据。

面向对象方法如此强调对象的数据应该保持私有(private),背后也是同样的原理。每当看见一个公开(public)的字段时,考虑使用封装变量来缩小其可见范围。

封装数据很重要,不过,不可变数据更重要。如果数据不能修改,就根本不需要数据更新前的验证或者其他逻辑钩子。

做法

  • 创建封装函数,在其中访问和更新变量值。
  • 执行静态检查。
  • 逐一修改使用该变量的代码,将其改为调用合适的封装函数。每次替换之后,执行测试。
  • 限制变量的可见性。
  • 测试。
  • 如果变量的值是一个记录,考虑使用封装记录(162)。

封装值

对数据结构的引用做封装,能控制对该数据结构的访问和重新赋值;但并不能控制对结构内部数据项的修改。

控制结构内部数据的修改方法:

  • 取值函数中返回数据的一份副本(保护源数据,防止源数据变化导致的意外事故)

  • 阻止对数据的修改(禁止对数据结构内部的数值做任何修改)

数据封装很有价值,但往往并不简单。到底应该封装什么,以及如何封装,取决于数据被使用的方式,以及想要修改数据的方式。

变量改名(Rename Variable | 137)

在这里插入图片描述

let a = height * width;
let area = height * width; 

动机

好的命名是整洁编程的核心。变量名起得好,变量可以很好地解释一段程序在干什么。

变量使用范围越广,名字的好坏就越重要。

机制

  • 如果变量被广泛使用,考虑运用封装变量(132)将其封装起来。
  • 找出所有使用该变量的代码,逐一修改。(已发布变量,不能进行这个重构)
  • 测试。

引入参数对象(Introduce Parameter Object | 140)

在这里插入图片描述

function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}
function amountInvoiced(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}

动机

一组数据项(作为函数参数)总是同时出现(这样一组数据就是所谓的数据泥团)。

将数据组织成结构是一件有价值的事,因为这让数据项之间的关系变得明晰。使用新的数据结构,参数的参数列表也能缩短。

做法

  • 如果暂时还没有一个合适的数据结构,就创建一个
  • 测试
  • 使用改变函数声明(124)给原来的函数新增一个参数,类型是新建的数据结构
  • 测试
  • 调整所以调用者,传入新数据结构的适当实列。修改每一处,执行测试
  • 用新数据结构中的每一项元素,逐一取代参数列表中与之对应的参数项,然后删除原来的参数。测试

函数组合成类(Combine Functions into class | 144)

在这里插入图片描述

function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}
class Reading {
    base() {...}
    taxableCharge() {...}
    calculateBaseCharge() {...}
}

动机

类,在大多数现代编程语言中都是基本的构造。它们把数据与函数捆绑到同一个环境中,将一部分数据与函数暴露给其他程序元素以便协作。

如果发现一组函数相互操作同一块数据(通常是将这块数据作为参数传递给函数),可以将这几个函数足见2一个类。类能明确地给这些函数提供一个共用的环境,在对象内部调用这些函数可以少传许多参数,从而简化函数调用。

做法

  • 运用封装记录(162)对多个函数共用的数据记录加以封装。
  • 对于使用该记录结构的每个函数,运用搬移函数(198)将其移入新类。(如果函数调用时传入的参数已经是新类的成员,则从参数列表中去除之。)
  • 用以处理该数据记录的逻辑可以用提炼函数(106)提炼出来,并移入新类。

函数组合成变换(Combine Functions into Transfrom | 149)

在这里插入图片描述

动机

在软件中经常有这类操作:一个数据项在一个函数中处理加工后,有进入到其他函数中继续处理加工,经过几番处理,才得到最终需要的结果(可能我们的初衷不这样,但由于后期新需求、功能的添加才形成这种现象)。

一个方式是采用数据变换(transform)函数:这种函数接受源数据作为输入,计算出所有的派生数据,将派生数据以字段形式填入输出数据。

函数组合成变换的替代方案是函数组合成类(144),后者的做法是先用源数据创建一个类,再把相关的计算逻辑搬移到类中。

做法

  • 创建一个变换函数,输入参数是需要变换的记录,并直接返回该记录的值。
  • 挑选一块逻辑,将其主体一如变换函数中,把结果作为字段添加到输出记录中。修改客户端代码,令其使用这个新字段
  • 测试
  • 针对其他相关的计算逻辑,重复上述步骤

拆分阶段(Split Phase | 154)

在这里插入图片描述

const orderData = orderString.split(/\s+/);
const productPrice = priceList[orderData[0].split("-")[1]];
const orderPrice = parseInt(orderData[1]) * productPrice;
const orderRecord = parseOrder(order);
const orderPrice = price(orderRecord, priceList);
function parseOrder(aString) {
const values = aString.split(/\s+/);
    return ({
        productID: values[0].split("-")[1],
        quantity: parseInt(values[1]),
    });
}
function price(order, priceList) {
	return order.quantity * priceList[order.productID];
}

动机

一段代码在同时处理两件不同的事。

最简洁的拆分方法之一,就是把一大段行为分成顺序执行的两个阶段。

做法

  • 将第二阶段的代码提炼成独立的函数
  • 测试
  • 引入一个中转数据结构,将其作为参数添加到提炼出的新函数的参数中
  • 测试
  • 逐一检查提炼出的“第二阶段函数”的每个参数。如果某个参数被第一阶段用到,将其移入中转数据结构。每次搬移之后都要执行测试。
  • 对第一阶段的代码运用提炼函数(106),让提炼出的函数返回中转数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/992024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pointpillar 论文解读

Pointpillar 论文解读 主要贡献: 这篇文章的主要贡献在于 “Fast Encoder”, 也就是将点云稠密的Pillar(上文提到2847个)**输入给网络,**这才是这篇论文的精髓所在, 这大大提升了识别速度, 论文写可以达到62Hz.仅使用2D卷积层进行端到端学习PointPillars uses a n…

攻防世界题目练习——Web难度1(二)

题目目录 1. fileinclude2. fileclude3. easyphp4. file_include5. unseping 1. fileinclude 一道文件包含题,对文件包含还是不太懂,直接看flag.php文件什么都没有。 根据WriteUp提示先f12查看源码,发现了文件包含的漏洞点lan.php文件&#…

【LeetCode75】第五十题 无限集中的最小数字

目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 这是我们在LeetCode75里遇到的第二道设计类题目,难度比上一次的设计题目要难上一些。 题目假设我们拥有一个从1开始的无限集…

Vivado XADC IP核 使用详解

本文介绍Vivado中XADC Wizard V3.3的使用方法。 XADC简介 XADC Wizard Basic Interface Options: 一共三种,分别是AXI4Lite、DRP、None。勾选后可在界面左侧看到相应通信接口情况。Startup Channel Selection Simultaneous Selection:同时监…

小米将推出中端手机,高通骁龙7系列再添一员,能否吸引消费者?

小米将于近日推出一款新的中端智能手机,该手机将搭载全新的处理器——骁龙SM7550。这个代号为“Crow”的处理器引起了广泛的关注和猜想,因为它是高通骁龙7系列的一员,但性能可能低于7 Gen 2,那么它的亮点和面向用户群体是什么呢&a…

字符串讲解

文章目录 字符串一.String概述二.创建String对象的两种方式三.Java的内存模型四.字符串的比较五.StringBuilder的基本操做六.StringJoiner概述七.字符串相关类的底层原理 字符串 一.String概述 1.String是Java定义好的一个类,定义在java.long包中,所以使用的时候不需要导包 …

群晖 NAS WebDAV服务手机ES文件浏览器远程访问【无公网IP内网穿透】

📱 iOS开发上架主页 在强者的眼中,没有最好,只有更好。我们是移动开发领域的优质创作者,同时也是阿里云专家博主。 ✨ 关注我们的主页,探索iOS开发的无限可能! 🔥我们与您分享最新的技术洞察和实…

解除百度安全验证

使用chrome浏览器用百度浏览时,一直弹百度安全验证: 在设置里进行重置: 然后重启浏览器就可以了。

算法通过村第六关-树青铜笔记|中序后序

文章目录 前言1. 树的常见概念2. 树的性质3. 树的定义与存储方式4. 树的遍历方式5. 通过序列构建二叉树5.1 前中序列恢复二叉树5.2 中后序列恢复二叉树 总结 前言 提示:瑞秋是个小甜心,她只喜欢被爱,不懂的去爱人。 --几米《你们 我们 他们》…

uni-app 之 表格设置

uni-app 之 表格设置 image.png <view style"padding: 3%; border: #1296db;"><table style"width: 100%; border-collapse: collapse; "><tr style"height: 50px;border: 2px solid;border-color: #F7F7F7;"><td style&qu…

企业架构LNMP学习笔记26

通过Keepalived可以实现主服务器整机不可用&#xff0c;实现VIP的切换。保证用户可以通过VIP进行访问服务。但是实际上&#xff0c;往往不是服务器整机不可用&#xff0c;只是对应的服务或者软件不可用。 比如说&#xff0c;nginx提供的web的服务&#xff0c;nginx进程关闭。 …

什么是JVM常用调优策略?分别有哪些?

目录 一、JVM调优 二、堆内存大小调整 三、垃圾回收器调优 四、线程池调优 一、JVM调优 Java虚拟机&#xff08;JVM&#xff09;的调优主要是为了提高应用程序的性能&#xff0c;包括提高应用程序的响应速度和吞吐量。以下是一些常用的JVM调优策略&#xff1a; 堆内存大小…

【系统设计系列】数据库

系统设计系列初衷 System Design Primer&#xff1a; 英文文档 GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. 中文版&#xff1a; https://github.com/donnemarti…

8路编码器脉冲计数器或16路DI高速计数器,Modbus RTU模块 YL69-485 可识别正反转

特点&#xff1a; ● 编码器解码转换成标准Modbus RTU协议 ● 可用作编码器计数器或者转速测量 ● 支持8个编码器同时计数&#xff0c;可识别正反转 ● 也可以设置作为16路独立DI高速计数器 ● 编码器计数值支持断电自动保存 ● DI输入和电源之间3000V隔离 ● 通过RS-4…

奇舞周刊第 505 期:实践指南-前端性能提升 270%!

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 实践指南-前端性能提升 270% 当我们疲于开发一个接一个的需求时&#xff0c;很容易忘记去关注网站的性能&#xff0c;到了某一个节点&#xff0c;猛地发现&#xff0c;随着越来越多…

为什么Python在数据分析行业备受欢迎?优势在哪?

在今天的数据驱动世界中&#xff0c;数据分析已经成为各行业的核心工作之一。数据分析的应用场景非常广泛&#xff0c;例如金融、医疗、教育、电商、社交等领域。数据分析师是一种专门从事数据分析的职业&#xff0c;他们需要掌握一定的编程技能和统计知识&#xff0c;以及对数…

JavaScript实现广告倒计时和跳过广告

倒计时和跳过广告 最近打开手机上的app&#xff0c;映入眼帘的都是一个几秒的广告&#xff0c;带有倒计时&#xff0c;当然如果不喜欢的话可以点击跳过&#xff0c;跳过广告其实质应该就是关闭广告。以前用JavaScript做过一个定时关闭的广告&#xff0c;于是把代码完善了一下&…

作为数据分析师,如何能把AI工具和数据分析工作更好的结合?

​ 做为数据分析师&#xff0c;如果能够学会把AI工具应用到实际的数据分析工作当中&#xff0c;可以把一些重复性很强的工作交给AI来完成&#xff0c;这样数据分析师在提升效率的过程中能够去有更多的时间考虑具有创意的工作。 数据分析师&#xff0c;在使用AI工具完成数据分…

Android相机-架构3

目录 引言 1. Android相机的整体架构 2. 相机 HAL 2.1 AIDL相机HAL 2.2 相机 HAL3 功能 3. HAL子系统 3.1 请求 3.2 HAL和相机子系统 3.2.1 相机的管道 3.2.2 使用 Android Camera API 的步骤 3.2.3 HAL 操作摘要 3.3 启动和预期操作顺序 3.3.1 枚举、打开相机设备…

Java(二)数据类型与变量以及运算符

数据类型与变量以及运算符 二、数据类型与变量&#xff08;重要&#xff09;2.数据类型2.1 基本数据类型 1.变量与常量1.1常量&#xff08;字面常量&#xff09;1.2变量数据类型小总结 三、运算符1.运算符号&#xff08;重要&#xff09;1.1计算运算符1.2增量运算符1.3 自增或自…