4

фондизайн шаблон потреблять WebAPI от MVP Winform Client

Я строю двухуровневую приложение:

  • Этап 1: Winforms приложение, использующее MVP (Model-View-Presenter) дизайн рисунок.
  • Tier 2: WebAPI RESTful обслуживание.

Клиент Winforms будет использовать службу WebAPI с использованием HttpClient. Оба яруса тяжело использовать IoC и Dependency Injection паттернов проектирования

Вопрос

Когда приложение 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