我目前正在将我的代码重构为符合SOLID。 SOLID原则已成为大多数开发人员的习惯,而我却忽略了学习该原则。但是现在我需要它!
我的ViewModel包含事件,字段,属性(ObseravbleCollection和其他数据类型),ICommand,为特定视图创建的方法,并且只有当该集合将在其他视图中使用时,某些ViewModel才包含属性和方法。因此,对于某些列表,它更像是可重用的VM。即用于管理员工的视图和用于出于其他目的列出员工的视图。
我正在尝试使用WPF理解并制作一个简单的Login UI,而我的ViewModel看起来像这样。
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using UnderstandingSOLID.Models;
using UnderstandingSOLID.Services.API;
namespace UnderstandingSOLID.ViewModels
{
public class ViewModel_Login : VMBase
{
#region events
public EventHandler OnLoggedIn;
#endregion
#region vars
#endregion
#region properties
public ObservableCollection<Model_ServerType> ServerTypes { get; set; } = new ObservableCollection<Model_ServerType>();
private Model_ServerType _SelectedServerType = new Model_ServerType();
public Model_ServerType SelectedServerType
{
get { return _SelectedServerType; }
set
{
Set(nameof(SelectedServerType), ref _SelectedServerType, value);
}
}
private string _ServerName = null;
public string ServerName
{
get { return _ServerName; }
set { Set(nameof(ServerName), ref _ServerName, value); }
}
private string _Username = null;
public string Username
{
get { return _Username; }
set { Set(nameof(Username), ref _Username, value); }
}
private string _Password = null;
public string Password
{
get { return _Password; }
set { Set(nameof(Password), ref _Password, value); }
}
private bool _ShowLoginUI = false;
public bool ShowLoginUI
{
get { return _ShowLoginUI; }
set { Set(nameof(ShowLoginUI), ref _ShowLoginUI, value); }
}
#endregion
#region commands
public ICommand Command_Connect { get; set; }
public ICommand Command_Help { get; set; }
#endregion
#region ctors
public ViewModel_Login()
{
InitCommands();
// used only in UWP & WPF
// or anything that supports design time updates
if (base.IsInDesignMode)
{
DesignData();
}
}
#endregion
#region command methods
void Command_Connect_Click()
{
Connect();
}
void Command_Help_Click()
{
// does nothing at the moment
}
#endregion
#region methods
void InitCommands()
{
if (Command_Connect == null) Command_Connect = new RelayCommand(Command_Connect_Click);
if (Command_Help == null) Command_Help = new RelayCommand(Command_Help_Click);
}
/// <summary>
/// codes here are messy since they are only used for desining the UI
/// </summary>
void DesignData()
{
this.ShowLoginUI = true;
this.ServerTypes.Clear();
string[] serverTypes = new string[]
{
"Database Engine",
"Analysis Services"
};
for (int i = 0; i < serverTypes.Length; i++)
{
this.ServerTypes.Add(new Model_ServerType()
{
Id = i,
ServerType = serverTypes[i]
});
}
this.ShowDlgMsg("Title", "Failed to login");
}
// this is called in Loaded event in MainPage.xaml.cs
public async Task RefreshData()
{
this.ServerTypes.Clear();
string[] serverTypes = new string[]
{
"Database Engine",
"Analysis Services",
"Reporting Services",
"Integration Services"
};
for(int i = 0; i < serverTypes.Length; i++)
{
this.ServerTypes.Add(new Model_ServerType()
{
Id = i,
ServerType = serverTypes[i]
});
}
}
async Task Connect()
{
if(this.SelectedServerType.ServerType != null && await ApiClient.I.Login(this.Username, this.Password))
{
this.HideDlgMsg();
// navigate to main page
// but we'll just Invoke an event for simplicity
this.OnLoggedIn?.Invoke(this, null);
}
else
{
this.ShowDlgMsg("Error", "Failed to login");
}
}
#endregion
}
}
VMBase看起来像这样
using GalaSoft.MvvmLight;
namespace UnderstandingSOLID.ViewModels
{
public abstract class VMBase : ViewModelBase
{
private bool _ShowMessage = false;
public bool ShowMessage
{
get { return _ShowMessage; }
set { Set(nameof(ShowMessage), ref _ShowMessage, value); }
}
private string _MessageTitle = null;
public string MessageTitle
{
get { return _MessageTitle; }
set { Set(nameof(MessageTitle), ref _MessageTitle, value); }
}
private string _MessageBody = null;
public string MessageBody
{
get { return _MessageBody; }
set { Set(nameof(MessageBody), ref _MessageBody, value); }
}
public virtual void ShowDlgMsg(string title, string message)
{
this.ShowMessage = true;
this.MessageTitle = title;
this.MessageBody = message;
}
public void HideDlgMsg()
{
this.ShowMessage = false;
}
}
}
因此,这就是我通常在ViewModel中编写所有成员的方式。不用介意我如何在ViewModel_Login中的Connect方法中调用Login。只是简化了。
那么在这种情况下您如何实现SOLID原理?
编程原理就像盗版代码:与实际规则相比,它们更像是准则,它们并不总能改进。
您的VM看起来不错-这就是您执行ViewModels的方式。我使用不同的框架,所以我会做一些不同的事情,但是:
APIClien
t应该是一个依赖项,实例应该已经在构造函数中作为实例传递,并且接口od IAPIClient
与静态属性相对应IViewModel_Login
,包含该视图使用的所有公共方法,属性和事件(实际上可以用来制作您可以指定的ViewModel_Login_Design
类在XAML中仅返回一些静态值[有d:DataContext])VMBase
的消息-也许不在基础中使用它,而是将其注入使用它的ViewModels中。 Base应该只具有几乎所有ViewModel都通用的东西。我还将在此处链接到ReactiveUI,因为我坚信其设计以及围绕IObservable
的关注点以及将DynamicData用于动态集合是远远优于其他任何MVVM框架的经验。