重构——解决过长参数列表(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,一经查实,立即删除!

相关文章

从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…

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

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

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

一.子类和父类方法之间的关系 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()…

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

错误 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…

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

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

Linux系统上的程序调优思路概要

目录文件系统Linux内核应用程序架构设计性能监控性能测试CPU内存网络磁盘IO文件系统 Linux内核 应用程序 架构设计 性能监控 性能测试 CPU 内存 网络 磁盘IO

观察者模式Java实现

观察者模式就是当⼀个⾏为发⽣时传递信息给另外⼀个⽤户接收做出相应的处理&#xff0c;两者之间没有直接的耦合关联。 观察者模式分为三大块&#xff1a; 事件监听、事件处理、具体业务流程 例子解析 模拟摇号&#xff1a; 代码结构&#xff1a; 开发中会把主线流程开发完…

JavaScript | 声明数组并在每个循环中使用的代码

Declare an array and we have to print its elements/items using for each loop in JavaScript. 声明一个数组&#xff0c;我们必须使用JavaScript中的每个循环来打印其元素/项目。 Code: 码&#xff1a; <html><head><script>var fruits ["apple&…

拾牙的2021年秋招总结(大概会有帮助?)

目录秋招面试经历秋招面经参考基础部分面经常见问题对秋招一些经验最后收获后续安排秋招面试经历 时间公司岗位面试轮次是否完成2021年7月2日 07:00禾赛嵌入式软件工程师提前批一面pass2021年7月7日 16:00图森未来软件研发工程师-Linux应用提前批一面not pass2021年7月9日华为…

CPU使用率的查看以及性能分析(perf top/record/report)

目录CPU使用率查看CPU使用率&#xff08;top、pidstat解释&#xff09;CPU使用率过高perf topperf record 和 perf reportCPU使用率 Linux通过/proc虚拟文件系统&#xff0c;向用户空间提供了系统内部状态的信息。 /proc/stat提供的就是系统的CPU和任务统计信息。 执行命令cat…

如何从JavaScript数组中获取多个随机唯一元素?

The JavaScript is a very versatile language and it has a function almost everything that you want. JavaScript是一种非常通用的语言&#xff0c;它几乎具有您想要的所有功能。 Here, we will show you how to generate random unique elements from an array in JavaSc…

什么是ACID理论(二阶段、三阶段提交、TCC)

目录二阶段提交协议TCC&#xff08;Try-Confirm-Cancel&#xff09;预留成功预留失败三阶段提交协议总结Some questionsreferenceACID理论时对事务特性的抽象和总结&#xff0c;想要实现ACID需要掌握二阶段提交协议以及TCC 这里是有关协议的论文PDF链接&#xff1a; CONCURRENC…

oracle安装后新建数据库实例及配置

ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法 (2011-01-20 13:50:37) 转载▼标签&#xff1a; it 分类&#xff1a; 技术早上同事用PL/SQL连接虚拟机中的Oracle数据库&#xff0c;发现又报了“ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务…

html5游戏开发--动静结合(二)-用地图块拼成大地图 初探lufylegend

一、前言 本次教程将向大家讲解如何用html5将小地图块拼成大地图&#xff0c;以及如何用现有的高级html5游戏开发库件lufylegend.js开发游戏。 首先让我们来了解了解如何用html5实现动画&#xff0c;毕竟“动静结合”是先有动再有静。看了上一章的内容&#xff0c;或许你就有了…

BASE理论(基本可用策略+ 最终一致性实现)

目录实现基本可用的几个策略1、流量削峰&#xff08;不同地区售票时间错峰出售&#xff09;2、延迟响应&#xff0c;异步处理&#xff08;买票排队&#xff0c;基于队列先收到用户买票请求&#xff0c;排队异步处理&#xff0c;延迟响应&#xff09;3、体验降级&#xff08;看到…

Paxos算法(Basic Paxos 与 Multi-Paxos思想)

目录Basic Paxos三个角色达成共识的方法对于Basic Paxos的总结Multi-Paxos领导者优化 Basic Paxos 执行referencePaxos 算法包含 2 个部分&#xff1a; 1、Basic Paxos &#xff1a; 描述多节点之间如何就某个值达成共识 2、Multi-Paxos &#xff1a; 描述执行多个Basic Paxos实…

vs2012下调试mvc4源代码

当前流行的应该是mvc3才对。然后在研究mvc3的源代码时候&#xff0c;Html这个属性下的扩展方法Partial()都没有。IntelliSense不会提示该方法&#xff0c;找了半天的资料也问了一些博友&#xff0c;没看到好的解决棒法。最后没辙另辟蹊跷&#xff0c;就开始着手研究mvc4的源代码…

JAVA UDP网络编程学习笔记

一、UDP网络编程概述 采用TCP协议通信时&#xff0c;客户端的Socket必须先与服务器建立连接&#xff0c;连接建立成功后&#xff0c;服务器端也会持有客户端连接的Socket&#xff0c;客户端的Socket与服务器端的Socket是对应的&#xff0c;它们构成了两个端点之间的虚拟通信链路…

(转)页游安全攻与防,SWF加密和隐藏密匙

原文链接&#xff1a;http://netsecurity.51cto.com/art/201211/364775.htm 页游&#xff0c;最最核心的就是客户端&#xff08;swf&#xff09;与服务端的游戏通信了。游戏通信产生的封包&#xff0c;内容是否可识别&#xff0c;可篡改&#xff0c;可重放&#xff0c;处理逻辑…