[Разработка веб-сайтов, PHP, Программирование, Go] Strategy Design Pattern

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
14-Апр-2021 04:30

Всем привет, друзья. Меня зовут Alex, я профессиональный разработчик и создатель программных продуктов в веб индустрии. Много лет изучаю языки, делюсь опытом с другими. Сегодня хочу с вами поговорить про шаблон проектирования Стратегия (Strategy). Постараюсь донести до вас принципы и суть шаблона без воды, и покажу как его применять на практике. В чем суть?Design patter Strategy  или шаблон проектирования Стратегия  относится к поведенческим шаблонам проектирования. Его задача - выделить схожие алгоритмы, решающие конкретную задачу. Реализация алгоритмов выносится в отдельные классы и предоставляется возможность выбирать алгоритмы во время выполнения программы.Шаблон дает возможность в процессе выполнения выбрать стратегию (алгоритм, инструмент, подход) решения задачи.  В чем проблема?Рассмотрим задачи, при решении которых можно применять такой подход.  Представьте, что перед вами стоит задача написать веб-портал по поиску недвижимости. MVP (Minimum Viable Product) или минимально работающий продукт был спроектирован и приоритизирован вашей командой Product Manager’ов и на портале должен появиться функционал для покупателей квартир. То есть целевые пользователи вашего продукта в первую очередь - это те, кто ищет себе новое жилье для покупки. Одной из самых востребованных функций должна быть возможность:
  • Выбрать область на карте, где покупатель желает приобрести жилье
  • И указать ценовой диапазон цен на квартиры для фильтрации.
Первая версия вашего портала отлично справилась с поставленной задачей и пользователи могли без проблем искать, сужая свой поиск квартир по ценовом диапазону и выбранной географической области на карте. Ну и конечно вы хорошо постарались, как разработчик и все правильно сделали на ваш взгляд с точки зрения архитектуры кода, реализовали классы, которые ищет квартиры на продажу в вашей базе. Но тут приходят к вам Product Manager'ы и говорят, что нужно добавить возможность искать и отображать недвижимость, которая сдается в аренду. У нас появляется еще один тип пользователя - арендаторы. Для арендаторов не так важно показывать фильтры по цене, им важно состояние квартиры, поэтому нужно отображать фотографии арендуемых квартир. Следующей версией вашего продукта была значительная доработку текущей архитектуры и добавление нового функционала, чтобы можно было в зависимости от роли пользователя искать разные типы недвижимости и отображать разные элементы интерфейса - для покупателей фильтры по цене и географическое расположение, для арендаторов фотографии квартир. Конечно на этом все не закончилось. В ближайших планах добавить функционал работы юридических лиц, функционал оплаты и бронирования квартир сразу на сайте. Дальше-больше - добавить возможность просматривать историю недвижимости, запрашивать пакет документов для сделки и связь с владельцами, оформление кредита и так далее.  Если функционал поиска и фильтрации с квартирами на продажу было довольно легко реализовать, то любые новые изменения вызывали много вопросов и головную боль по архитектуре. Пришлось очень многое менять в коде. Вы понимали, что любое изменение алгоритмов выдачи нужных квартир и элементов для отображения затрагивает основные базовые классы, в которых реализован весь функционал фильтрации. Основной функционал поиска квартир изначально был реализован в одном классе, при добавлении нового функционала этот класс разрастался, вы добавляли новые условия, новые ветвления, новые методы и функции. Изменение такого класса, даже в случае исправление багов, сильно повышает риск сделать новые ошибки. Над данным функционалом работала целая команда разработчиков, любые изменения в итоге проходили долгую обкатку и тестирование, а изменения, добавляемые другими программистами в базовые классы, создавала новые проблемы и конфликты, релизы затягивались. Вы столкнулись со следующими проблемами: 
  • Основной алгоритм поиска квартир был реализован в одном супер-классе
  • Алгоритм выбора и отображения элементов интерфейса был реализован в одном супер-классе
  • Изменения в этих классах, сделанные разными программистами, приводили к конфликтам и необходимости регрессивного тестирования
  • Релизы продукта затягивались, время на разработку нового функционала увеличилась в несколько раз
  • Больше времени стало уходить на разработку, тестирование, появилось множество багов.

Супер-класс с единым методом реализации алгоритма.Какое решение?В данном примере мы имеем несколько алгоритмов для одной функции: 
  • Поиск квартир с продажей
  • Поиск квартир в аренду
  • Отображение или нет различных наборов фильтров
  • Отображение различных элементов интерфейса - фотографии, кнопки бронирования, кнопки обратной связи и т.д. 
Шаблон проектирования Стратегия - решает такую задачу. Он предлагает выделить семейство похожих алгоритмов, вынести их в отдельные классы. Это позволит без проблем изменять нужный алгоритм, расширять его, сводя к минимум конфликты разработки, зависимости от других классов и функционала. Вместо того, чтобы реализовывать алгоритм в едином классе, наш класс будет работать с объектами классов-стратегиями через объект-контекста и в нужным момент делегировать работу нужному объекту. Для смены алгоритма достаточно в нужным момент подставить в контекст нужный объект-стратегию.  Чтобы работа нашего класса была одинаковой для разного поведения, у объектов-стратегии должен быть общий интерфейс. Используя такой интерфейс вы делаете независимым наш класс-контекста от классов-стратегий. 
Диаграмма классов шаблона StrategyВозвращаясь к нашему примеру с порталом по поиску недвижимости, мы должны вынести алгоритмы поиска недвижимости на продажу и аренду в отдельные классы-стратегии, где реализовать конкретные алгоритмы по поиску разных типов объектов. Аналогичное можно проделать и с классами работы с элементами интерфейса для различных видов пользователей. В классах-стратегиях, реализующих поиск объектов недвижимости, будет определен только один метод doSearch(filters), принимающий в качестве аргумента фильтры, по которым будет совершен поиск конкретных объектов с использованием конкретного алгоритма.  Несмотря на то, что каждый класс-стратегия реализует поиск по своему алгоритму, с применением необходимых фильтров (продажа, аренда, загородный дом, жилая, не жилая и т.д.), не исключено, кстати, что они будут работать с разными базами - все это для веб-интерфейса, который отображает список найденных объектов, не имеет никакого значения. После того, как пользователь выбрал интересующий его тип недвижимости в фильтрах на сайте, будет происходить запрос в контроллер на backend, с экшеном получения данных по входящим фильтрам и типам пользователя. Задача контроллера определить класс-стратегию и запросить у класса-контекста данные для отображения, передав ему известный набор фильтров. Класс-контекст в этой схеме  - это класс, которые реализует метод поиска квартир по заданным фильтрам. На диаграмме классов выше мы видим, что класс контекста определяет метод getData, и принимает аргументы filters. У него должен быть конструктор, принимающий активный в данный момент объект-стратегии и сеттер setStrategy, устанавливающий активную стратегию. Такой метод пригодится для случая, когда пользователь меняет тип искомого объекта, например, он ищет недвижимость на продажу и хочет снять квартиру. Пример реализацииНиже рассмотрим пример, как решается описанная задача на языке GOlang. Первое что сделаем - определим интерфейс с методом doSearch
Strategy.goДанный метод определяет общее поведение для конкретных алгоритмов, реализующих разные стратегии. Метод может принимать различные аргументы, позволяющие реализовать ветвления в ваших алгоритмах. В примера я передаю пользовательские фильтры с типом Map.Для реализации конкретных алгоритмов создаем два файла. В каждом файле определяется свой определяемый тип с базовым типом struct, реализующие интерфейс Strategy. Соответственно, в методы, определяемые интерфейсом для каждого алгоритма, будут передаваться пользовательские фильтры. Реализации выглядит следующим образом:
FirstAlgorithm.go
SecondAlgorithm.goПосмотрим на нашу диаграмму классов. Нам осталось реализовать класс-контекста и клиентский код вызова конкретных алгоритмов в нужным момент. Как это сделать? Для создания слоя класса-контекста реализуем исходник, реализующий:
  • определяемый тип в базовым типом struct
  • функцию initStrategy, инициализирующий стратегию по-умолчанию и пользовательские фильтры
  • метод типа struct setStrategy, устанавливающий активную стратегию
  • и функция getData, вызывающий конкретную стратегию и возвращаемый данные для показа пользователю.

Context.goПоследний шаг - посмотрим на клиентский код. Сначала инициализируется стратегия по-умолчанию, получаем данные из этой стратегии. Затем выставляем новую активную стратегию и повторно вызываем функцию получения данных getData. Как видим, каждый раз вызывается одна и та же функция файла-контекста (или класса-контекста в диаграмме классов) для получения данных рендеринга, отображаемых в дальнейшем пользователю. Для простоты я вывожу отладочную информацию в консоль, чтобы убедиться, что работают разные алгоритмы при вызовах. Вот код:
Client.goВот вывод такого подхода:First implements strategy map[role:1] Second implements strategy map[role:2] Как видите, мы можем управлять вызовом разных алгоритмов в зависимости от контекста и пользовательских фильтров. Алгоритмы могут создавать ветвления в зависимости от входных фильтров и других параметров, переданных из клиентского кода в методы, реализующие конкретные алгоритмы. Вот такой интересный пример. Объектно-ориентированный подход можно посмотреть. например, в этом курсе. Там показан пример на PHP. Когда применять?Напоследок поговорим когда применяется шаблон Strategy?
  • У вас есть множество похожих реализаций отличающихся незначительным поведением. Можно вынести отличающее поведение в классы-стратегии, а повторяющий код свести к единому классу-контекста. 
  • Ваш алгоритм реализован в супер-классе с множественными условными операторами. Выделите блоки условных операторов в отдельные классы-стратегии, а управление вызовов нужных доверьте классу-контекста. 
  • Конкретные стратегии позволяют инкапсулировать алгоритмы в своих конкретных классах. Используйте этот подход для снижения зависимостей от других классов. 
  • В зависимости от ситуации вы можете менять стратегию выполнения задачи в процессе выполнения программы. Например, в зависимости от скорости интернета использовать разные стратегии-поведения, возвращающие разный набор данных для отображения страницы.
Подведем итогДрузья, мы познакомились с поведенческим шаблоном проектирования Strategy. Шаблон используется для выделения схожих алгоритмов, решающих конкретную задачу. Посмотрели с вами реализацию на языке GOlang, ознакомились в возможностями подхода и разобрали когда его лучше применять.Рад был с вами пообщаться, [url=https://www.facebook.com/groups/201365194945690 ]Alex Versus[/url]. Успехов!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_php, #_programmirovanie (Программирование), #_go, #_go, #_golang, #_php, #_design_patterns, #_design_pattern, #_shablony_proektirovanija (шаблоны проектирования), #_strategy_pattern, #_strategija (стратегия), #_arhitektura_prilozhenij (архитектура приложений), #_arhitektura (архитектура), #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
, #_php, #_programmirovanie (
Программирование
)
, #_go
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 12:31
Часовой пояс: UTC + 5