依赖倒置原则(Dependency Inversion Principle)
依赖倒置原则(Dependency Inversion Principle)是 Robert C. Martin 总结的S.O.L.I.D. Principles中的五个原则之一。讲的是灵活的系统在源代码层的依赖应该只依赖抽象层,而非具体实现。
基本原则
- 不要依赖频繁变更的实体类
- 不要继承频繁变更的实体类
- 不要重写实体方法
- 不要引用任何频繁改变的实体类和实体方法
示例
我们在 MVVM - 爱奇艺UWP客户端应用与实践 介绍了MVVM的应用,我们在真正需要执行登录操作的时候,需要一个后台服务PassportService,一般我们会用一个实体类来封装这个服务,然后在我们的LoginCommand中直接调用这个实体服务,那么如果有一天我们需要换一个登录的服务,那么我们就需要对LoginCommand进行修改。
根据依赖倒置原则,我们在LoginCommand里不应该依赖易变的实体类,因此我们可以按如下方面来做
- 定义接口:IPassportService
public interface IPassportService
{
Task<bool> LoginAsync(string userName, string password);
}
- 实现IPassportService
public class PassportService : IPassportService
{
public async Task<bool> LoginAsync(string userName, string password)
{
// Call your login service here
return Task.FromResult<bool>(true);
}
}
- 修改LoginCommand:注意IPassportService是通过构造函数传入的
public class LoginCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private LoginModel model;
private IPassportService passportService;
public LoginCommand(LoginModel model, IPassportService passportService)
{
this.model = model;
this.passportService = passportService;
this.model.PropertyChanged += Model_PropertyChanged;
}
private void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(model.UserName) ||
e.PropertyName == nameof(model.Passowrd))
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
return !string.IsNullOrEmpty(this.model.UserName) &&
!string.IsNullOrEmpty(this.model.Passowrd);
}
public async void Execute(object parameter)
{
await passportService.LoginAsync(this.model.UserName, this.model.Password);
}
}
- 修改LoginViewModel,通过构造函数传入PassportService
public class LoginViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private LoginModel loginModel = new LoginModel();
private ICommand loginCommand;
public ICommand LoginCommand
{
get { return loginCommand; }
}
public LoginViewModel(IPassportService passportService)
{
loginCommand = new LoginCommand(loginModel, passportService);
}
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
- 修改LoginPage,构建PassportService实例。在实际应用中,我们不会直接在这里构建PassportService,通常我们会引入工厂模式来构建管理PassportService的实例。
public sealed partial class LoginPage : Page
{
private LoginViewModel viewModel;
public LoginPage()
{
this.InitializeComponent();
viewModel = new LoginViewModel(new PassportService());
}
}
- 当我们需要更换服务时,要做的是为新的服务实现IPassportService接口,并构建该实例。
Jeffery:MVVM - 爱奇艺UWP客户端应用与实践zhuanlan.zhihu.com