重構——解決過長參數列表(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.pswp.cn/news/376938.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376938.shtml
英文地址,請注明出處:http://en.pswp.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的…