[PHP, Программирование, Проектирование и рефакторинг, ООП, Go] Prototype Design Pattern в Golang

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

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

Создавать темы news_bot ® написал(а)
24-Май-2021 20:31

Привет друзья! С вами Алекс и я продолжаю серию статей, посвящённых применению шаблонов проектирования в языке Golang. Интересно получать обратную связь от вас, понимать на сколько применима данная область знаний в мире языка Golang. Ранее уже рассмотрели шаблоны: Simple Factory, Singleton и Strategy. Сегодня хочу рассмотреть еще один шаблон проектирования - Prototype.Для чего нужен?Это порождающий шаблон проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации.Какую проблему решает?Представьте, у вас есть объект, который необходимо скопировать. Как это сделать? Создать пустой объект такого же класса, затем поочерёдно скопировать значения всех полей из старого объекта в новый. Прекрасно, но есть нюанс! Не каждый объект удается скопировать таким образом, ведь часть его состояния может быть приватной, а значит - недоступной для остального кода программы. Есть и другая проблема. Копирующий код станет зависим от классов копируемых объектов. Ведь, чтобы перебрать все поля объекта, нужно привязаться к его классу. Из-за этого вы не сможете копировать объекты, зная только их интерфейсы, а не конкретные классы.Какое решение?Шаблон Prototype поручает создание копий самим копируемым объектам. Он вводит общий интерфейс для всех объектов, поддерживающих клонирование. Это позволяет копировать объекты, не привязываясь к их конкретным классам. Обычно такой интерфейс имеет всего один метод clone.Реализация этого метода в разных классах очень схожа. Метод создаёт новый объект текущего класса и копирует в него значения всех полей собственного объекта. Так получится скопировать даже приватные поля, так как большинство языков программирования разрешает доступ к приватным полям любого объекта текущего класса. Объект, который копируют, называется прототипом, отсюда и название шаблона. Когда объекты программы содержат сотни полей и тысячи возможных конфигураций, прототипы могут служить своеобразной альтернативой созданию подклассов. Шаблон прототип должен копировать объекты любой сложности без привязки к их конкретным классам. Все классы-прототипы имеют общий интерфейс. Поэтому вы можете копировать объекты, не обращая внимания на их конкретные типы и всегда быть уверены, что получите точную копию. Клонирование совершается самим объектом-прототипом, что позволяет ему скопировать значения всех полей, даже приватных. В этом случае все возможные прототипы заготавливаются и настраиваются на этапе инициализации программы. Потом, когда программе нужен новый объект, она создаёт копию из приготовленного прототипа. Диаграмма классов
Prototype Class DiagramНа диаграмме видим Интерфейс прототипов, который описывает операции клонирования. В большинстве случаев - это единственный метод clone. Конкретный прототип реализует логику клонирования самого себя. Тут важно не допускать ошибку клонирования. Например, клонирование связанных объектов, распутывание рекурсивных зависимостей и прочее. Клиент создаёт копию объекта, обращаясь к нему через общий интерфейс прототипов. Все просто.Как реализовать?Подробно пошаговую реализацию данного шаблона, а также других шаблонов проектирования на языке PHP можнопосмотреть тут. Наша задача реализовать шаблон Prototype на языке Golang. Рассмотрим пример реализации рубрикатора каталога продуктов в интернет-магазине с большим количеством категорий товаров. В рубрикаторе есть разделы верхнего уровня, в которых содержатся конечные рубрики и разделы второго уровня. Разделы второго уровня также могут содержать рубрик и разделы третьего уровня и т.д. То есть это  древовидная структура объектов, у них есть стволы и листья - конечные элементы дерева. Каждая рубрика и раздел имеет свой набор свойств, относительно товаров раздела. Например, рубашки - это цвет, размер, бренд и т.д.  В какой-то момент потребовалась панель администрирования рубрикатора, чтобы копировать разделы в раздел и рубрики со всеми свойствами товаров. Например, в разделе одежды нужно быстро уметь клонировать рубрики мужские, женские, детские шорты и т.д. Каждую рубрика, как конечный элемент рубрикатора, может быть представлен интерфейсом prototype, который объявляет функцию clone. За основу конкретных прототипов рубрики и раздела мы берем тип struct, которые реализуют функции show и clone интерфейса prototype.Извините, данный ресурс не поддреживается. :( Итак, реализуем интерфейс прототипа. Далее мы реализуем конкретный прототип directory, который реализует интерфейс prototype представляет раздел рубрикатора. И конкретный прототип для рубрики. Обе структуру реализуют две функции show, которая отвечает за отображение конкретного контента ноды и clone для копирования текущего объекта. Функция clone в качестве единственного параметра принимает аргумент, ссылающийся на тип указателя на структуру конкретного прототипа - это либо рубрика, либо директория. И возвращает указатель на поле структуры, добавляя к наименованию поля _clone. В клиенте создаем древовидную структуру рубрикатора. Копируем одну из директорий со всеми дочерними элементами. Видим, что все дерево успешно скопировано благодаря реализации функция клонирования для каждого конкретного прототипа. Вывод:
Open directory 2
  Directory 2
    Directory 1
        category 1
    category 2
    category 3
Clone and open directory 2
  Directory 2_clone
    Directory 1_clone
        category 1_clone
    category 2_clone
    category 3_clone
Когда применять?
  • У вас много объектов с множеством свойств и вам нужно создавать их клоны быстро и эффективно. Шаблон предлагает использовать набор прототипов, вместо создания подклассов для описания популярных конфигураций объектов. Вместо порождения объектов из подклассов, вы копируете существующие объекты-прототипы, в которых уже настроено состояние. Тем самым избегая роста количества классов в программе и уменьшая её сложность.
  • Код не должен зависеть от классов копируемых объектов. Если ваш код работает с объектами, переданными через общий интерфейс - вы не можете привязаться к их классам, даже если бы хотели, поскольку их конкретные классы неизвестны. Прототип предоставляет клиенту общий интерфейс для работы со всеми прототипами. Клиенту не нужно зависеть от классов копируемых объектов, а только от интерфейса клонирования.
ИтогДрузья, шаблон Prototype предлагает:
  • Удобную концепцию для создания копий объектов.
  • Помогает избежать дополнительных усилий по созданию объекта стандартным путём, когда это непозволительно дорого для приложения.
  • В объектных языках позволяет избежать наследования создателя объекта в клиентском приложении, как это делает паттерн abstract factory, например.
Кстати, друзья, вот тутможно посмотреть результаты опроса читателей хабра. 63% опрошенных считают, что применение шаблонов проектирования в Golang - это зло. Связано, скорее всего, с тем, что язык Golang процедурный и ему чужды подходы объектно-ориентированных языков. Но рассматривать реализации и применение шаблонов стоит, так как это позволяет больше их понимать и периодически применять для решения тех или иных задач. Каждый подход требует, конечно, дискуссии и разумного применения. Друзья, рад был поделиться темой, Алекс. На английском статью можнонайти тут.
Удачи!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_php, #_programmirovanie (Программирование), #_proektirovanie_i_refaktoring (Проектирование и рефакторинг), #_oop (ООП), #_go, #_php, #_go, #_golang, #_prototype, #_design_patterns, #_design_pattern, #_shablony_proektirovanija (шаблоны проектирования), #_php, #_programmirovanie (
Программирование
)
, #_proektirovanie_i_refaktoring (
Проектирование и рефакторинг
)
, #_oop (
ООП
)
, #_go
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 21-Сен 12:52
Часовой пояс: UTC + 5