重构——解决过长参数列表(long parameter list)

目录

    • 1、Replace Param with Query
    • 2、Preserve Whole Object
    • 3、Introduce Param Object
    • 4、Remove Flag Argument
    • 5、Combine Functions into Class
    • Reference

当我们需要在超长函数中提炼子函数时,如果函数内有大量的参数和临时变量,这将会对函数的提炼形成很大阻碍,你可能会形成过长参数列表,当然你也可以使用全局变量,显然上面两种都不是很好的办法。
函数参数列表应该总结出函数的可变性,标志出函数可能体现出行为差异的主要方式。参数列表应当避免重复以及过长。
《重构》给我们提供了几个好点子:(WHAT)
1、以查询取代参数
2、保持对象完整
3、引入参数对象
4、移除标记参数
5、函数组合成类
接下来讲解它们具体怎么做(HOW)

1、Replace Param with Query

注意点:若移除参数可能会给函数体增加不必要的依赖关系,不要使用。
具体做法:使用提炼函数将参数的计算过程提炼到一个独立的函数

class Order {
/*
...
*/
double getFinalPrice() 
{const double basePrice = this.quantity * this.itemPrice;int discountLevel = 1;if (this.quantity > 100) discountLevel = 2;return this.discountedPrice(basePrice, discountLevel);
}double discountedPrice(const double basePrice, int discountLevel)
{switch (discountLevel) {case 1: return basePrice * 0.95;case 2: return basePrice * 0.9;}
}};

修改后的代码:

class Order {
public:int quantity = 0;int itemPrice = 0;int discountLevel = 0;Order(int quan, int price, int level){quantity = quan;itemPrice = price;discountLevel = level;}double getFinalPrice(){const double basePrice = this->quantity * this->itemPrice;return this->discountedPrice(basePrice);}int getDiscountLevel(){return (this->quantity > 100) ? 2 : 1;}double discountedPrice(const double basePrice){switch (this->getDiscountLevel()) {case 1: return basePrice * 0.95;case 2: return basePrice * 0.9;}}};

当需要使用discountLevel 变量的时候,后者自己调用函数,不需要把结果传入了。
debug结果正确:

int main() {Order* order = new Order(1000,1,0);double res = order->getFinalPrice();std::cout << res << std::endl;
}

在这里插入图片描述

2、Preserve Whole Object

需要注意:
如果从一个对象中抽取几个值,单独对这几个值做某些逻辑操作,通常标志着这段逻辑应该被搬移到对象中,然后从外部调用它。
当看见代码从一个记录结构中导出几个值,然后又把这几个之一起传给一个函数,那么最好把整个记录传给这个函数,在函数体内部导出所需要的值。
举例:一个室温监控系统,负责记录一天中最高气温和最低气温,然后将实际温度范围与预定温度控制计划比较,如果不符合要求,发出警告。

class HeatingPlan {
public:bool withinRange(int bottom, int top){return (bottom >= this._tempRange.low) && (top <= this._tempRange.high);}
};
int main() {// 调用方const int low = aRoom.daysTempRange.low;const int high = aRoom.daysTempRange.high;if(aPlan.withinRange(low, high))std:: cout << "Warning" << std::endl;
}

我们不必将温度范围的信息拆开单独传递,只需要将整个范围对象传递给withinRange即可
通过重构,可以写成下面形式:

class HeatingPlan {
public:bool withinRange(int bottom, int top){return (bottom >= this._tempRange.low) && (top <= this._tempRange.high);}bool xxNewWithinRange(tempRange range){const int low = range.low;const int high = range.high;const bool res = this.withinRange(low, high);return res;}
};
int main() {// 调用方const tempRange range = aRoom.daysTempRange;const bool isWithinRange = aPlan.xxNewWithinRange(range);if(isWithinRange)std:: cout << "Warning" << std::endl;
}

3、Introduce Param Object

当一组数据项总是结伴而行,出没于一个又一个函数,这样的一组数据称为数据泥团,通常用一个数据结构来代替它们。
如下方,查看一组温度读数是否有超出运行范围。

bool readingsOutsideRange(Station station, int min, int max) 
{return station.readings.filter(station.temp < min || station.temp > max);
}
int main() {// 调用方bool res = readingsOutsideRange(station, operatingPlan.temperatureFloor,operatingPlan.temperatureCeiling);
}

很显然,temperatureFloor与temperatureCeiling是一堆数据泥团。
我们构造一个新的类去聚合它们,并将判断逻辑up到这个类的内部。

class NumberRange {
public:int min;int max;NumberRange(int min, int max){this->min = min;this->max = max;}bool contains(int argv) {return (argv >= this->min) && (argv <= this->max);}
};
bool readingsOutsideRange(Station station, NumberRange range)
{return station.readings.filter(!range.contains(station.temp));
}
int main() {// 调用方const NumberRange range = new NumberRange(operatingPlan.temperatureFloor, operatingPlan.temperatureCeiling);bool res = readingsOutsideRange(station, range);
}

4、Remove Flag Argument

以明确的函数取代参数
如:

function setDimension(name, value) 
{if (name == "height")this->_height = value;else if(name == "width")this->_width = value;return;
}
应当转换为
====>
function setHeight(value) {this->_height = value;}
function setWidth(value) {this->_width = value;}

标记参数指的是调用者用它来指示被调函数应该执行哪一部分的逻辑。
上面的情况还算简单,可以重新构造两个函数以及内部逻辑,但有时想将标记参数的分发逻辑剥离到顶层,需要的工作量很大:
我们可以退而求其次,保留原有的函数,在其之上添加两个函数:

function setHeight(value) {return setDimension("height", value);}
function setWidth(value) {return setDimension("width", value);}

这两个包装函数分别代表了原函数的一部分使用方式,不过并非从原函数拆分,而是使用代码文本强行定义的。

5、Combine Functions into Class

function base(aReading) {...}
function taxable(aReading) {...}
function calBaseCharge(aReading) {...}应当改为
======>
class Reading {base() {...}taxable() {...}calBaseCharge() {...}
};

如果发现一组函数形影不离地操作同一块数据,且通常使用参数传递的方式将数据块传给函数,那么,是时候组建一个类。
类能够明确地给这些函数提供一个共用地环境,在对象内部调用函数可以减少参数传递

Reference

《重构 改善既有代码的设计.第二版》P324 P319 P140 P314 P144

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

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

相关文章

C# 点点滴滴: out和ref

用c#很长一段时间了&#xff0c;不过基本是啥都不会&#xff0c;当C用的&#xff0c;作为写单片机的&#xff0c;还是真心觉得C比较亲切&#xff0c;呵呵。 不过总是要进步啊&#xff0c;慢慢积累呗&#xff0c;这次是写一个CAN的上位机模板出来&#xff0c;以后的项目就要彻底…

css控制图片最宽 最高值

.content img{width:expression_r(this.width > 500 && this.height < this.width ? 500:true);max-width:500px;height:expression_r(this.height >500 ? 500:true);max-height:500px; }转载于:https://www.cnblogs.com/panlin/archive/2013/01/06/2848017…

踩踩踩

http://china.findlaw.cn/laodongfa/ctjg/cy/cybc/ 二、合法裁员经济补偿标准的计算 按照《劳动合同法》第四十七条规定&#xff0c;经济补偿按劳动者在本单位工作的年限&#xff0c;每满一年支付一个月工资的标准向劳动者支付。六个月以上不满一年的&#xff0c;按一年计算;不…

c# 字节十六进制转十进制_用C中的十进制,八进制和十六进制数字初始化字节数组...

c# 字节十六进制转十进制C中的字节数组 (byte array in C) In C programming language, an unsigned char type can be used to declare byte array in C programming language. An unsigned char can contain a value from 0 to 255, which is the value of a byte. 在C编程语…

从uptime、stress、mpstat、pidstat观察CPU密集型、IO密集型、进程密集型切换的系统性能

uptime dyydyy-Lenovo-ThinkBook-14-IIL:~$ uptime10:27:10 up 7 min, 1 user, load average: 1.32, 0.99, 0.49结果分别对应&#xff1a;当前时间、系统运行时间、当前用户数目、过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average) 平均负载是指单位时间内&#xff0c…

解析和创建xml

http://www.cnblogs.com/Li-Cheng/p/3610474.html 转载于:https://www.cnblogs.com/mxw272618/p/3769900.html

python - VirtualEnv virtualenvwrapper

VirtualEnv 是什么 VirtualEnv用于在一台机器上创建多个独立的python运行环境&#xff0c;VirtualEnvWrapper为前者提供了一些便利的命令行上的封装。 为什么要用 - 隔离项目之间的第三方包依赖&#xff0c;如A项目依赖django1.2.5&#xff0c;B项目依赖django1.3。- 为部署应用…

多台计算机共享内存_共享内存多处理器和指令执行| 计算机架构

多台计算机共享内存共享内存多处理器 (Shared Memory Multiprocessor) There are three types of shared memory multiprocessor: 共有三种类型的共享内存多处理器&#xff1a; UMA (Uniform Memory Access) UMA(统一内存访问) NUMA (Non- uniform Memory Access) NUMA(非统一…

htop与atop

htop htop使用详解–史上最强 atop Linux atop监控工具部署

js未看的文章

Web前端研发工程师编程能力飞升之路 在浏览器的背后&#xff08;一&#xff09; —— HTML语言的词法解析 组件化的前端开发流程 用js书写UI组件之js基础知识 GC与JS内存泄漏 蓝色理想之前端开发 w3c JavaScript Puzzlers react AngularJS入门教程 jQuery源码分析-如何做jQuery…

方法重写,隐藏在子类父类中的各种调用实践

一.子类和父类方法之间的关系 1.当子类和父类有方法完全相同的方法 namespace ConsoleApplication2 {class Program{static void Main(string[] args){B b new B();A a new A();A c new B();b.Show();a.Show();c.Show();Console.Read();}}public class A{public void Show()…

向量余弦值python_向量/矩阵的余弦值打印(元素明智的操作) 使用Python的线性代数

向量余弦值pythonPrerequisite: 先决条件&#xff1a; Defining a Vector 定义向量 Defining a Matrix 定义矩阵 Numpy is the library of function that helps to construct or manipulate matrices and vectors. The function numpy.cos(x) is a function used for generati…

centos 6.5网卡dhcp不能获得网关

环境:vmware centos6.5 添加两个虚拟网卡。一个自动获取ip(用于上网-桥接) 一个手动(与主机通信用于ssh-NAT)。 因为自已手动改了一下ifcfg-eth0里面的HWADDR地址。造成 eth0网卡不能识别。多出一个eth2的网卡。 配置eth2网卡&#xff0c;可以自动获取到ip地址 但用netstat -r…

CPU上下文切换(系统调用、进程上下文、线程上下文、中断上下文)

CPU寄存器&#xff0c;与程序计数器&#xff08;存储CPU正在执行的指令位置&#xff0c;或者即将执行的下一条指令的位置&#xff09;共同组成CPU上下文。 CPU上下文切换指的是&#xff1a;把前一个任务的CPU上下文保存起来&#xff0c;然后加载新任务的上下文到这些寄存器和程…

(解决)从同事那里取来的工程不能编译运行,出现以下错误,求帮助

错误 6 未能从程序集 C:\Program Files (x86)\MSBuild\Microsoft\Silverlight for Phone\v4.0\Microsoft.Phone.Build.Tasks.dll 加载任务“Microsoft.Phone.Build.Tasks.ValidateWMAppManifest”。 Could not load file or assembly Microsoft.Build.Utilities, Version2.0.0…

编程 小数位数_使用动态编程的n位数的非递减总数

编程 小数位数Problem statement: 问题陈述&#xff1a; Given the number of digits n, find the count of total non-decreasing numbers with n digits. 给定位数n &#xff0c;找到具有n位数字的非递减总数。 A number is non-decreasing if every digit (except the fir…

vmstat、sysbench、/proc/interrupts,性能压测

如何查看系统的上下文切换情况 vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。 # 每隔 5 秒输出 1 组数据 vmstat 5procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----r …

sql查询中自动统计某项数量

select * from dbo.Vehicle_Maintain_Details A inner join ( select MaintainType as tempTypeName,count(ID) as num from dbo.Vehicle_Maintain_Details group by MaintainType) B on A.MaintainTypeB.tempTypeName转载于:https://www.cnblogs.com/ryan-wan/archive/2013/0…

一个简易无锁池

一个简易 无锁池 1.所有读写无等待&#xff0c;不需要判断条件直接读写(除自动扩充容量时&#xff09;&#xff0c;效率是一般带锁或带条件判断池的两倍以上。 2.预先开辟2的幂大小容量&#xff0c;可自增&#xff0c;每次翻倍 3.仅提供思路&#xff0c;工程应用可靠性还不确定…

在给定约束下可以使用a,b和c形成的字符串数

Problem statement: 问题陈述&#xff1a; Given a length n, count the number of strings of length n that can be made using a, b and c with at-most one b and two cs allowed. 给定长度n &#xff0c;计算可以使用a &#xff0c; b和c且长度最多为b和两个c的长度为n的…