我的爛Code重構之路(二)-泛型方法
前篇提到,以功能分解開發的缺點,但功能分解的開發方式也有其優點,那就是程式碼壞味道(Code Bad Smell)特別的重,就像是吃臭豆腐,味道難然不佳,但如果好好認真盡快的處理,變成一道美味的佳餚也並無可能。
壞味道-發現相似的邏輯
在撰寫函數式Class時,你應該有個直覺,那就是很容易發現相似的邏輯。
舉個簡單的例子,我想整合使用臺北市政府資料開發平台上的資料:
我想整合停車與自行車兩項資訊,資料源到手後,接著就會很習慣的開發寫Code:
public static class DataTaipei { public static ParkResult GetPark() { // 發出停車場請求 // 取得停車場結果 return new ParkResult(); } public static YouBikeResult GetYouBike() { // 發出YouBike請求 // 取得YouBike結果 return new YouBikeResult(); } }
我故意不寫程式碼,只要是眼睛沒瞎的都應該知道我想要重構哪部分,說是簡單,但想想實際開發上,可能是第一天寫GetPark方法,第七天才寫GetYouBike方法,兩者中間還有數百、數千行的其他程式碼,你要怎麼聞到壞味道?如果是團隊分工開發,那又更不容易發現。
當然,今天的情境上全部都是我的爛Code,所以那味道很嗆鼻。再說一次,在對這程式碼還有印象、有記憶之前進行重構。你就能保有,怎麼今天寫的這段程式碼好像在那裡看過的感覺,當有這種感覺時,記得停下腳步想想,可能就是一次重構為美味佳餚的機會。
GetPark與GetYouBike的可能實作程式碼:
public static ParkResult GetPark() { string serverUrl = "http://data.taipei/"; string credential = "opendata/datalist/apiAccess?scope=resourceAquire&rid=9ba187c9-b07e-40bc-9aa5-8d3c9f1aad63"; ParkResult result = null; HttpClient client = new HttpClient(); client.BaseAddress = new Uri(serverUrl); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = client.GetAsync(credential).Result; if (response.IsSuccessStatusCode) { result = response.Content.ReadAsAsync<ParkResult>().Result; } return result; } public static YouBikeResult GetYouBike() { string serverUrl = "http://data.taipei/"; string credential = "opendata/datalist/apiAccess?scope=resourceAquire&rid=ddb80380-f1b3-4f8e-8016-7ed9cba571d5"; YouBikeResult result = null; HttpClient client = new HttpClient(); client.BaseAddress = new Uri(serverUrl); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = client.GetAsync(credential).Result; if (response.IsSuccessStatusCode) { result = response.Content.ReadAsAsync<YouBikeResult>().Result; } return result; }
泛型方法
就程式碼發展的歷程而言,這樣的結果很正確,兩個方法互不干擾並獨立作業,就程式碼的職責來看,每個方法只是對自己負責的URI資源做存取也很單一,並無不妥地方。就算不重構,也不會構成什麼大問題。
但我就是嬌情,就是想改,以下使用泛型方法重構之後的程式碼:
private static HttpClient Request(string serverUrl) { HttpClient client = new HttpClient(); client.BaseAddress = new Uri(serverUrl); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); return client; } private static T APIResponse<T>(string serverUrl, string credential, T responseType) { using (HttpClient client = Request(serverUrl)) { HttpResponseMessage response = client.GetAsync(credential).Result; if (response.IsSuccessStatusCode) { responseType = response.Content.ReadAsAsync<T>().Result; } } return responseType; }
可以看到二點,一、將重覆程式碼(HttpClient部分)抽離為一個獨立的函數,以增加可用性。二、我將相同邏輯的處理抽離並使用泛型技巧,以解決不同型別的問題。
接下來看看使用這些方法後的程式碼:
const string serverUrl = "http://data.taipei/"; public static ParkResult GetPark() { string credential = "opendata/datalist/apiAccess?scope=resourceAquire&rid=ddb80380-f1b3-4f8e-8016-7ed9cba571d5"; ParkResult result = null; result = APIResponse<ParkResult>(serverUrl, credential, result); return result; } public static YouBikeResult GetYouBike() { string credential = "opendata/datalist/apiAccess?scope=resourceAquire&rid=ddb80380-f1b3-4f8e-8016-7ed9cba571d5"; YouBikeResult result = null; result = APIResponse<YouBikeResult>(serverUrl, credential, result); return result; }
你可以發現,函數裡的程式碼變少變得更簡單了。未來想要呼叫任一個臺北市政府資料開發平台API都可以利用我重構好的泛型方法。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。