九、重構6:使用“多型”取代條件表示式
經過前面八篇文章,五次的重構,對於這個充電寶計費專案的重構基本上已經完成。今天是這一系列的最後一篇文章,我們來講講如何對條件表示式進行重構,對於條件表示式的重構比較的一種重構方式就是利用類的多型性進行重構。接下來我們就要使用該規則對PowerBank類中的GetAmount()與GetFrequentRenterPoints()函式進行重構。
我們對PowerBank類中的GetAmount()方法中的Switch-Case結構觀察時,我們發現,此處的程式碼完全可以使用類的多型來替代。具體實現方法就是將不同的價格計算方式提取到我們新建立的價格類中,每個地段的充電寶都有自己的價格類,而這些價格類都實現同一個介面,這樣一來在PowerBank類中就可以使用多類來獲取總金額與積分了。
1.首先我們來建立一個價格介面。將游標停在PowerBank類名上,單擊滑鼠右鍵,在彈出選單中選擇“快速操作和重構”,再彈出的快捷選單中選擇“提取介面”。如下圖。
2.如上圖,在“提取介面”對話方塊中,填寫介面名稱,介面檔名稱,在“選擇構成介面的公共成員”列表框中,勾選相應的方法名稱,使用滑鼠點選“確定”按鈕,Visual Studio 2019會自動生成一個介面檔案。我們對自動生成的介面檔案進行修改。具體程式碼如下:
namespace LeasePowerBank { public interface IPrice { int GetPriceCode(); decimal GetAmount(int RentedTime); int GetFrequentRenterPoints(int RentedTime); } }
3.在“解決方案資源管理器”中,選中“LeasePowerBank”專案,然後單擊滑鼠右鍵,在彈出的快捷選單中選擇“新增—》類”。如下圖。
4.在“新增新項”對話方塊中,選擇類,在名稱中輸入“LowTraffic”,然後點選“新增”按鈕。如下圖。
5.在Visual Studio 2019的程式碼編輯器中,在LowTraffic類繼承IPrice介面。在游標停在介面IPrice上,然後單擊滑鼠右鍵,在彈出的快捷選單中選擇“快捷操作和重構—》實現介面”。如下圖。
6.Visual Studio 2019會在LowTraffic類中自動新增實現介面的相應程式碼,我們只需要進行修改就可。具體程式碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { public class LowTraffic : IPrice { public decimal GetAmount(int RentedTime) { decimal amount = RentedTime; if (RentedTime > 12) { amount = 12; } return amount; } public int GetFrequentRenterPoints(int RentedTime) { decimal amount = GetAmount(RentedTime); int frequentRenterPoints = (int)Math.Ceiling(amount); return frequentRenterPoints; } public int GetPriceCode() { return PowerBank.LowTraffic; } } }
7. 重複上面的第3步到第6步,我們分別來實現MiddleTraffic與HighTraffic類。這兩個類的程式碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { public class MiddleTraffic:IPrice { public decimal GetAmount(int RentedTime) { decimal amount = RentedTime; amount = RentedTime * 3; if (RentedTime > 8) { amount = 24; } return amount; } public int GetFrequentRenterPoints(int RentedTime) { decimal amount = GetAmount(RentedTime); int frequentRenterPoints = (int)Math.Ceiling(amount); return frequentRenterPoints; } public int GetPriceCode() { return PowerBank.MiddleTraffic; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { public class HighTraffic:IPrice { public decimal GetAmount(int RentedTime) { decimal amount = RentedTime; amount = RentedTime * 5; if (RentedTime > 10) { amount = 50; } return amount; } public int GetFrequentRenterPoints(int RentedTime) { decimal amount = GetAmount(RentedTime); int frequentRenterPoints = (int)Math.Ceiling(amount); return frequentRenterPoints; } public int GetPriceCode() { return PowerBank.HighTraffic; } } }
8. 在新增了上面的程式碼之一,我們的PowerBank也要進行相應的修改,來利用多類來實現不同地段充電寶的計費計算與積分計算。在PowerBank中新增一個IPrice宣告的物件,我們會根據不同的priceCode來給price變數分配不同的物件。金額計算時,只要在GetAmount()中呼叫price的GetAmount()方法。積分計算類似。具體程式碼如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LeasePowerBank { /// <summary> /// 充電寶類 /// </summary> public class PowerBank { //地段人流量種類 public static int LowTraffic = 0;//低人流量地段 public static int MiddleTraffic = 1;//中人流量地段 public static int HighTraffic = 2; //高人流量地段 public int PriceCode; //價格程式碼 public string Title;//充電寶名稱 public PowerBank(string title, int priceCode) { SetPriceCode(priceCode); Title = title; } IPrice price = null; private void SetPriceCode(int priceCode) { PriceCode = priceCode; switch (priceCode) { case 0: price = new LowTraffic(); break; case 1: price = new MiddleTraffic(); break; case 2: price = new HighTraffic(); break; default: break; } } /// <summary> /// 根據消費金額,充電寶所處地段,進行積分計算 /// </summary> /// <param name="RentedTime">租賃時間</param> /// <returns></returns> public int GetFrequentRenterPoints(int RentedTime) { return price.GetFrequentRenterPoints(RentedTime); } /// <summary> /// 根據充電寶訂單,計算總金額 /// </summary> /// <param name="RentedTime">租賃時間</param> /// <returns></returns> public decimal GetAmount(int RentedTime) { return price.GetAmount(RentedTime); } } }
9. 我們的測試用例依然不變。在每次重構後我們都需要呼叫上述的測試用例來檢查重構是否產生了副作用。現在我們的類間的依賴關係沒怎麼發生變化,只是相應類中的方法有些變化。在Visual Studio 2019的選單欄上找到“測試-->執行所有測試”選單項。或者在“測試資源管理器中”選擇 “在檢視中執行所有測試”按鈕, 執行測試用例。監測重構的結果是否正確。如下圖。
經過上面的重構,我們的這個專案也差不多了。重構就是這樣,一步步的來,不要著急,每重構一次總是要向著好的方向發展。如果你從一始就是專案程式碼直接重構到重構6的程式碼,似乎有些困難。經過上面這些重構1至重構6這一步一步的進行重構,實際上也是挺簡單的。