当前位置: 首页 > news >正文

CH03_代码的坏味道(下)

循环语句(Loops)

从最早的编程语言开始,循环就一直是程序设计的核心要素。如今,函数作为一等公民已经得到了广泛的支持,因此我们可以使用以管道取代循环(231)管道操作(如filter和map)可以帮助我们更快地看清被处理的元素以及处理它们的动作。

冗赘的元素(Lazy Element)

程序员定义这样一个类,根本就是一个简单的函数;可能考虑到以后这个类会变复杂、但很长时间这个类都没有变化。或者存在一个类,但随着重构的进行越变越小,最后只剩了一个函数。不论上述哪一种原因,请让这样的程序元素庄严赴义吧。通常你只需要使用内联函数(115)或是内联类(186)。如果这个类处于一个继承体系中,可以使用折叠继承体系(380)。

夸夸其谈通用性(Speculative Generality)

考虑到总有一天需要做这事,并企图以各式各样的钩子和特殊情况来处理一些非必要的事情。这么做的结果往往造成系统更难理解和维护。如果所有装置都会被用到,就值得那么做;如果用不到,就不值得。

如果你的某个抽象类其实没有太大作用,请运用折叠继承体系(380)。不必要的委托可运用内联函数(115)和内联类(186)除掉。如果函数的某些参数未被用上,可以用改变函数声明(124)去掉这些参数。如果有并非真正需要、只是为不知远在何处的将来而塞进去的参数,也应该用改变函数声明(124)去掉。

临时字段(Temporary Field)

有时会看到这样的类:其内部某个字段仅为某种特定情况而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有字段。在字段未被使用的情况下猜测当初设置它的目的,会让人发疯。

使用提炼类(182)给这些变量创建一个新类,然后用搬移函数(198)把所有和这些字段相关的代码都放进这个新类。也许你还可以使用引入特例(289)在“变量不合法”的情况下创建一个替代对象,从而避免写出条件式代码。

过长的消息链(Message Chains)

如果看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象……这就是消息链。在实际代码中看到的可能是一长串取值函数或一长串临时变量。采取这种方式,意味客户端代码将与查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。

这时候应该使用隐藏委托关系(189)。可以重构消息链上的所有对象,但这么做就会把所有中间对象都变成“中间人”。通常更好的选择是:先观察消息链最终得到的对象是用来干什么的,看看能否以提炼函数(106)把使用该对象的代码提炼到一个独立的函数中,再运用搬移函数(198)把这个函数推入消息链。如果还有许多客户端代码需要访问链上的其他对象,同样添加一个函数来完成此事。

中间人(Middle Man)

对象的基本特征之一就是封装——对外部世界隐藏其内部细节。封装往往伴随着委托。

如果过度使用委托,这时应该使用移除中间人(192),直接和真正负责的对象打交道。如果这样“不干实事”的函数只有少数几个,可以运用内联函数(115)把它们放进调用端。如果这些中间人还有其他行为,可以运用以委托取代超类(399)或者以委托取代子类(381)把它变成真正的对象,这样你既可以扩展原对象的行为,又不必负担那么多的委托动作。

内幕交易(Insider Trading)

软件开发者喜欢在模块之间建起高墙,极其反感在模块之间大量交换数据,因为这会增加模块间的耦合。在实际情况里,一定的数据交换不可避免,但我们必须尽量减少这种情况

如果两个模块数据交换频繁,就应该用搬移函数(198)和搬移字段(207)减少它们的交流。如果两个模块有共同的数据,可以尝试再新建一个模块,把这些共用的数据放在一个管理良好的地方;或者用隐藏委托关系(189),把另一个模块变成两者的中介。

继承常会造成密谋,因为子类对超类的了解总是超过后者的主观愿望。可以运用以委托取代子类(381)或以委托取代超类(399)让它离开继承体系。

过大的类(Large Class)

如果想利用单个类做太多事情,其内往往就会出现太多字段。一旦如此,重复代码也就接踵而至了。

可以运用提炼类(182)将几个变量一起提炼至新类内。提炼时应该选择类内彼此相关的变量,将它们放在一起。如果类内的数个变量有着相同的前缀或后缀,这就意味着有机会把它们提炼到某个组件内。如果这个组件适合作为一个子类,你会发现提炼超类(375)或者以子类取代类型码(362)(其实就是提炼子类)往往比较简单。

类内如果有太多代码,也是代码重复、混乱并最终走向死亡的源头。最简单的解决方案是把多余的东西消弭于类内部。

观察一个大类的使用者,经常能找到如何拆分类的线索。看看使用者是否只用到了这个类所有功能的一个子集,每个这样的子集都可能拆分成一个独立的类。一旦识别出一个合适的功能子集,就试用提炼类(182)、提炼超类(375)或是以子类取代类型码(362)将其拆分出来。

异曲同工的类(Alternative Classes with Different Interfaces)

使用类的好处之一就在于可以替换:今天用这个类,未来可以换成用另一个类。但只有当两个类的接口一致时,才能做这种替换。可以用改变函数声明(124)将函数签名变得一致。但这往往还不够,请反复运用搬移函数(198)将某些行为移入类中,直到两者的协议一致为止。如果搬移过程造成了重复代码,或许可运用提炼超类(375)补偿一下。

纯数据类(Data Class)

所谓纯数据类是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。这样的类只是一种数据容器,它们几乎一定被其他类过分细琐地操控着。这些类早期可能拥有public字段,需要运用封装记录(162)将它们封装起来。对于那些不该被其他类修改的字段,运用移除设值函数(331)。然后,找出这些取值/设值函数被其他类调用的地点。尝试以搬移函数(198)把那些调用行为搬移到纯数据类里来。如果无法搬移整个函数,就运用提炼函数(106)产生一个可被搬移的函数。

纯数据类常常意味着行为被放在了错误的地方。也就是说,只要把处理数据的行为从客户端搬移到纯数据类里来,就能使情况大为改观。但也有例外情况,一个最好的例外情况就是,纯数据记录对象被用作函数调用的返回结果。这种结果数据对象有一个关键的特征:它是不可修改的,不可修改的字段无须封装,使用者可以直接通过字段取得数据,无须通过取值函数。

被拒绝的馈赠(Refused Bequest)

如果子类复用了超类的行为(实现),却又不愿意支持超类的接口,被拒绝的遗赠”的坏味道就会变得很浓烈。

既然不愿意支持超类的接口,就不要使用继承体系,应该运用以委托取代子类(381)或者以委托取代超类(399)彻底划清界限。

注释(Comments)

一段代码有着长长的注释,然后发现,这些注释之所以存在乃是因为代码很糟糕。这种情况的发生次数之多,实在令人吃惊。

如果你需要注释来解释一块代码做了什么,试试提炼函数(106);如果函数已经提炼出来,但还是需要注释来解释其行为,试试用改变函数声明(124)为它改名;如果你需要注释说明某些系统的需求规格,试试引入断言(302)。

相关文章:

CH03_代码的坏味道(下)

循环语句(Loops) 从最早的编程语言开始,循环就一直是程序设计的核心要素。如今,函数作为一等公民已经得到了广泛的支持,因此我们可以使用以管道取代循环(231)管道操作(如filter和ma…...

journal日志导致服务器磁盘满

背景 ubuntu 18.04服务器磁盘突然100% 一查/var/log/journal目录占了14G 清理 要清理 journal 日志,可以使用以下步骤: 运行以下命令来查看 journal 日志的使用情况: journalctl --disk-usage这将显示 journal 日志的当前使用情况&#x…...

“Go程序员面试笔试宝典”复习便签

一.逃逸分析 1.1逃逸分析是什么? 逃逸分析,主要是Go编译器用来决定变量分配在堆或者栈的手段。 区分于C/C手动管理内存分配,Go将这些工作交给了编译器。 1.2逃逸分析有什么作用 解放程序员。程序员不需要手动指定指针分配内存。 灵活的…...

数组的度(指数组里任一元素出现频数的最大值)

题目: 给定一个非空且只包含非负数的整数数组 nums,数组的 度 的定义是指数组里任一元素出现频数的最大值。 你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。 示例 1: 输入:nums …...

scala array类型参数

在Scala中,数组(Array)是一种用于存储相同类型元素的数据结构。数组可以用于保存基本数据类型和自定义数据类型的元素。当定义数组类型参数时,您通常是在函数、类或方法签名中使用它们。以下是一些有关Scala数组类型参数的示例&am…...

构建 NodeJS 影院预订微服务并使用 docker 部署(03/4)

一、说明 构建一个微服务的电影网站,需要Docker、NodeJS、MongoDB,这样的案例您见过吗?如果对此有兴趣,您就继续往下看吧。 你好社区,这是🏰“构建 NodeJS 影院微服务”系列的第三篇文章。本系列文章演示了…...

html写一个向flask_socketio发送消息和接收消息并显示在页面上

以下是一个简单的HTML页面&#xff0c;它包含一个输入框、一个发送按钮和一个显示区域。用户可以在输入框中输入消息&#xff0c;点击发送按钮&#xff0c;然后这个消息会被发送到 Flask-SocketIO 服务器。当服务器回应消息时&#xff0c;它会在页面的显示区域显示出来。 <…...

C#使用.Net Core进行跨平台开发

使用 .NET Core 进行跨平台开发是一种灵活的方法&#xff0c;可以在多个操作系统上运行 C# 应用程序。以下是在 C# 中使用 .NET Core 进行跨平台开发的一般步骤&#xff1a; 安装 .NET Core SDK&#xff1a; 在开始之前&#xff0c;需要安装适用于操作系统的 .NET Core SDK。可…...

Java“牵手”天猫店铺所有商品API接口数据,通过店铺ID获取整店商品详情数据,天猫API申请指南

天猫商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。天猫商品详情可以帮助消费者更好的了解宝贝信息&#xff0c;从而做出购买决策。同时&#xff0c;消费者也可以通过商品详情了解其他买家对宝贝的评价&#xf…...

php输入post过滤函数,入库出库,显示

第一部分 php输入post过滤函数 function GLOBAL_POST($str) {$str_origin$str; if (empty($str)) return false;$str str_replace( /, "", $str);//替换关键词 $str str_replace("\\", "", $str); $str str_replace("&gt", &…...

matlab-对数据集加噪声并实现tsne可视化

matlab-对数据集加噪声并实现tsne可视化 最近才知道&#xff0c;原来可以不用模型&#xff0c;也能实现对数据集数据的可视化。 **一、**以COIL-100数据集为例子。 问题&#xff1a; 前提&#xff1a;首先对COIL-100数据集根据角度0-175和180-255&#xff0c;分别划分成C1,C…...

【BASH】回顾与知识点梳理(三十八)

【BASH】回顾与知识点梳理 三十八 三十八. 源码概念及简单编译38.1 开放源码的软件安装与升级简介什么是开放源码、编译程序与可执行文件什么是函式库什么是 make 与 configure什么是 Tarball 的软件如何安装与升级软件 38.2 使用传统程序语言进行编译的简单范例单一程序&#…...

Sql注入攻击的三种方式

SQL注入是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。SQL 注…...

dockerfile部署前端vue打包的dist文件实战

背景&#xff1a;一般前端开发后会将打包后的dist文件交由我们部署&#xff0c;部署的方式有很多&#xff0c;这里提供一种思路 在服务器的路径下新建一个目录&#xff0c;在目录中新建Dockerfile&#xff0c;编辑这个文件 FROM nginxCOPY ./dist /home/front COPY nginx.con…...

[技术杂谈]MobaXterm中文乱码编码问题一种解决方法

今日使用mobaxterm连接树莓派发现安装出现乱码&#xff0c;看不清文字是什么。最最简单方式是ssh设置终端字体&#xff0c;具体步骤为&#xff1a; 1. 右键会话&#xff0c;点击编辑会话 2.在以下画面点击终端字体设置 3.选择编码&#xff1a;GBK或者ISO-8859-1...

mac os M1 安装并启动 postgreSQL 的问题

Homebrew 安装 postgreSQL brew install postgresql启动 brew services start postgresql但报错&#xff1a; uninitialized constant Homebrew::Service::System解决方案 brew doctor按照 brew doctor 中的建议进行操作&#xff0c;如果不行&#xff0c;如下&#xff1a; h…...

如何使用Wireshark进行网络流量分析?

如何使用Wireshark进行网络流量分析。Wireshark是一款强大的网络协议分析工具&#xff0c;可以帮助我们深入了解网络通信和数据流动。 1. 什么是Wireshark&#xff1f; Wireshark是一个开源的网络协议分析工具&#xff0c;它可以捕获并分析网络数据包&#xff0c;帮助用户深入…...

抖音web主页视频爬虫

需要抖音主页视频爬虫源码的发私信&#xff0c;小偿即可获得长期有效的采集程序。 比构造 s_v_web_id 验证滑块的方法更快&#xff0c;更稳定。...

常用的jar包【maven坐标格式】

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f3e8;mysql加载启动项jar包&#x1f993;mybatis加载启动项jar包 &#x1f412;个人主页 &#x1f3c5;JavaEE系列专栏 &#x1f4d6;前言&#xff1a; 本篇博客主要以介绍常…...

【分布式】VMware FT概要

讨论了1primary 1backup的情况&#xff0c;比较好理解&#xff0c; 6.824中以该论文为例&#xff0c;介绍了分布式系统中复制的概念&#xff0c;复制的方式。以下简要讲述一些关键点&#xff0c;来源于MIT6.824课程&#xff0c;翻译版本 https://mit-public-courses-cn-transl…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...