目录

  • 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

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

  1. 关于c++变长参数列表总结

    2019独角兽企业重金招聘Python工程师标准>>> 写在前面 在C++语言中,有两个三个(???)地方用到了"..."这个符号,分别是: 变长参数列表.下面用 ...

  2. c语言参数buf,C语言---变长参数列表---变长参数的传递

    5.4.2 变长参数的传递 上一节讲述了如何创建具有变长参数的函数和如何读取变长参数,其操作都在函数内完成,本节将讲述把变长参数列表整体作为参数传递给其他函数的方法. 变长参数传递的函数族如下: #i ...

  3. [转载] 【python】Python中*args和**kwargs的区别(在Python中如何使用可变长参数列表)

    参考链接: Python中的*args 和 **kwargs 博客已经搬家到"捕获完成": https://www.v2python.com 或者可以叫做,在Python中如何使用 ...

  4. Java可变长参数列表

    可以把类型相同但个数可变的参数传递给方法,方法的参数声明为: (类型名--参数名) 在方法声明中,指定类型后紧跟着省略号(...),只能给方法中指定一个可变长参数,同时该参数必须是最后一个参数,任何常 ...

  5. 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  6. 如何消除代码山中那一大坨参数列表

    本文分享自华为云社区<如何消除代码屎山中的一大坨参数列表?>,作者: JavaEdge . 有经验的程序员应该都见过,一个方法坐拥几十上百个参数. 1 方法为何要有参数? 因为不同方法之间 ...

  7. 如何消除代码屎山中的一大坨参数列表?

    有经验的程序员应该都见过,一个方法坐拥几十上百个参数. 方法为何要有参数? 因为不同方法间需共享信息. 但方法间共享信息的方式不止一种,除了参数列表,还有全局变量.但全局变量总能带来意外惊喜,所以,取 ...

  8. Scala可变参数列表,命名参数和参数缺省

    重复参数 Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用"*"来指明该参数为重复参数.例如: 1 ...

  9. 如何获取函数的变长参数(va_list, va_start, va_arg, va_end)

    最近在花时间研读C++. 函数这章讲到了函数的变长参数(ellipsis...),但是primer中讲得比较浅,提到了怎么声明怎么调用,但是没有写明在函数内部是如何获取变长的参数的. 1)省略号(el ...

最新文章

  1. 英特尔:谁说深度学习已死?AI任务挑大梁的是CPU,不是GPU
  2. 第一天:制定项目章程
  3. 771. Jewels and Stones 宝石与石头
  4. python_getopt解析命令行输入参数的使用
  5. 苏老师首播3小时!超500人观看!录屏!源码!PPT……你要的都在这里!
  6. Java对PHP服务器hmac_sha1签名认证方法的匹配实现
  7. this的作用(转)
  8. 微信抢红包插件xposed框架
  9. 搜索百度网盘的小姐姐,云网盘之家
  10. 计算机科学与技术 未来,浅析计算机科学与技术的未来发展趋势
  11. css字体.ttf文件压缩3.1M变8K(原生和Vue中使用)
  12. 计算机控制系统信号的采样,计算机控制系统-信号采样与分析演示.ppt
  13. python语音转文字软件_免费的语音转文字电脑版软件-批量语音转文字小工具(不限时长)下载V1.1完全免费版-西西软件下载...
  14. html打开网页过场动画_一款谷歌(Google)打造的广告网页设计制作软件
  15. 虚拟机中ip地址总是自动变化解决办法
  16. 51单片机:1602液晶显示屏
  17. 量化选股宝三步走,京豆礼包你拿走
  18. 自然语言处理核心期刊_中国中文信息学会
  19. 传统语音识别介绍【四】—— 语言模型
  20. 如何在html里面搜索代码,怎样在网页里查找关键字

热门文章

  1. linux ssh无需密码,linux下 ssh 实现无需密码的远程登陆
  2. linux备份日志文件脚本,Linux篇:Shell脚本实现Gitlab双备份
  3. 如何仅通过CSS实现多行文本超长自动省略号
  4. angularjs中 $watch 和$on 2种监听的区别?
  5. 原生JS封装ajax以及request
  6. CSS布局(五) 网页布局方式
  7. CF888G XOR-MST 最小异或生成树
  8. 物联网架构成长之路(24)-Docker练习之Compose容器编排
  9. 经典MapReduce作业和Yarn上MapReduce作业运行机制
  10. freopen()函数在ACM中的使用