Мы можем оптимизировать паттерн Pipeline & Filter (конвейер и фильтры), сократив количество кода, необходимое на его реализацию, используя лямбда-выражение (упрощенную запись анонимных методов) в качестве конкретного условия фильтрации. В качестве примера для демонстрации этой концепции, было выбрано WPF-приложение с пользовательским интерфейсом. Вот его исходный код.
Паттерн Pipeline & Filter
Обычно в рамках этого паттерна для каждого нового фильтра реализуется интерфейс. Вместо того чтобы реализовывать интерфейс для каждого условия фильтрации, в качестве входных данных для конвейера фильтров можно использовать обобщенное (generic) лямбда-выражение. В результате кода станет меньше. Ниже представлена диаграмма классов:
ConditionAggregator
- это конвейерный класс, в
котором хранится коллекция Condition<T>
.
Filter<T>
владеет
ConditionAggregator
или
Condition<T>
для применения условий (condition)
фильтрации к набору данных. Когда вызывается функция apply
(применить) Filter<T>
, выполняется метод check
(проверить) ICondition<T>
.
ConditionAggregator<T>
имеет событие
OnFilterChanged
. Оно срабатывает, когда в классах
модели представления изменяется значение коллекции или условия. В
следующем разделе будет описано использование паттерна Filter
моделью представления.
Код
Использование в модели представления
Объяснение паттерна MVVM можно найти по этой
ссылке. Одна из обязанностей модели представления (View Model)
в MVVM - обрабатывать взаимодействие с пользователем и изменения
данных. В нашем случае изменения значений условий фильтрации должны
быть переданы на бизнес-уровень, чтобы применить фильтры к
определенной коллекции данных. Изменение значения условия в модели
представления стригерит событие
ConditionAggregator<T>
OnFilterChanged
, на которое подписан метод фильтра
apply. Ниже приведена диаграмма классов модели представления.
Класс сущности Employee
создан для хранения
информации о сотрудниках. Generic
тип T
паттерна проектирования Filter
будет заменен классом
Employee
. EmployeeList
содержит список
данных о сотрудниках и применяемые фильтры. Конструктор класса
получает список условий и переходит к списку фильтров.
public EmployeeList(IEmployeesRepository repository, ConditionAggregator<employee> conditionAggregator) { this.repository = repository; this.filters = new ConcreteFilter<employee>(conditionAggregator); conditionAggregator.OnFilterChanged += this.FilterList; _ = initAsync(); }
Метод FilterList
подписан на событие
OnFilterChanged
для применения фильтров к данным,
когда происходит изменение условия или значения.
private void FilterList(){ this.Employees = this.filters.Apply(this.employeesFullList);}
EmployeesViewModel
подключен к пользовательскому
интерфейсу. В этом примере продемонстрирован только один фильтр
свойства EmployeeTypeselected
, но в
ConditionAggregator
можно передать множество фильтров.
Следующий фрагмент кода - это метод-конструктор, в котором
регистрируется условие фильтра.
public EmployeesViewModel(IEmployeesRepository repository) { this.repository = repository; Condition<employee> filterEmployee = new Condition<employee>((e) => e.employeeCode == this.EmployeeTypeSelected); this.conditionAggregator = new ConditionAggregator<employee>(new List<condition<employee>> { filterEmployee }); this.EmployeeList = new EmployeeList(repository, this.conditionAggregator); } </condition<employee></employee></employee></employee>
Условие передается как лямбда-выражение. Их может быть сколько
угодно, поскольку конструктор ConditionAggregator
принимает список условий фильтрации. Основная цель уменьшить код,
необходимый для создания определенного класса условий фильтрации,
достигнута.
Архитектура приложения
WPF.Demo.DataFilter
- это представление
пользовательского интерфейса WPF. Он имеет одну сетку и одно поле
со списком для фильтрации. Проект
WPF.Demo.DataFilter.ViewModels
обрабатывает данные,
фильтрует изменения и перезагружает данные для обновления
пользовательского интерфейса. Проект
WPF.Demo.DataFilter.Common
представляет собой полную
реализацию шаблона Pipeline & Filter.
WPF.Demo.DataFilter.DAL
загружает простенький
json-файл в качестве хранилища данных.
Это основной интерфейс:
Перевод статьи был подготовлен в преддверии старта курса "Архитектура и шаблоны проектирования". А прямо сейчас приглашаем всех желающих на бесплатный демо урок в рамках которого обсудим назначение и структуру шаблона "Интерпретатор", формы Бекуса-Науэра, лексический, синтаксический и семантический анализы, а также разберем практические примеры. Записаться на урок можно по ссылке.