Ioc (Inversão de controle)
Um padrão comum que pode ser usado para aumentar a modularidade na base de código de um aplicativo usando o padrão MVVM é usar alguma forma de inversão de controle. Uma das soluções mais comuns, em particular, é usar a injeção de dependência, que consiste na criação de vários serviços que são injetados em classes de back-end (por exemplo, passadas como parâmetros para os construtores do viewmodel). Isso permite que o código que usa esses serviços não dependa dos detalhes de implementação desses serviços e também facilita a troca de implementações concretas desses serviços. Esse padrão também facilita a disponibilização de recursos específicos da plataforma para o código de back-end, abstraindo-os por meio de um serviço que é injetado quando necessário.
O Kit de Ferramentas MVVM não fornece APIs internas para facilitar o uso desse padrão, pois já existem bibliotecas dedicadas especificamente para isso, como o pacote Microsoft.Extensions.DependencyInjection
, que fornece um conjunto de DI de APIs totalmente em destaque e avançado, e atua como um IServiceProvider
fácil de configurar e usar. O guia a seguir fará referência a essa biblioteca e fornecerá uma série de exemplos de como integrá-la a aplicativos usando o padrão MVVM.
APIs de Plataforma:
Ioc
Configurar e resolver serviços
A primeira etapa é declarar uma instância IServiceProvider
e inicializar todos os serviços necessários, geralmente na inicialização. Por exemplo, na UWP (mas uma configuração semelhante também pode ser usada em outras estruturas):
public sealed partial class App : Application
{
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
}
/// <summary>
/// Gets the current <see cref="App"/> instance in use
/// </summary>
public new static App Current => (App)Application.Current;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<IClipboardService, ClipboardService>();
services.AddSingleton<IShareService, ShareService>();
services.AddSingleton<IEmailService, EmailService>();
return services.BuildServiceProvider();
}
}
Aqui, a propriedade Services
é inicializada na inicialização e todos os serviços de aplicativo e viewmodels são registrados. Há também uma nova propriedade Current
que pode ser usada para acessar facilmente a propriedade Services
de outras exibições no aplicativo. Por exemplo:
IFilesService filesService = App.Current.Services.GetService<IFilesService>();
// Use the files service here...
O principal aspecto aqui é que cada serviço pode estar usando APIs específicas da plataforma, mas como todas elas são abstraídas por meio da interface que o código está usando, não é necessário se preocupar com elas sempre que estivermos resolvendo uma instância e usando-a para executar operações.
Injeção de construção
Um recurso poderoso que está disponível é a “injeção de construtor”, que significa que o provedor de serviços de DI é capaz de resolver automaticamente dependências indiretas entre serviços registrados ao criar instâncias do tipo que está sendo solicitado. Considere o seguinte serviço:
public class FileLogger : IFileLogger
{
private readonly IFilesService FileService;
private readonly IConsoleService ConsoleService;
public FileLogger(
IFilesService fileService,
IConsoleService consoleService)
{
FileService = fileService;
ConsoleService = consoleService;
}
// Methods for the IFileLogger interface here...
}
Aqui temos um tipo FileLogger
implementando a interface IFileLogger
e exigindo instâncias IFilesService
e IConsoleService
. A injeção de construtor significa que o provedor de serviços de DI reunirá automaticamente todos os serviços necessários, da seguinte maneira:
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<IConsoleService, ConsoleService>();
services.AddSingleton<IFileLogger, FileLogger>();
return services.BuildServiceProvider();
}
// Retrieve a logger service with constructor injection
IFileLogger fileLogger = App.Current.Services.GetService<IFileLogger>();
O provedor de serviços de DI verificará automaticamente se todos os serviços necessários estão registrados e, em seguida, os recuperará e invocará o construtor do tipo concreto IFileLogger
registrado, para fazer com que a instância retorne.
E os viewmodels?
Um provedor de serviços tem “serviço” no nome, mas pode ser usado para resolver instâncias de qualquer classe, incluindo viewmodels! Os mesmos conceitos explicados acima ainda se aplicam, incluindo a injeção de construtor. Imagine que temos um tipo ContactsViewModel
, usando uma instância IContactsService
e outra IPhoneService
por meio de seu construtor. Poderíamos ter um método ConfigureServices
como este:
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Services
services.AddSingleton<IContactsService, ContactsService>();
services.AddSingleton<IPhoneService, PhoneService>();
// Viewmodels
services.AddTransient<ContactsViewModel>();
return services.BuildServiceProvider();
}
Em seguida, em nosso ContactsView
, atribuiremos o contexto de dados da seguinte maneira:
public ContactsView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}
Mais documentos
Para obter mais informações sobre Microsoft.Extensions.DependencyInjection
, consulte aqui.
Exemplos
- Confira o aplicativo de exemplo (para várias estruturas de interface do usuário) para ver o Kit de Ferramentas MVVM em ação.
- Você também pode encontrar mais exemplos nos testes de unidade.