我即将开始构建一个大型应用程序,它将对文本文件执行不同的字符串加密方法。
这最终将成为一个相当大的应用程序,所以我想确保我的设计从一开始就很好。我已经看过模型 - 视图 - 视图模型设计模式,但由于没有数据被传递回视图,我不认为这是最好的模式。
应用程序的目的实际上是对任意文本文件执行字符串加密方法,然后显示一个消息框,该消息框仅根据进程是否成功提供标准成功或失败消息。
MVC在这里是更合适的模式吗?我知道这是相当罕见的,并且MVVM似乎是首选模式,但我再次感觉因为没有数据被传递回MVVM是不合适的视图。
此应用程序的界面只有几个用于选择加密方法的文本框和一个按钮,单击该按钮将执行相应的加密方法。
答案 0 :(得分:4)
欢迎来到StackOverflow!我会尽力回答你的第一个问题:D
从我读过的内容来看,我就要冲上键盘,然后告诉你:"是的,去吹嘘" 而不是我'我们决定为您提供MVVM模式可以为您带来哪些好处的一个小例子。
就个人而言,我之前感觉有点像你,对它可能带来的好处和额外工作持怀疑态度;但是,让我告诉你,它真的值得。直到最近我才采用这种模式,我无法停止思考为什么我以前没有这样做过?这样可以节省时间,让问题分离,并且非常简单。
所以这就是示例!
现在是MVVM模式
这里是组件如何通信的图表,注意它是一个链:
查看< - > ViewModel< - >模型
(你很快就会从中受益)
模型:它是您的应用程序所做的核心,对于此示例来说,它是加密数据的组件。这是处理加密过程的低级类,它不必了解ViewModel和View。它唯一关心的是加密输入数据并输出它,没有别的!
public class EncryptorModel
{
public string Cipher(string text)
{
char[] enumerable = text.Select(s => ++s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
}
ViewModel(模型的视图):现在它变得有趣了,虽然这个组件的好处乍一看并不明显,但它们实际存在并且我已经#39 ;我会试着把它们卖给你:D
将ViewModel视为View和Model之间的网关,它的工作是通过对Model执行操作并将Model发送的结果返回给View来提供来自View(用户)的请求。
如下所示:它承载此加密示例的属性;从View向/从模型传递数据(自动感谢WPF数据绑定)。最后,它托管View触发的命令。
public class EncryptorViewModel : ViewModelBase
{
private RelayCommand _cipher;
private string _inputText;
private string _outputText;
public EncryptorViewModel()
{
Model = new EncryptorModel();
}
private EncryptorModel Model { get; set; }
#region Public properties
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
public string OutputText
{
get { return _outputText; }
set
{
_outputText = value;
RaisePropertyChanged();
}
}
#endregion
#region Commands
public RelayCommand Cipher
{
get { return _cipher ?? (_cipher = new RelayCommand(CipherExecute, CipherCanExecute)); }
}
private void CipherExecute()
{
OutputText = Model.Cipher(InputText);
}
private bool CipherCanExecute()
{
return !string.IsNullOrWhiteSpace(InputText);
}
#endregion
}
视图:除了它在ViewModel中显示您的应用程序和调用命令外,没什么可说的。
<Window x:Class="WpfApplication1.EncryptorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="EncryptorView"
Width="165"
Height="188">
<Window.Resources>
<wpfApplication1:EncryptorViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModel}}">
<StackPanel>
<TextBlock Text="Input text" />
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Output text" />
<TextBox Text="{Binding OutputText}" />
<Button Command="{Binding Cipher}" Content="Cipher" />
</StackPanel>
</Grid>
</Window>
我可以而且已经做了更长的答案但取消了保留简单,你现在应该保留的内容:
View只绑定用户需要查看/操作的属性,并绑定到用户需要在模型上执行的命令
ViewModel代表Model的简化视图,它只显示View需要的内容并在Model上执行命令
模特严格来说是你的专业领域,它的工作就是加密/解密,别无其他!
最终结论:关注点仍然使用MVVM分离,使用此模式的应用程序易于维护。除非我花了3天时间与MVVM合作,否则对我来说并不是很明显,所以我只能鼓励你这样做,你的编程项目显然会从中受益。
使用的环境:
我使用了Galasoft MVVM Light:http://www.mvvmlight.net/installing/nuget/
(仅限 MVVM Light库包)
我已经自愿省略了服务定位器部分,您可以在Visual Studio项目模板中找到它:http://www.mvvmlight.net/installing
多个ViewModels:
规则是每个View(或用户界面)有一个ViewModel,所以如果您的应用程序。有2个窗口,那么你将有2个ViewModel。
在2个或更多ViewModel之间共享模型:
例如,如果您可以证明两个窗口可以在同一个模型上工作,那么您只能拥有1个模型,而是在App.xaml
中声明它:
的App.xaml:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
StartupUri="EncryptorView.xaml">
<Application.Resources>
<wpfApplication1:EncryptorModel x:Key="Model1"/>
</Application.Resources>
</Application>
EncryptorViewModel1:
public class EncryptorViewModel1 : ViewModelBase
{
//private EncryptorModel Model { get; set; }
public EncryptorViewMode1l()
{
// Model = new EncryptorModel();
// Now you retrieve the model in App.xaml instead of declaring a private one above
var model =(EncryptorModel) Application.Current.FindResource("Model1");
}
}
场景:在同一个视图中使用多个加密器
这是另一个小例子,向您展示如何让用户在同一个视图中选择加密方法。
我们使用相同的ViewModel,
AvailableEncryptors
和CurrentEncryptor
属性CipherCanExecute
以便考虑到CurrentEncryptor
,用户只有在设置InputText
且选择了加密器时才能加密CipherExecute
也有所改变,EncryptorModel
密码根据指定的字符串和加密器更新了ViewModel:
public class EncryptorViewModel : ViewModelBase
{
private RelayCommand _cipher;
private IEncryptor _currentEncryptor;
private string _inputText;
private string _outputText;
public EncryptorViewModel()
{
Model = new EncryptorModel();
}
private EncryptorModel Model { get; set; }
public IEnumerable<IEncryptor> AvailableEncryptors
{
get
{
Type type = typeof (IEncryptor);
IEnumerable<IEncryptor> encryptors =
Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract)
.Select(s => (IEncryptor) Activator.CreateInstance(s));
return encryptors;
}
}
public IEncryptor CurrentEncryptor
{
get { return _currentEncryptor; }
set
{
_currentEncryptor = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
#region Public properties
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
public string OutputText
{
get { return _outputText; }
set
{
_outputText = value;
RaisePropertyChanged();
}
}
#endregion
#region Commands
public RelayCommand Cipher
{
get { return _cipher ?? (_cipher = new RelayCommand(CipherExecute, CipherCanExecute)); }
}
private void CipherExecute()
{
OutputText = Model.Cipher(CurrentEncryptor, InputText);
}
private bool CipherCanExecute()
{
return CurrentEncryptor != null && !string.IsNullOrWhiteSpace(InputText);
}
#endregion
}
注意:您可能想要完全删除模型并在ViewModel中执行所有操作,但不要这样做,即使ViewModel只是充当网关而且您不应该抓住机会实施模型中的加密相关逻辑而不是ViewModel。
如果将事物分开,将来可能会节省大量时间,例如,如果您需要应用程序的命令行版本,则只需使用模型,因为所有必要的逻辑都在那里,没有通过它和ViewModel分散。 (请参阅ViewModel绑定到特定的UI框架,如WPF)
然后我更新模型以在需要加密时调用加密器:
public class EncryptorModel
{
public string Cipher(IEncryptor encryptor, string text)
{
return encryptor.Cipher(text);
}
}
最后,我实施了加密器:
public interface IEncryptor
{
string Description { get; }
string Cipher(string text);
}
public class Encryptor1 : IEncryptor
{
#region IEncryptor Members
public string Description
{
get { return "Encryptor 1"; }
}
public string Cipher(string text)
{
char[] enumerable = text.Select(s => ++s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
#endregion
}
public class Encryptor2 : IEncryptor
{
#region IEncryptor Members
public string Description
{
get { return "Encryptor 2"; }
}
public string Cipher(string text)
{
char[] enumerable = text.Select(s => --s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
#endregion
}
更新后的观点:
<Window x:Class="WpfApplication1.EncryptorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="EncryptorView"
Width="165"
Height="188">
<Window.Resources>
<wpfApplication1:EncryptorViewModel x:Key="ModelView" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ModelView}}">
<StackPanel>
<TextBlock Text="Select an encryptor" />
<ComboBox ItemsSource="{Binding AvailableEncryptors}" SelectedItem="{Binding CurrentEncryptor}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="wpfApplication1:IEncryptor">
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="Input text" />
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Output text" />
<TextBox Text="{Binding OutputText}" />
<Button Command="{Binding Cipher}" Content="Cipher" />
</StackPanel>
</Grid>
</Window>
<强>结论强>
正如你所看到的,我已经在努力实现新类型,但它得到了回报,每种加密方法都是独立的,加密器也是好的。毕竟,加密器不是加密方法,因此最好将它们分开。