重构改善既有代码的设计-学习(三):重新组织数据

1、拆分变量(Split Variable)

        有些变量用于保存一段冗长代码的运算结果,以便稍后使用。这种变量应该只被赋值一次。

        如果它们被赋值超过一次,就意味它们在函数中承担了一个以上的责任。如果变量承担多个责任,它就应该被替换(分解)为多个变量,每个变量只承担一个责任。同一个变量承担两件不同的事情,会令代码阅读者糊涂。

        有两种情况除外:

  • 循环变量(loop variable)会随循环的每次运行而改变(例如for(let i=0; i<10; i++)语句中的
    i)
  • 结果收集变量(collecting variable)负责将“通过整个函数的运算”而构成的某个值收集起来。

2、字段改名(Rename Field)  

        将字段改为易于理解的名字,不要出现类似 int a等。

 3、以查询取代派生变量(Replace Derived Variable with Query)

        可变数据是软件中最大的错误源头之一。对数据的修改常常导致代码的各个部分以丑陋的形式互相耦合:在一处修改数据,却在另一处造成难以发现的破坏。很多时候,完全去掉可变数据并不现实,但我还是强烈建议:尽量把可变数据的作用域限制在最小范围。 

        例如:

get discountedTotal() {return this._discountedTotal;}
set discount(aNumber) {const old = this._discount;this._discount = aNumber;this._discountedTotal += old - aNumber;
}

        改为:

get discountedTotal() {return this._baseTotal - this._discount;}
set discount(aNumber) {this._discount = aNumber;}

4、将引用对象改为值对象(Change Reference to Value)

        在把一个对象(或数据结构)嵌入另一个对象时,位于内部的这个对象可以被视为引用对象,也可以被视为值对象。两者最明显的差异在于如何更新内部对象的属性:如果将内部对象视为引用对象,在更新其属性时,我会保留原对象不动,更新内部对象的属性;如果将其视为值对象,我就会替换整个内部对象,新换上的对象会有我想要的属性值。

        如果把一个字段视为值对象,我可以把内部对象的类也变成值对象[mf-vo]。值对象通常更容易理解,主要因为它们是不可变的。一般说来,不可变的数据结构处理起来更容易。我可以放心地把不可变的数据值传给程序的其他部分,而不必担心对象中包装的数据被偷偷修改。我可以在程序各处复制值对象,而不必操心维护内存链接。值对象在分布式系统和并发系统中尤为有用。

        何时不应该使用本重构手法?如果我想在几个对象之间共享一个对象,以便几个对象都能看见对共享对象的修改,那么这个共享的对象就应该是引用。这便是5中的情况了。

        4的例子:

class Product {applyDiscount(arg) {this._price.amount -= arg;}
}

        改为:

class Product {applyDiscount(arg) {this._price = new Money(this._price.amount - arg, this._price.currency);}
}

5、将值对象改为引用对象(Change Value to Reference) 

        一个数据结构中可能包含多个记录,而这些记录都关联到同一个逻辑数据结构。例如,我可能会读取一系列订单数据,其中有多条订单属于同一个顾客。遇到这样的共享关系时,既可以把顾客信息作为值对象来看待,也可以将其视为引用对象。如果将其视为值对象,那么每份订单数据中都会复制顾客的数据;而如果将其视为引用对象,对于一个顾客,就只有一份数据结构,会有多个订单与之关联。

        如果顾客数据永远不修改,那么两种处理方式都合理。把同一份数据复制多次可能会造成一点困扰,但这种情况也很常见,不会造成太大问题。过多的数据复制有可能会造成内存占用的问题,但就跟所有性能问题一样,这种情况并不常见。

        如果共享的数据需要更新,将其复制多份的做法就会遇到巨大的困难。此时我必须找到所有的副本,更新所有对象。只要漏掉一个副本没有更新,就会遭遇麻烦的数据不一致。这种情况下,可以考虑将多份数据副本变成单一的引用,这样对顾客数据的修改就会立即反映在该顾客的所有订单中。 

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

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

相关文章

mac 修改flutter sdk配置

问题描述&#xff1a;我mac电脑上有高低2个版本的flutter sdk&#xff0c;我需要低版本sdk的项目在setting里设置了sdk版本&#xff0c;可是命令行还是提示我版本过高。 直接上解决办法&#xff1a; 打开mac终端&#xff0c;输入open -e .bash_profile&#xff0c;然后修改下…

【Java】SpringMVC参数接收(二):JSON、URI、文件

1、获取JSON参数 RequestMapping("/hello") RestController public class HelloSpring {RequestMapping("/t10")public String t10(RequestBody UserInfo userInfo){return userInfo.toString();} } 2、获取URI中的参数 &#xff08;1&#xff09;获取单…

手机耗电快怎么办?最全解析分享!

在现代社会&#xff0c;手机已经成为我们日常生活的重要伙伴&#xff0c;承载着通讯、娱乐、工作等多种功能。然而&#xff0c;随着手机功能的不断升级和使用频率的增加&#xff0c;许多人都曾经面临过一个普遍的问题&#xff1a;手机电池消耗过快。这不仅令人感到困扰&#xf…

B2B企业增长合集:10个案例4大策略深度解析

本文5300余字&#xff0c;静心阅读需要15分钟&#xff0c;建议收藏后再阅读。 现代管理学大师彼得德鲁克曾说&#xff1a;“不增长&#xff0c;一切都是成本。” 现实中的增长乏力和生存压力&#xff0c;迫使众多企业不得不寻求新的增长模式和增长渠道。 然而&#xff0c;增…

体育赛事编排管理系统设计与实现 -计算机毕业设计源码59094

目 录 摘要 1 绪论 1.1研究背景与意义 1.2研究内容 1.3springboot框架介绍 1.4论文结构与章节安排 2 体育赛事编排管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非…

基本语法和 package 与 jar

3.基本语法 1.输入输出 // 导入 java.util 包中的 Scanner 类 import java.util.Scanner;// 定义名为 ScannerExample 的公共类 public class ScannerExample {// 主方法&#xff0c;程序的入口点public static void main(String[] args) {// 创建 Scanner 对象&#xff0c;用…

VMware虚拟机安装优麒麟(ubuntukylin)操作系统

1.镜像下载 官网:https://www.ubuntukylin.com/ 优麒麟官网提供的宣传视频:https://www.ubuntukylin.com/upload/video/202204/1650594049260581.mp4官网提供的视频后续随着版本的更新,此视频可能失效,去官网查看最新的即可,这不是重点 1.1搜索出优麒麟官网,下载镜像 下载…

Flutter中实现中国省份地图

效果展示(这里只展示局部&#xff0c;完全展示违规)&#xff1a; 可以点击省份改变颜色&#xff0c;更多功能可以自行拓展。 注&#xff1a;非完整中国地图&#xff01;&#xff01;&#xff01; 本文用于记录在Flutter项目中安卓端实现中国地图&#xff0c;因为实现过程是通过…

微软 AD 介绍 | 安全建议 | 防护

介绍&#xff1a; 什么是Active Directory&#xff08;AD&#xff09;&#xff1f; Active Directory 是由 微软开发的目录服务&#xff0c;用于存储和管理网络中的资源&#xff0c;如计算机、用户、组和其他网络对象。允许组织管理员轻松地管理和验证网络中的用户和计算机。 …

【C++】stack、queue的使用及模拟实现

目录 一、stack1.1 stack的使用1.2 stack的模拟实现 二、queue2.1 queue的使用2.2 queue的模拟实现 一、stack 1.1 stack的使用 stack是一种容器适配器&#xff0c;它的特点是后进先出&#xff0c;只能在容器的一端进行插入和删除操作。 stack的使用很简单&#xff0c;主要有…

卫星影像离线瓦片如何调用?

我们曾为你分享了按区县购买卫星影像并在线调用的方法。 于是就有朋友问&#xff0c;卫星影像瓦片可以离线调用吗&#xff1f; 当然可以&#xff0c;这里就来分享一下卫星影像瓦片离线调用的方法。 卫星影像离线瓦片如何调用&#xff1f; 这里以OpenLayers、Mapbox和Cesiu…

安卓移动设备使用DS file文件管理工具远程访问本地群晖NAS文件

文章目录 1. 群晖安装Cpolar2. 创建TCP公网地址3. 远程访问群晖文件4. 固定TCP公网地址5. 固定TCP地址连接6. 结语 DS file 是一个由群晖公司开发的文件管理应用程序&#xff0c;主要用于浏览、访问和管理存储在群晖NAS&#xff08;网络附加存储&#xff09;中的文件。这个应用…

搭建nodejs服务器

简单搭建nodejs服务器&#xff0c;用于爬虫js逆向. 1、安装镜像源 下载nrm npm install -g nrm 设置下载源&#xff1a;&#xff08;最好使用npm源或者淘宝源&#xff09; 例子&#xff1a;npm config set registry http://registry.npmjs.org 查看是否设置成功&#xff1a…

Mac 也能玩文明6!下载安装详细教程

最近朋友给我分享了一个 Mac 玩文明6的方法&#xff0c;丝毫不卡顿&#xff0c;非常流畅&#xff0c;分享给大家 文明6是最新的文明系列游戏&#xff0c;和以往的文明游戏一样&#xff0c;玩家将从石器时代创建文明&#xff0c;然后迈向信息时代&#xff0c;最终通过军事、经济…

考研C语言刷题基础篇之数组(一)

目录 第一题&#xff1a;用数组作为参数实现冒泡排序 不用函数的冒泡排序 冒泡排序原理&#xff1a; 错误的数值传参冒泡排序 错误的原因 就是什么是数组名 正确的数组传参的冒泡排序 数组的地址和数组首元素的地址的区别 第一题&#xff1a;用数组作为参数实现冒泡排…

Docker网络配置与自定义IP容器通信

目录 前言 一、docker网络配置 1. bridge 虚拟网桥 2. host 网络模式 3. none 网络模式 4. 自定义container网络模式 二、自定义IP容器通信 1. 自定义IP 2. 创建所需容器&#xff08;mysql&#xff0c;tomcat&#xff09; 3. 准备项目资源 4. 构建Nginx实现负载均衡…

this指向问题以及修改函数的this指向方法

1、什么是this this表示对象 取决于函数调用&#xff08;this表示对象>当前对象>当前环境对象>函数运行时环境对象&#xff09; this就是函数运行时所在的环境对象&#xff08;取决于函数调用>不同场合&#xff0c;this有不同的值&#xff09; 函数的不同使用场…

PyTorch深度学习实战(32)——DCGAN详解与实现

PyTorch深度学习实战&#xff08;32&#xff09;——DCGAN详解与实现 0. 前言1. 模型与数据集分析1.1 模型分析1.2 数据集介绍 2. 构建 DCGAN 生成人脸图像小结系列链接 0. 前言 DCGAN (Deep Convolutional Generative Adversarial Networks) 是基于生成对抗网络 (Convolution…

PWN入门Protostar靶场Stack系列

Protostar靶场地址 https://exploit.education/protostar/溢出 源码分析 #include <stdlib.h> #include <unistd.h> #include <stdio.h>int main(int argc, char **argv) {volatile int modified; //定义一个变量char buffer[64]; //给…

Git 入门精讲

我们为什么要学习git&#xff1f; 就当下的发展而言&#xff0c;只要你从事开发就一定会接触git。作为最强大的分布式版本控制器&#xff0c;git 与 svn 有着本质上的区别。 Git是一种分布式版本控制系统&#xff0c;每个开发者都可以在本地维护完整的代码库&#xff0c;可以离…