代码整洁之道【2】--函数

关于函数部分的总结

一、函数只做一件事

函数应该只做一件事、做好这件事、只做这件事。
判断函数是否不止做了一件事,还有一个方法,就是看是否能再拆出一个函数,该函数不仅只是单纯地重新

二、函数尽量不要太长

按照作者的理论,函数长度20行封顶为佳。
我的理解是,函数长度需要跟节的函数只做一件事结合起来,并不需要完全拘泥于20行的限制,只要函数在逻辑层次上不可再分解成新的函数(参见第三条),就可以。

三、每个函数一个抽象层次

要确保函数只做一件事,函数中的语句都要在同一抽象层级上。

我们想要让代码拥有自顶向下的阅读顺序。我们想要让每个函数后面都跟着位于下一抽象层级的函数,这样一来,在查看函数列表时,就能循抽象层级向下阅读了。我把这叫做向下规则。

这里咱举个例子,加入你要对一个字符串进行一些处理(例如,append,substring等),然后再对处理后的字符串进行校验。
良好的实践应该类似如下:

String handledStr = handleStr(username);
validate(handledStr);

不是很优雅的实践类似如下:

String trimmedUserName = username.trim();
String handledStr = trimmedUserName.append("something");
validate(handledStr);

为什么说它不是佳实践呢?因为前两行代码是关于对字符串进行处理的具体操作,应该把它们抽象成一个函数,这个抽象出来的函数是这两个具体操作的上一层概念,和validate方法同一层。

四、switch语句

写出精简的switch语句很难,写出只做一件事的switch语句也很难,它天生就要做N件事。我们无法避开switch语句,不过还是可以确保每个switch都放在较低的抽象层级,而且永远不重复。

利用多态实现switch的优化:

假设有下面的需求:根据雇员类型计算薪资。

public Money calculatePay(Employee e) throws InvalidEmployeeType{switch (e.type){case COMMISSIONED:return calculateCommissionedPay(e);case HOURLY:return calculateHourlyPay(e);case SALARIED:return calculateSalariedPay(e);default:throw new InvalidEmployeeType(e.type);}
}

这里仅仅依赖了雇员类型一种操作,就有好几个问题:

  1. 函数太长,当有新的雇员类型,还会更长。
  2. 违反开闭原则(OCP原则),每添加新类型,就必须修改它。
  3. 违反了单一权责原则,它做了多件事情。

更麻烦的是:到处都有类似的调用函数(传入的参数类似)。
比如可能多处调用
isPayday(Employee e, Date date);

deliverPay(Employee e, Money pay);

下面我们针对这样的问题进行优化。

对每个类都会有同样的操作,比如isPayday(), deliverPay()等,不如把类的行为抽象出来到一个抽象类Employee中。在抽象工厂中使用switch语句为Employee的派生物创建适当的实体。

对于switch语句,我们的规矩是如果只出现一次,用于创建多态对象,而且隐藏在某个继承关系中,在其他系统看不到,就还能容忍。

public abstract class Employee{public abstract boolean isPayday();public abstract Money calculatePay();public abstract void deliverPay(Money pay);
}

public interface EmployeeFactory{public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}

public class EmployeeFactoryImpl implements EmployeeFactory{public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{switch (r.type){case COMMISSIONED:return new CommissionedEmployee(r);case HOURLY:return new HourlyEmployee(r);case SALARIED:return new SalariedEmployee(r);default:throw new InvalidEmployeeType(r.type);}}
}

用优化后的代码,再有新的类型加入时,业务程序是不用修改的,因为类型已隐藏在了抽象类中,返回给业务的都是抽象的Employee,无需考虑类型的变化,只是调用抽象类的方法即可。我们需要改动的只是再创建一个抽象类的实体类,在EmployeeFactoryImpl中多加一个switch分支。

五、使用描述性的名称

长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。使用某种命名约定,让函数名称中的多个单词容易阅读,然后使用这些单词给函数取个能说清其功用的名称。

六、函数参数

参数数量越少越好。 尽量不要有输出参数,而是将输出设置为返回值。 如果参数较多的时候可以考虑使用类进行封装。

七、无副作用

副作用是一种谎言。函数承诺只做一件事,但还是会做其他被藏起来的事。有时,它会对自己类中的变量做出没有预料到的改动。有时,它会把变量搞成向函数传递的参数或是系统全局变量。无论哪种情况,都是具有破坏性的,会导致古怪的时序性耦合及顺序依赖。

例如如下代码,改函数使用标准算法来匹配userName和passWord,如果匹配成功,返回true,如果匹配失败,返回false,但是它会有副作用:

副作用就在于对Session.initialize()调用。checkPassword函数,顾名思义,就是用来检查密码的。该名称并没有暗示它会初始化该次会话。所以,当某个误信了函数名的调用者想要检查用户有效性时,就会冒着抹除现有会话数据的风险。也就是说,这个副作用造成了一次时序性耦合。

八、分隔指令与询问

这实际上内生的包含于一个函数只做一件事的要求中,但是还是有必要单独指出。函数要么做什么事,要么回答什么事,但二者不可兼得。

函数应该修改某对象的状态,或者返回该对象的有关信息。两样都干会导致混乱。

举个例子:

if (set("username", "unclebob"))...

上面这个语句会让人迷惑:它是询问username属性之前是否已经被设置为unclebob了?还是在问username属性是否成功被设置为unclebob呢?从这行调用很难判断其含义。

要解决这个问题,可以按如下方式改造,防止混淆的发生:

if (attributeExists("username")) {setAttribute("username", "unclebob");
}

九、使用异常替代返回错误码

可减少过度嵌套(判断多种错误码及内层错误码)。
可减少对错误码枚举类的过度依赖(当修改了错误码枚举类时,所有依赖这个枚举类的其他类都得重新编译和部署)。

十、别重复自己

如果你发现某两个函数用到了相同甚至相近的代码块应该迅速思考是不是可以将其抽取成单独的函数。 重复就是万恶之源。

十一、结构化函数

Dijkstra认为,每个函数、每个代码块都应该只有一个入口一个出口。这意味着每个函数只能有一个return语句,循环中不能有break或continue,而且永远不能出现goto。 事实上,当代码相对较短的时候,适当多几个return、break、continue无伤大雅。当代码冗长时,这样的规则才能够发挥出其效力来。

十二、如何写出好的函数?

写代码和写别的东西很像,在写文章或者论文时,你先想些什么就写什么,然后再打磨它。 初稿也许粗陋无序,你就斟酌推敲,直到达到你心中的样子。

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

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

相关文章

【算法】双指针算法

个人主页 : zxctscl 如有转载请先通知 题目 1. 283. 移动零1.1 分析1.2 代码 2. 1089. 复写零2.1 分析2.2 代码 3. 202. 快乐数3.1 分析3.2 代码 4. 11. 盛最多水的容器4.1 分析4.2 代码 5. LCR 179. 查找总价格为目标值的两个商品5.1 分析5.2 代码 6. 15. 三数之和…

大语言模型开源数据集

本文目标:汇聚目前大语言模型预训练、微调、RM/RL、评测等全流程所需的常见数据集,方便大家使用,本文持续更新。文章篇幅较长,建议收藏后使用。 一、按语料类型分类 1、维基百科类 No.1 Identifying Machine-Paraphrased Plagia…

企业微信认证后可以修改主体吗?

企业微信变更主体有什么作用?如果原有的公司注销了,或者要更换一家公司主体来运营企业微信,那么就可以进行变更主体,变更主体后才可以保留原来企业微信上的所有用户,否则就只能重新申请重新积累用户了。企业微信变更主…

【资源分享】MAC上最好用的截图软件-Snipaste

::: block-1 “时问桫椤“是一个关注本科生到研究生教育阶段的不严肃的公众号,希望能在大家迷茫、难受、困难之时帮助到大家。用广大研究生的经验总结,让大家能尽早的适应研究生生活,尽快的看透科研本质。祝好!!&#…

vue通过echarts实现数据可视化

1、安装echarts cnpm install echarts -Sechart官方图表示例大全&#xff1a;https://echarts.apache.org/examples/zh/index.html#chart-type-line 2、代码实现 <template><div><div class"box" ref"zhu"></div><div class&…

注解式 WebSocket - 构建 群聊、单聊 系统

目录 前言 注解式 WebSocket 构建聊天系统 群聊系统&#xff08;基本框架&#xff09; 群聊系统&#xff08;添加昵称&#xff09; 单聊系统 WebSocket 作用域下无法注入 Spring Bean 对象&#xff1f; 考虑离线消息 前言 很久之前&#xff0c;咱们聊过 WebSocket 编程式…

掌握网络抓取技术:利用RobotRules库的Perl下载器一览小红书的世界

引言 在信息时代的浪潮下&#xff0c;人们对于获取和分析海量网络数据的需求与日俱增。网络抓取技术作为满足这一需求的关键工具&#xff0c;正在成为越来越多开发者的首选。而Perl语言&#xff0c;以其卓越的文本处理能力和灵活的特性&#xff0c;脱颖而出&#xff0c;成为了…

Android 属性动画及自定义3D旋转动画

Android 动画框架 其中包括&#xff0c;帧动画、视图动画&#xff08;补间动画&#xff09;、属性动画。 在Android3.0之前&#xff0c;视图动画一家独大&#xff0c;之后属性动画框架被推出。属性动画框架&#xff0c;基本可以实现所有的视图动画效果。 视图动画的效率较高…

【第七篇】使用BurpSuite进行主动、被动扫描和主动、被动爬虫

文章目录 前言主动扫描被动扫描主动爬虫被动爬虫前言 Burp Scanner 既可以用作全自动扫描仪,也可以用作增强手动测试工作流程的强大手段。 扫描网站涉及两个阶段: 抓取内容和功能: Burp Scanner 首先在目标站点周围导航,密切反映真实用户的行为。它对站点的结构和内容以及…

数字社会下的智慧公厕:构筑智慧城市的重要组成部分

智慧城市已经成为现代城市发展的趋势&#xff0c;而其中的数字化转型更是推动未来社会治理体系和治理能力现代化的必然要求。在智慧城市建设中&#xff0c;智慧公厕作为一种新形态的信息化公共厕所&#xff0c;扮演着重要角色。本文智慧公厕源头实力厂家广州中期科技有限公司&a…

【攻防世界】web2(逆向解密)

进入题目环境&#xff0c;查看页面信息&#xff1a; <?php $miwen"a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";function encode($str){$_ostrrev($str);// echo $_o;for($_00;$_0<strlen($_o);$_0){$_csubstr($_o,$_0,1);$__ord($_c)1;…

龙蜥社区「人人都可以参与开源」——体验开源成为“开源人“

龙蜥社区「人人都可以参与开源」体验开源——让更多的人了解开源&#xff01; 龙蜥社区开源概述&#xff1a;龙蜥社区开源的探索过程:龙蜥社区收获总结:AtomGit评测:服务设计上:功能结构上:安全设计上: AtomGit测评总结: 龙蜥社区开源概述&#xff1a; 在追求技术的路上少不了…

铸造大型基础平板的结构应该怎样设计

设计大型基础平板的结构时&#xff0c;需要考虑以下几个方面&#xff1a; 地质条件&#xff1a;首先要了解工程所在地的地质条件&#xff0c;包括土质、地下水位、地震状况等。根据地质条件来选择合适的基础类型&#xff0c;如浅基、深基或地下连续墙等。 荷载分析&#xff1a…

Proxmox VE qm 方式一键创建Windows虚拟机

前言 实现qm 方式一键创建Windows虚拟机&#xff0c;提高效率。 qm 一键创建Windows虚拟机 以下实现在线下载镜像&#xff0c;创建虚拟机&#xff0c;安装系统需要自己手动安装哦&#xff0c;如果想实现全自动安装系统&#xff0c;建议部署自己的内网pxe server 系统参考各参…

【C语言】整数和浮点数在内存中的存储

点这里是个人主页~ 这次的内容是比较底层的奥&#xff0c;对于理解编程很重要~ 整数浮点数在内存中的存储 一、 整数在内存中的存储二、大小端字节序和字节序判断大小端的概念一道简单关于大小端排序的百度面试题 三、简单理解数据类型存储范围例一例二例三例四例五例六 四、 …

STM32F4 IAP跳转APP问题及STM32基于Ymodem协议IAP升级笔记

STM32F4 IAP 跳转 APP问题 ST官网IAP例程Chapter1 STM32F4 IAP 跳转 APP问题1. 概念2. 程序2.1 Bootloader 程序 问题现象2.2. APP程序 3. 代码4. 其他问题 Chapter2 STM32-IAP基本原理及应用 | ICP、IAP程序下载流程 | 程序执行流程 | 配置IAP到STM32F4xxxChapter3 STM32基于Y…

未来工厂大脑:图扑组态软件在智能制造中的应用

组态软件&#xff1a;一般英文简称有三种分别为 HMI/MMI/SCADA&#xff0c;中文翻译为&#xff1a;人机界面/监视控制和数据采集软件。 运行于 PC 平台的一个通用工具软件&#xff0c;涉及各行各业&#xff0c;其主要功能是对生产现场的运行设备进行监控并就危险情况进行报警&…

【学习】使用VScode连接服务器。

step1: 安装 Remote - ssh 扩展 step2&#xff1a; 进入步骤2中&#xff0c;进行文件配置。 step3&#xff1a; 点击箭头进行连接。 step4&#xff1a; 输入密码即可。选择 platform时候&#xff0c;选择使用 Linux&#xff0c;而不是windows。

FreeRTOS创建第一个程序

使用freeRTOS创建任务时使用如下函数 函数的参数 创建一个FreeRTOS任务点亮led灯实现led灯500毫秒翻转一次 具体的代码实现 #include "stm32f10x.h" // Device header #include "Delay.h" #include "freeRTOS.h" #include &quo…

PMP持证者在面试项目经理时有加持吗?

对PMP认证获取后是否在面试中加持很多人是没有体验过的&#xff0c;因为大部分人考取PMP认证的原因是因为公司的要求&#xff0c;没有这个证书可能面临被“优化”的风险。理论上来说一样的道理&#xff0c;PMP认证既然能够保住工作岗位&#xff0c;那么在面试中一定会有相应的作…