我的爛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都可以利用我重構好的泛型方法。

沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。