4

背景設計模式從MVP WinForm的客戶端消耗的WebAPI

我建立兩個層次的應用:

  • 1級:使用MVP的WinForms應用程序(模型 - 視圖 - 演示)設計模式。
  • 第2層:WebAPI RESTful服務。

Winforms客戶端將使用HttpClient使用WebAPI服務。這兩個層次巨資利用IoC和依賴注入設計模式

問題

當WinForms應用程序從的WebAPI服務需要的數據,演示者將協調請求。我的問題是,你會直接在演示者的內部使用HttpClient嗎?爲了保持演示者的可測試性,您如何確保您不必依靠具體的HttpClient調用?我正在想辦法整合這個question的最佳答案。

+0

我投票結束這個問題作爲題外話,因爲這個問題屬於http://programmers.stackexchange.com/ –

回答

4

我通過抽象一切來解決這個問題。

在表示層我想有一個服務抽象...

public interface IServiceAgent { 
    Task<SomeResultObject> GetSomething(string myParameter); 
} 

...抽象是我從網上API希望。主講人不需要協調請求。演示者不關心數據來自何處。它只知道它需要一些東西並要求它(SoC)。這是服務代理的工作(SRP)。

服務代理實現可能需要調用不同的數據源。包括網頁。所以抽象HttpClient將放鬆與該實現的耦合。

一個簡單的例子像...

public interface IHttpClient { 
    System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class; 
    System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class; 
    //...other members as needed : DeleteAsync, PostAsync, PutAsync...etc 
} 

一些示例實現可以是這樣的......

public class MyPresenter { 
    public MyPresenter(IServiceAgent services) {...} 
} 

public class MyDefaultServiceAgent : IServiceAgent { 
    IHttpClient httpClient; 

    public MyDefaultServiceAgent (IHttpClient httpClient) { 
     this.httpClient = httpClient; 
    } 

    public async Task<SomeResultObject> GetSomething(string myParameter) { 
      var url = "http://localhost/my_web_api_endpoint?q=" + myParameter; 
      var result = await httpClient.GetAsync<SomeResultObject>(url); 
      return result; 
    } 
} 

public class MyDefaultHttpClient : IHttpClient { 
    HttpClient httpClient; //The real thing 

    public MyDefaultHttpClient() { 
     httpClient = createHttpClient(); 
    } 

    /// <summary> 
    /// Send a GET request to the specified Uri as an asynchronous operation. 
    /// </summary> 
    /// <typeparam name="T">Response type</typeparam> 
    /// <param name="uri">The Uri the request is sent to</param> 
    /// <returns></returns> 
    public System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class { 
     return GetAsync<T>(new Uri(uri)); 
    } 

    /// <summary> 
    /// Send a GET request to the specified Uri as an asynchronous operation. 
    /// </summary> 
    /// <typeparam name="T">Response type</typeparam> 
    /// <param name="uri">The Uri the request is sent to</param> 
    /// <returns></returns> 
    public async System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class { 
     var result = default(T); 
     //Try to get content as T 
     try { 
      //send request and get the response 
      var response = await httpClient.GetAsync(uri).ConfigureAwait(false); 
      //if there is content in response to deserialize 
      if (response.Content.Headers.ContentLength.GetValueOrDefault() > 0) { 
       //get the content 
       string responseBodyAsText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 
       //desrialize it 
       result = deserializeJsonToObject<T>(responseBodyAsText); 
      } 
     } catch (Exception ex) { 
      Log.Error(ex); 
     } 
     return result; 
    } 

    private static T deserializeJsonToObject<T>(string json) { 
     var result = JsonSerializer.Deserialize<T>(json); 
     return result; 
    } 
} 

通過通過允許單元測試與抽象那些你保持演示檢驗的依賴假冒/模仿的服務代理。您可以使用僞造/模擬的HTTP客戶端來測試您的服務代理。如果您需要更改/交換/維護應用程序組件,它還允許您注入這些接口的任何具體實現。

+0

夢幻般的答案,謝謝!在創建服務代理時,您是否會爲每個演示者創建一個服務代理,或者爲每個模型創建一個服務代理?我正在考慮演示者需要多次調用以獲取不同類型的數據的場景。此外,在使用IoC容器時,如何確保正確的服務代理正在注入演示者? – Andrew

+0

這取決於您所感到的舒適程度。我通常每個域都有一個服務代理。我儘可能地堅持SRP。至於獲得正確的注入,我使用ISP – Nkosi

+0

我仍然學習正確的編程技術,因此,堅實的原則。當你說你使用ISP時,你是否說你爲每個服務代理創建了不同的界面? – Andrew