0 3.9K ru

Базовые паттерны из каталога PoEAA

Шлюз (Gateway)

шлюз паттерн

Шлюз — объект, инкапсулирующий доступ к внешней системе или источнику данных

Назначение

Шлюз рекомендуется применять во всех случаях, когда интерфейс доступа к внешнему источнику слишком неудобен. Наличие шлюза позволяет сконцентрировать все неудобные обращения в одном месте вместо того, чтобы распространять их по всей системе. Применение шлюза не несет побочных эффектов, а код приложения становится более читабельным и понятным.

Как уже отмечалось, шлюз значительно облегчает тестирование, поскольку представляет собой потенциальную точку внедрения фиктивных служб. Даже если с интерфейсом внешней системы все в порядке, использование шлюза позволяет сделать первый шаг в направлении реализациификтивной службы.

Не менее важным преимуществом шлюза является возможность легко переключаться между источниками данных. Дня перехода к другому источнику достаточно просто изменить класс шлюза — оставшейся части системы это не коснется. Таким образом, шлюз представляет собой простое и мощное средство инкапсуляции изменений. Иногда потребность в наличии подобной степени гибкости, а следовательно, и в реализации шлюза кажется спорной. Тем не менее, даже если вы не собираетесь менять источник данных в обозримом будущем, вы несомненно выиграете от простоты написания и тестирования кода, которую обеспечивает данное типовое решение.

В качестве альтернативного варианта изолирования приложений от внешних источников может применяться паттерн Mapper. Однако маппер имеет более сложную структуру, нежели шлюз.

Mapper

Mapper — объект, устанавливающий взаимодействие между двумя независимыми объектами.

mapper

Основное назначение преобразователя состоит в отделении друг от друга различных частей программной системы. Похожие функции выполняет и шлюз который мы описали выше. Шлюз применяется гораздо чаще, чем маппер, поскольку он намного проще и в написании, и в последующем использовании.

Таким образом, маппер необходимо использовать только тогда, когда ни одна из отображаемых систем не должна зависеть от взаимодействия с другой системой. Это действительно важно только в том случае, когда структура взаимодействия особенно сложна и практически не связана с основным назначением каждой системы. Поэтому в корпоративных приложениях главной областью применения маппера является обслуживание взаимодействий с базой данных который выражается отдельным паттерном Data mapper.

Layer Supertype

Тип, выполняющий роль суперкласса для всех классов своего слоя. Довольно часто одни и те же методы дублируются во всех объектах слоя. Чтобы избежать повторений, все общее поведение можно вынести в Layer Supertype.

Типичным примером может стать IdableEntity, к примеру у вас есть несколько доменных классов и вы создали IdableEntity класс, в который поместили проперти айди, который общий для всех доменных объектов.

Отделенный интерфейс (Separated Interface)

Separated Interface предполагает размещение интерфейса и его реализации в разных пакетах.

seperated interface

При разработке какой-либо системы, можно добиться улучшение её архитектуры, уменьшая связанность между её частями. Это можно сделать так - распределив классы по отдельным пакетам и контролировать зависимости этими пакетами. Тогда можно следовать правилам о том, как классы из одного пакета могут обращаться к классам из другого пакета. Например, то, которое запрещает классам с уровня данных обращаться к классам с уровня представления.

Назначение

Отделенный интерфейс применяется для того, чтобы избежать возникновения зависимостей между двумя частями системы. Наиболее часто подобная необходимость возникает в описанных ниже ситуациях.

  • Если вы разместили в пакете инфраструктуры абстрактный код для обработки стандартных случаев, который должен вызывать конкретный код приложения.
  • Если код одного слоя приложения должен обратиться к коду другого слоя, о суще ствовании которого он не знает (например, когда код домена обращается к Data Mapper.
  • Если нужно вызвать методы, разработанные кем-то другим, но вы не хотите, что бы ваш код зависел от интерфейсов API этих разработчиков.

Registry (Реестр)

registry

Хорошо известный объект, который используется другими объектами для получения общих объектов и сервисов.

Когда нужно найти какой-нибудь объект, обычно начинают с другого объекта, связанного с целевым. Например, если нужно найти все счета для покупателя, начинают, как раз с покупателя и используют его метод получения счетов. Тем не менее, в некоторых случаях нет подходящего объекта, с которого начать. Например, известен ID покупателя, но нет ссылки на него. Тогда нужен своего рода объект-поисковик, но тогда возникает вопрос - как вы найдёте сам поисковик?

Реестр (Registry) — это глобальный объект по сути своей или, по крайней мере, так выглядит - он может функционировать только будучи глобальным.

Объект-значение (Value Object)

Работая с объектными системами различных типов, необходимо четко осознавать разницу между ссылочными объектами и объектами-значениями. Последние обычно представляют собой небольшие объекты; они напоминают элементарные типы данных (например диапазон дат, деньги и тд), присутствующие во многих языках, которые не являются исключительно объектноориентированными.

Примеры Value Object приведены ниже в паттерне Money.

Примечение: ООП языки, такие как C# имеют уже встроенную реализацию этого паттерна, а именно - структуры (ключевое слово struct).

Деньги (Money)

Как говорит Фаулер:

Огромное количество компьютеров в мире обрабатывают данные о деньгах. Удивительно, что класс Деньги до сих пор не является базовым в любом языке программирования. Недостаток такого рода типа данных приводит к проблемам, наиболее заметные из которых - работа с валютой. Если все вычисления в программе проделываются в одной валюте, никаких особых проблем нет, но как только вводится многовалютность - надо думать о том, чтобы не сложить 10 долларов с 10 йенами без перевода курсов валют. Менее заметна проблема с округлением. Денежные вычисления часто округляют до наименьшей из существующих мер. При этом легко не учесть копейки из-за ошибок округления.

Что действительно хорошо в ООП, так это то, что вы можете исправить эти проблемы, созданием класса Money (Деньги), чтобы работать с денежными величинами и избегать общих ошибок.

Схематически класс "деньги" можно отобразить следующим образом:

money pattern

Частный случай (Special Case)

Special Case — производный класс, описывающий поведение объекта в особых ситуациях.

case

Значения NULL — настоящий бич объектно-ориентированных приложений. Потенциальное наличие таких значений напрочь лишает разработчика возможности воспользоваться преимуществами полиморфизма. Обычно методу можно передавать ссылки на переменные или же значения заданных типов, не беспокоясь о том, принадлежит ли переданное значение к указанному типу или же к производному классу. В строго типизированных языках проверку на правильность вызова можно проводить еще на этапе компиляции. К сожалению, если передаваемая переменная имеет значение NULL, при попытке доступа к последнему вы совершите ошибку времени выполнения и получите не более чем красивую и весьма "дружелюбную" трассировку стека.

Основная идея данного паттерна состоит в том, чтобы создать производный класс для обработки частных, особых случаев. Таким образом, чтобы избежать проверки объекта Customer на наличие значений NULL, можно создать объект NullCustomer. Последний будет переопределять все методы объекта Customer, чтобы при возникновении "особой ситуации" приложение не начало вести себя совершенно непредсказуемым образом, а выполнило что-нибудь безобидное. В этом случае при обнаружении значения NULL вызывающий метод вместо экземпляра класса Customer будет возвращать экземпляр производного класса NullCustomer. Обычно приложению не требуется проводить различие между экземплярами объекта NullCustomer, поэтому для его реализации можно воспользоваться паттерном приспособленец (Flyweight).

Как итог можно сказать следующее:

Паттерн Special Case следует применять для описания повторяющегося поведения, связанного с обработкой значений NULL ИЛИ других особых ситуаций после выполнения соответствующих проверок на их наличие.

Дополнительный модуль (Plugin)

Plugin — связывает классы во время настройки, а не во время компиляции приложения.

plugin

Паттерн Separated Interface часто используется, когда один код выполняется в нескольких средах и требует разной реализации отдельной логики. Большинство разработчиков добиваются этого при помощи использования шаблона фабрики. Представим, что надо генерировать первичный ключ при помощи паттерна Separated Interface. Можно использовать для юнит-тестирования простой объект-счёчик, а на реальной системе - последовательность из БД. 

Как только у вас появится ещё несколько фабрик - начнётся путаница. Создание новой конфигурации, например "запуск юнит-тестов на БД без контроля транзакций" или "запуск в продакшн на DB2 с полной поддержкой транзакций", потребует правок в условиях в большом количестве фабрик, пересборку и переразвёртывание.

Конфигурация не должна быть разбросана по приложению, также как и требовать пересборки и переразвёртывания. Паттерн Plugin решает обе эти проблемы, предоставляя централизованную динамическую конфигурацию.

Пример реализации

Вначале нужно определить отделенный интерфейс для каждого поведения, которое требует применения различных реализаций в зависимости от выбранной исполняющей среды. Помимо этого, воспользуемся шаблоном фабрикой, несколько видоизменив его в соответствии с нашими требованиями. Реализация дополнительного модуля в виде объекта-фабрики требует, чтобы все правила связывания находились в единственной внешней точке. Это необходимо для облегчения настройки приложения. Кроме того, связывание интерфейса с реализацией должно выполняться не во время компиляции, а динамически, т.е. во время выполнения, чтобы избежать повторной сборки приложения.

Правила связывания можно хранить в обычном текстовом файле. Метод-фабрика дополнительного модуля считывает файл настроек, ищет строку, задающую реализацию запрошенного интерфейса и возвращает эту реализацию:

pattern plugin

 

Применение дополнительного модуля наиболее предпочтительно в языках, поддерживающих рефлексию, поскольку объект-фабрика может создавать объекты реализаций без необходимости устанавливать зависимости во время компиляции. При использовании рефлексии файл настроек должен содержать отображения имен интерфейсов на имена классов реализаций. В этом случае объект-фабрика может находиться в пакете инфраструктуры, никак не связанном с пакетами, в которых находятся классы реализаций, и не нуждается в изменениях при добавлении в файл новых реализаций.

Фиктивная служба (Service Stub)

Service stub — устраняет зависимость приложения от труднодоступных или проблемных служб на время тестирования.

service stub

Назначение

Фиктивная служба используется тогда, когда зависимость приложения от конкретной внешней службы значительно затрудняет процесс разработки и тестирования. Следует отметить, что многие приверженцы экстремального программирования употребляют термин объект-имитатор (mock object), имея в виду не что иное, как фиктивную службу. По сути каждый раз когда вам нужно имитировать работу какой-то внешней службы, можно с помощью интерфейса и DI подставить реализация stub сервиса при тестировании.

Record Set

Record Set — представление табличных данных в оперативной памяти

record set

За последние двадцать лет основным способом представления данных в БД стали реляционные таблицы. Почти каждый новый разработчик использует реляционные данные.

За это время появилось множество UI инструментов. Эти UI-фреймворки основываются на реляционности данных и предоставляют различные UI-элементы, которые легко настраиваются и управляются практически безо всякого программирования.

Обратная сторона медали в том, что, несмотря на невероятную лёгкость вывода и работы с данными, эти элементы не предусматривают возможности добавления кода бизнес-логики. Проверки типа "правильный ли формат у эта даты" и любые правила исполнения попросту некуда поставить. И в итоге, эта логика либо забивается в БД, либо смешивается в кодом вывода информации.

Суть Record Set в предоставлении структуры данных, которая выглядит в точности как результат SQL-запроса, но может управляться и обрабатываться любыми частями системы.

Источник: Martin Fowler

Comments:

Please log in to be able add comments.