View
1.885
Download
0
Category
Preview:
Citation preview
Шаблони проектування
Частина 2
Розглянемо
• Presentation pattern Model View Presenter
• Шаблони поведінки• Структурні шаблони• Деякі вже відомі нам шаблони
Model View Presenter
Призначення
Розділення • Даних для показу• Логіки програми• Відображення даних
View Model
Presenter
updates
fetches
change
request
displays
Сповіщає Presenter про
дії користувача,відображає
дані
Завантажує дані, передає дані на View, реагує на дії
користувача
Дані для показу
View Model
PresenterUI
DomainData
Access
Відповідальності: Model
• Контейнер для даних для відображення• Supervising Controller: View знає про
Модель• Passive View: View не знає про Модель• Також може бути класом з предметної
області
Відповідальності: View
• Є формою, показує UI• Створює Презентер, передає себе йому• Перенаправляє дії користувача
Презентеру• Може знати про Модель• Або може надавати детальний
інтерфейс для встановлення даних• Звертається до конкретного
Презентера
Відповідальності: Presenter
• Реагує на дії користувача• Оновлює View даними з Моделі• Позбавляє View відповідальності за
логіку та взаємодію з бізнес-класами та data access– Презентер отримує дані з бізнес-рівня– Трансформація/фільтрування даних– Презентер зберігає зміни, зроблені
користувачем
• Звертається до View через інтерфейс IView
Модифікації
• Supervising Controller– View відповідає за data binding Моделі– Презентер передає Модель на View– Презентер керує складнішими
взаємодіями
• Passive View– Презентер відповідає за передачу
кожної частини даних на View– View лише відображає передані
прості дані
Застосовується
• Windows Forms– Існують розвинені MVP фреймворки
• ASP.NET Web Forms
Strategy
public sealed class SurveysImporter { public void Import() { // ... initialize import here switch (GetFileStorageType(importContext)) { case SurveysStorageType.Xls: ImportFromExcel(); break; case SurveysStorageType.Csv: ImportFromCsvFile(); break; case ... default: DoNothingButLog(); break; } // ... report import completed successfully }
public sealed class SurveysImporterWithStrategy{ private readonly IFileImporterStrategy fileImporterStrategy; public void Import() { // ... initialize import here fileImporterStrategy.Import(); // ... report import completed successfully }
Призначення
• Інкапсуляція алгоритму• Зміна та розвиток алгоритму
окремо від клієнта• Винесення наборів різної
поведінки з класу• Усунення умовних операторів• Уникнення зміни класу при
додаванні нового алгоритму
Реалізація
• Створення класу-стратегії для кожної варіації алгоритму
• Створення спільного інтерфейсу для всіх цих алгоритмів
• Стратегія може використовувати клас-клієнт
• Або інший контекст
public class FilesSender{ public void SendToServer(Directory directory, Context context) { if (context.SendTopLevelFilesFirst) { foreach (var file in directory.Files) { SendToServer(file); } foreach (var childDirectory in directory.Directories) { SendToServer(childDirectory, context); } }
Higher order functions
• Є стратегіями
public class FilesSender{ private readonly Func<Directory, IEnumerable<File>> enumerateFiles;
public FilesSenderWithStrategy( Func<Directory, IEnumerable<File>> directoryTraverser) { enumerateFiles = directoryTraverser; }
public void SendToServer(Directory directory, Context context) {
foreach (var file in enumerateFiles(directory)) { SendToServer(file); } }
Template Method
public class SurveysImporter{ public void Import() { InitializeImport(); DoImport(); NotifyUsers(); ReportSuccessfullCompletion(); }
public class CsvSurveysImporter : SurveysImporterWithTemplateMethod{ protected override void DoImport() { // CSV import implementation here }
protected override void NotifyUsers() { // users notification implementation here }}
Призначення• Алгоритм складається з декількох
кроків• Реалізація одного кроку може
змінюватись• (або декількох кроків)• Інкапсуляція незмінної послідовності
кроків• Можливість задати реалізацію кроків,
що можуть змінюватись• Зміна кроків алгоритму без зміни його
структури
Реалізація
• Підкласи перевизначають окремі кроки алгоритму, визначеного в базовому класі
• Базується на наслідуванні• Альтернатива – Strategy
Приклад
• ASP.NET page lifecycle– OnInit(), OnLoad() etc.
Command
private void Import(Survey survey){ if (survey.CreatedBy == currentUser && survey.CreatedOn < DateTime.Today && ( new[] { SurveyState.New, SurveyState.Finished } .Contains(survey.State) || survey.State == SurveyState.Paused && !survey.HasAnswers ) && !AlreadyExistsSurveyWithTitle(survey.Title)) { // create new or update existing survey }}
Призначення
• Представлення дії як об’єкта• Відділення виконання дії від
деталей і залежностей, необхідних для реалізації дії
• Додавання нових дій без зміни клієнтів
public interface ICommand{ void Execute();} Не приймає
аргументів
public interface ICommand<T>{ void ExecuteFor(T obj);}
public class ImportSurveyCommand : ICommand<Survey>{ private readonly string currentUser; private readonly object importContext;
public ImportSurveyCommand(string currentUser, object importContext) { this.currentUser = currentUser; this.importContext = importContext; }
public void ExecuteFor(Survey obj) { // create new or update existing survey }}
private void Import(Survey survey){ if (survey.CreatedBy == currentUser && ...) { var importSurvey = new ImportSurveyCommand( currentUser, GetImportContext()); importSurvey.ExecuteFor(survey); }}
public interface ICommand{ bool CanExecute(); void Execute(); void Undo();}
Команда може використовуватись для undo
функціональності
Specification
private void Import(Survey survey){ if (survey.CreatedBy == currentUser && survey.CreatedOn < DateTime.Today && ( new[] { SurveyState.New, SurveyState.Finished } .Contains(survey.State) || survey.State == SurveyState.Paused && !survey.HasAnswers ) && !AlreadyExistsSurveyWithTitle(survey.Title)) { // create new or update existing survey }}
Передумови
• Бізнес-правило містить багато коду• Правило логічно складне• Правило може часто змінюватись• Правил може бути декілька
• Призначення: спрощення коду класу-клієнта
public interface ISpecification<T>{ bool IsSatisfiedBy(T obj);}
public class SurveyShouldBeImported : ISpecification<Survey>{ public bool IsSatisfiedBy(Survey obj) { // ... }}
Реалізація
• Інкапсулює булеве бізнес-правило• Можливі композитні Специфікації– AndSpecification, OrSpecification,
NotSpecification
• Можливе поєднання Команд та Специфікацій
public class AndSpecification<T> : ISpecification<T>{ private readonly ISpecification<T> first; private readonly ISpecification<T> second;
public AndSpecification(ISpecification<T> first, ISpecification<T> second) { this.first = first; this.second = second; }
public bool IsSatisfiedBy(T obj) { return first.IsSatisfiedBy(obj) && second.IsSatisfiedBy(obj); }}
Adapter
Призначення
• Класи-клієнти використовують певний інтерфейс
• Потрібне перетворення інтерфейсу одного класу в інтерфейс іншого
• Для використання існуючих класів • Адаптує один інтерфейс до іншого
Прикладpublic interface ISurveyRepository{ IList<Survey> GetAll(); Survey Get(int id);}
Decorator
Призначення
• Зміна поведінки об’єкта без зміни інтерфейсу
• Додання функціональності динамічно
• Приклад: Додавання валідації
public interface ISurveysImporter{ void Import();}
public sealed class SurveysImporter : ISurveysImporter{ ... public void Import() { // ... initialize import here fileImporterStrategy.Import(); // ... report completed successfully }}
public sealed class SecurityCheckingSurveysImporter : ISurveysImporter { private readonly ISurveysImporter importer;
public void Import() { if (GetCurrentUserRole() != "Admin") { throw new InvalidOperationException( "User is not allowed to import surveys."); } importer.Import(); }...
Реалізація• Декоратор реалізує інтерфейс
початкового об’єкту• Декоратор зберігає посилання на
початковий об’єкт• Початковий об’єкт не знає про
додаткову функціональність• Декоратори можуть поєднуватись• Композиція замість наслідування
Вже відомі шаблони• Iterator– IEnumerable<T> + IEnumerator<T>– yield return -- компілятор сам генерує
ітератор
• Observer– C# events, based on delegates
• Proxy– Доступ до веб-сервісів
• MVC• Repository
Recommended