我的爛Code重構之路(二)-泛型方法

我的爛Code重構之路(二)-泛型方法

Stinky tofu

前篇提到,以功能分解開發的缺點,但功能分解的開發方式也有其優點,那就是程式碼壞味道(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都可以利用我重構好的泛型方法。

在《重構:改善既有程式的設計(二版) pdf》中 page 87 有個三次法則,即:事不過三,三則重構。事實上,我還真的忍到第三次(更正確的說,是寫了第三次才發現壞味道)才進行重構。

整理過後,這盤臭豆腐可以上桌了。

沒有留言:

張貼留言

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