九、重構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這一步一步的進行重構,實際上也是挺簡單的。