Я собираюсь начать создание большого приложения, которое будет выполнять различные методы строкового шифрования в текстовых файлах.
Это в конечном итоге будет довольно большим приложением, поэтому я хочу быть уверенным, что дизайн, который у меня есть, хорош с самого начала. Я рассмотрел шаблон модели Model-View-View Model, так как не будет данных, передаваемых обратно в представление, я не думаю, что это лучший шаблон для использования.
Цель приложения - буквально просто выполнить строковый метод шифрования в произвольных текстовых файлах, а затем отобразить окно сообщения, которое будет давать только сообщение об успешном завершении или сбое, основанное на том, был ли процесс успешным или нет.
Является ли MVC более подходящей моделью здесь? Я знаю, что это довольно редко, и MVVM, по-видимому, является образцом выбора, но, опять же, я чувствую, что, поскольку данные не передаются обратно в представление о том, что MVVM не подходит.
Интерфейс для этого приложения будет всего лишь несколькими текстовыми полями, которые используются для выбора метода шифрования и кнопки, которая при нажатии будет выполнять соответствующий метод шифрования.
Добро пожаловать в StackOverflow! Я постараюсь ответить на ваш первый вопрос как можно лучше: D
Из того, что я прочитал, я собирался броситься на клавиатуру и просто сказать вам: "Да, пойдите, чтобы помахать", но вместо этого я решил создать вам действительно маленький пример того, что может принести MVVM-шаблон вы.
Лично я был немного похож на вас, скептически относился к преимуществам и дополнительной работе, которые он мог понести; но позвольте мне сказать вам, что это действительно того стоит. Только недавно я принял образец, и я не могу перестать думать, почему я этого не сделал раньше? Это такая экономия времени, сохраняет озабоченности разделенными и действительно простыми.
Так вот пример!
Теперь шаблон MVVM
Здесь схема взаимодействия компонентов, обратите внимание, что это цепочка:
Просмотреть <-> ViewModel <-> Модель
(вы скоро получите преимущества от этого)
Модель: это ядро того, что делает ваше приложение, для этого примера - компонент, который шифрует данные. Это ваш низкоуровневый класс, который занимается процессом шифрования, он не должен знать о ViewModel и представлении. Его единственная проблема заключается в шифровании входных данных и выводе их, ничего другого!
public class EncryptorModel
{
public string Cipher(string text)
{
char[] enumerable = text.Select(s => ++s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
}
ViewModel (вид вашей модели): теперь это становится интересным, в то время как преимущества этого компонента на первый взгляд очевидны, они действительно присутствуют, и я попытаюсь продать их вам: D
См. ViewModel в качестве шлюза между представлением и моделью, его задача состоит в обслуживании запросов, поступающих от представления (пользователя), путем выполнения операций над моделью и возврата результатов, отправленных моделью обратно в представление.
Как вы можете видеть ниже: он содержит свойства для этого примера шифрования; передает данные из представления в/из модели (автоматически благодаря привязке данных 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 представляет упрощенное представление Модели, оно показывает только то, что требуется View и выполняет команды на модели
Модель - это строго ваше профессиональное поле, задача заключается в шифровании/расшифровке, ничего больше!
Окончательный вывод: проблемы остаются разделенными с использованием MVVM, приложения, использующие этот шаблон, просты в обслуживании. Для меня это не было очевидно, если я не провел 3 дня с MVVM, поэтому я могу только поощрять вас сделать это, ваши проекты программирования явно выиграют от этого.
Используемая среда:
Я использовал Galasoft MVVM Light: http://www.mvvmlight.net/installing/nuget/
(только библиотеки библиотеки MVVM Light)
Я добровольно пропустил часть локатора службы, вы найдете ее в шаблонах проекта Visual Studio здесь: http://www.mvvmlight.net/installing
Несколько ViewModels:
Правило должно иметь один ViewModel для каждого представления (или пользовательский интерфейс), поэтому, если ваше приложение. имеет 2 окна, тогда у вас будет 2 ViewModel.
Разделите модель между двумя или более ViewModels:
Если, например, вы можете подтвердить, что оба окна могут работать на одной и той же модели, тогда вы можете иметь только 1 модель, и вместо этого вы App.xaml
ее в 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
и выбран 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, привязанную к определенному интерфейсу пользовательского интерфейса, например 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>
Вывод
Итак, как вы можете видеть, я немного пытался реализовать новые типы, но это окупается, каждый метод шифрования является независимым, а также шифр, который хорош. В конце концов, шифр не является методом шифрования, поэтому они лучше разделены.