Главная - Безопасность
Адаптер шаблон проектирования. Паттерн проектирования Adapter (Адаптер) на PHP

Последнее обновление: 31.10.2015

Паттерн Адаптер (Adapter) предназначен для преобразования интерфейса одного класса в интерфейс другого. Благодаря реализации данного паттерна мы можем использовать вместе классы с несовместимыми интерфейсами.

Когда надо использовать Адаптер?

    Когда необходимо использовать имеющийся класс, но его интерфейс не соответствует потребностям

    Когда надо использовать уже существующий класс совместно с другими классами, интерфейсы которых не совместимы

Формальное определение паттерна на UML выглядит следующим образом:

Формальное описание адаптера объектов на C# выглядит таким образом:

Class Client { public void Request(Target target) { target.Request(); } } // класс, к которому надо адаптировать другой класс class Target { public virtual void Request() {} } // Адаптер class Adapter: Target { private Adaptee adaptee = new Adaptee(); public override void Request() { adaptee.SpecificRequest(); } } // Адаптируемый класс class Adaptee { public void SpecificRequest() {} }

Участники

    Target : представляет объекты, которые используются клиентом

    Client : использует объекты Target для реализации своих задач

    Adaptee : представляет адаптируемый класс, который мы хотели бы использовать у клиента вместо объектов Target

    Adapter : собственно адаптер, который позволяет работать с объектами Adaptee как с объектами Target.

То есть клиент ничего не знает об Adaptee, он знает и использует только объекты Target. И благодаря адаптеру мы можем на клиенте использовать объекты Adaptee как Target

Теперь разберем реальный пример. Допустим, у нас есть путешественник, который путешествует на машине. Но в какой-то момент ему приходится передвигаться по пескам пустыни, где он не может ехать на машине. Зато он может использовать для передвижения верблюда. Однако в классе путешественника использование класса верблюда не предусмотрено, поэтому нам надо использовать адаптер:

Class Program { static void Main(string args) { // путешественник Driver driver = new Driver(); // машина Auto auto = new Auto(); // отправляемся в путешествие driver.Travel(auto); // встретились пески, надо использовать верблюда Camel camel = new Camel(); // используем адаптер ITransport camelTransport = new CamelToTransportAdapter(camel); // продолжаем путь по пескам пустыни driver.Travel(camelTransport); Console.Read(); } } interface ITransport { void Drive(); } // класс машины class Auto: ITransport { public void Drive() { Console.WriteLine("Машина едет по дороге"); } } class Driver { public void Travel(ITransport transport) { transport.Drive(); } } // интерфейс животного interface IAnimal { void Move(); } // класс верблюда class Camel: IAnimal { public void Move() { Console.WriteLine("Верблюд идет по пескам пустыни"); } } // Адаптер от Camel к ITransport class CamelToTransportAdapter: ITransport { Camel camel; public CamelToTransportAdapter(Camel c) { camel = c; } public void Drive() { camel.Move(); } }

И консоль выведет:

Машина едет по дороге Верблюд идет по пескам пустыни

В данном случае в качестве клиента применяется класс Driver, который использует объект ITransport. Адаптируемым является класс верблюда Camel, который нужно использовать в качестве объекта ITransport. И адптером служит класс CamelToTransportAdapter.

Возможно, кому-то покажется надуманной проблема использования адаптеров особенно в данном случае, так как мы могли бы применить интерфейс ITransport к классу Camel и реализовать его метод Drive(). Однако, в данном случае может случиться дублирование функциональностей: интерфейс IAnimal имеет метод Move(), реализация которого в классе верблюда могла бы быть похожей на реализацию метода Drive() из интерфейса ITransport. Кроме того, нередко бывает, что классы спроектированы кем-то другим, и мы никак не можем на них повлиять. Мы только используем их. В результате чего адаптеры довольно широко распространены в.NET. В частности, многочисленные встроенные классы, которые используются для подключения к различным системам баз данных, как раз и реализуют паттерн адаптер (например, класс System.Data.SqlClient.SqlDataAdapter).

Перед прочтением ознакомьтесь с , в котором описаны принятые соглашения и понятия. Данная статья дополняется с некоторой периодичностью, так что если вы ее читали ранее, не факт что данные не изменились.

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

Принцип работы

Адаптер наследует открытым способом целевой интерфейс (назовем его Target ), и закрытым способом адаптируемый интерфейс (Adaptee ). В реализации методов целевого интерфейса происходит перенаправление (делегирование) запросов классу с адаптируемым интерфейсом

Пример

// Целевой интерфейс, клиент умеет работать только с ним interface iTarget { public function query(); } // Адаптируемый интерфейс. Клиент с ним не умеет работать, но очень хочет interface iAdaptee { public function request(); } // Класс, реализующий адаптирумым интерфейс class Adaptee implements iAdaptee { public function request() { return __CLASS__ . "::" . __METHOD__; } } class Adapter implements iTarget { protected $adaptee = null; public function __construct() { $this -> adaptee = new Adaptee(); } public function query() { return $this -> adaptee -> request(); } } $Target = new Adapter(); print $Target -> query(); // "Adaptee::request"

Заключение

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

В прошлом уроке мы рассмотрели возможность шаблона проектирования Фасад, с помощью которого можно перестроить код крупного проекта.

В этой статье под наш прицел попал шаблон проектирования под названием Адаптер. Его использование целесообразно, если в вашем проекте происходит взаимодействие с сторонними API, или другим классом, который подвержен частым изменениям. Данный шаблон относится к "структурному" типу т.к. с его помощью мы можем мы можем организовать структуру классов по определённому образцу.

Ещё раз хотел бы напомнить, что шаблоны проектирования ничем не отличаются от обычных классов. С их помощью мы можем лучшим способом структурировать наши классы, управлять их поведением.

Задача

В приведённом примере мы используем класс Twitter для упрощения процедуры публикации сообщения. Далее мы создаём объект для обращения к Twitter API и публикации сообщения. Представьте, что данный код используется в нескольких местах. Обратите внимание, что для публикации сообщения мы используем метод $twitter->send("Posting on Twitter");

Некоторое время назад, Twitter изменили название метода API для публикации сообщения. Те, кто пользовался предыдущей версией явно увидят сложившуюся проблему. Нам необходимо поменять все названия методов для отправки твитта. Представьте сколько кода нам нужно поменять и сколько на это потребуется времени. Что если смена названия произойдёт ещё раз?

Решение

В качестве решения можем применить шаблон проектирования Адаптер.

Адаптер — структурный шаблон проектирования, предназначенный для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс.

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

Давайте рассмотрим код, написанный на основе шаблона проектирования Адаптер:

// Имплементация класса Twitter class Twitter { public function __construct() { // Your Code here // } public function send($msg) { // Posting to Twitter // echo $msg; } } // Простой интерфейс для каждого адаптера, который будет создан interface socialAdapter { public function send($msg); } class twitterAdapter implements socialAdapter { private $twitter; public function __construct(Twitter $twitter) { $this->twitter = $twitter; } public function send($msg) { $this->twitter->send($msg); } }

Изучив код вы поймёте, что мы не тронули исходный класс Twitter. Вместо этого мы создали интерфейс нашего социального адаптера для класса Twitter.

Далее вместо создания объекта типа Twitter, мы создали объект его адаптера. В качестве параметра передаём объект основного класса Twitter, для того, чтобы наш адаптер имел доступ к объекту основного класса.

Теперь давайте определим как использовать методы исходного класса:

// клиентский код $twitter = new twitterAdapter(new Twitter()); $twitter->send("Posting to Twitter");

Теперь представьте, что Twitter изменил название метода с send на sendTweet. В этом случае, нам нужно изменить только twitterAdapter. Взгляните на изменённый код адаптера.

Class twitterAdapter implements socialAdapter { private $twitter; public function __construct(Twitter $twitter) { $this->twitter = $twitter; } public function send($msg) { $this->twitter->sendTweet($msg); } }

Только одно изменение и мы снова в теме.

Добавление нового адаптера

Давайте рассмотрим как можно использовать шаблон проектирования Адаптер в других случаях. Теперь очень просто добавить новый класс на основе существующего адаптера. Допустим Facebook API позволяет обновить статус.

Вместо того чтобы напрямую использовать класс Facebook, создадим новый адаптер.

Class Facebook { public function __construct() { // Ваш код // } public function updateStatus($msg) { // Пост на Facebook // echo $msg; } } // Адаптер Facebook class facebookAdapter implements socialAdapter { private $facebook; public function __construct(Facebook $facebook) { $this->facebook = $facebook; } public function send($msg) { $this->facebook->updateStatus($msg); } } // клиентский код $facebook = new facebookAdapter(new Facebook()); $facebook->send("Posting to Facebook");

Как видите, принцип тот же. Вы определяете класс оболочку, в который передаёте оригинальный объект класса. При изменении API, вам нужно сменить код только в одном месте.

Заключение

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

Я сделал всё зависящее от меня, чтобы продемонстрировать элементарный и одновременно полезный пример использования шаблона проектирования Адаптер.

    Адаптер (шаблон проектирования)/Примеры кода - Основная статья: Адаптер (шаблон проектирования) Пример реализации шаблона на C# using System; namespace Adapter { class MainApp { static void Main() { … Википедия

    Шаблон Proxy (шаблон проектирования)

    Шаблон проектирования - У этого термина существуют и другие значения, см. Паттерн. В разработке программного обеспечения, шаблон проектирования или паттерн (англ. design pattern) повторимая архитектурная конструкция, представляющая собой решение проблемы… … Википедия

    Интерфейс (шаблон проектирования) - Шаблон проектирования Интерфейс Interface Описан в Design Patterns Нет В информатике, шаблон интерфейса не является особым шаблоном среди шаблонов проектирования. Он является общим методом для структурирования компьютерных программ для того … Википедия

    Заместитель (шаблон проектирования) - Шаблон Proxy (Заместитель) Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 … Википедия

    Хранитель (шаблон проектирования) - Шаблон проектирования Хранитель Memento Тип: поведенческий Описан в Design Patterns Да Хранитель (также известный как Memento, Token, Лексема) поведенческий шаблон проектирования. Позволяет, не нарушая инкапсуляцию, зафикс … Википедия

    Итератор (шаблон проектирования) - Шаблон проектирования Итератор Iterator Тип: поведенческий Описан в Design Patterns Да Шаблон Iterator (также известный как Cursor) Шаблон проектирования, относится к паттернам поведения. Представляет собой объект, позволяющий получить … Википедия

    Интерпретатор (шаблон проектирования) - Шаблон проектирования Интерпретатор Interpreter Тип: поведенческий Назначение: решает часто встречающуюся, подверженную изменениям задачу Описан в Design Patterns Да Шаблон Интерпретатор (англ. … Википедия

    Компоновщик (шаблон проектирования) - Шаблон проектирования Компоновщик Composite Тип: структурный Описан в Design Patterns Да Компоновщик (англ. Composite pattern) шаблон проектирования, относится к структурным паттернам, объединяет объек … Википедия

    Состояние (шаблон проектирования) - Шаблон проектирования Состояние State Тип: поведенческий Описан в Design Patterns Да Состояние (англ. State) шаблон проектирования. Используется в тех случаях, когда во время выполнения программы объект … Википедия

Вернемся к рассмотрению структурных паттернов проектирования. На этот раз мы рассмотрим шаблон проектирования под названием Adapter (его еще называют Wrapper на ряду с паттерном Facade).

В этой статье поговорим о следующем:

Итак, паттерн Adapter используется для того, чтобы объекты с одним интерфейсом (контрактом) мог работать там, где необходим объект с совершенно другим интерфейсом. Существует два типа адаптеров - Class Adapter и Object Adapter.

Для начала мы рассмотрим каждый из этих типов, а потом я объясню разницу между двумя wrapper"ами - адаптером и фасадом.

Object Adapter

Object Adapter достигает своей цели с помощью композиции. На диаграмме, представленной ниже, клиенту требуется использовать интерфейс TargetInterface. Для этого создается класс ObjectAdapter, который реализует интерфейс TargetInterface, а также хранит объект класса Adaptee. При вызове метода targetMethod у Адаптера, осуществляется вызов соответствующего метода у адаптируемого интерфейса.

В самом простом случае реализация ObjectAdapter будет такой:

Public class ObjectAdapter implements TargetInterface { private Adaptee adaptee; public void targetMethod() { adaptee.method() } }

Плюс такого подхода в том, что мы полностью отделяем клиентский интерфейс от адаптируемого интерфейса.

Class Adapter

В случае с Class Adapter"ом, для достижения нашей цели используется множественное наследование. Наш ClassAdapter наследуется от клиентского интерфейса и от Адаптируемого интерфейса. Так как в Java нет множественного наследования, то только один из предков может быть абстрактным/конкретным классом. Второй предок будет интерфейсом, что не всегда удобно.

Диаграмма классов:

А вот и тривиальная реализация класса ClassAdapter:

Public class ClassAdapter extends Adaptee implements TargetInterface { public void targetMethod() { method(); } }

Хочу обратить ваше внимание, что при такой реализации адаптера может возникнуть конфликт сигратур методов. Такой проблемы у Object Adapter нет.

Class Adapter считается более простым решением в случае когда не требуется жесткого разделения клиентского и адаптируемого интерфейсов.

Разница между Адаптером и Фасадом

Теперь хочу сказать несколько слов по поводу паттерна Фасад, который как и Адаптер является Wrapper"ом. Facade определяет новый интерфейс, в то время как Адаптер использует существующие интерфейсы.

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

Пример использования Адаптера в JDK

В стандартной библиотеке тоже можно встретить примеры использования Адаптера. Наверное, самый популярный вариант использования - это java.io.InputStreamReader и OutputStreamWriter.

Конструктор InputStreamReader принимает на вход InputStream и в результате адаптирует поток в Reader.

Позже выложу код использования Адаптеров из реальных проектов, в которых я принимал участие, а пока жду ваших вопросов и комментариев. Удачи.



 


Читайте:



Как узнать VID, PID флешки и для чего служат эти идентификационные номера?

Как узнать VID, PID флешки и для чего служат эти идентификационные номера?

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

Apptools: как зарабатывать, играя

Apptools: как зарабатывать, играя

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

Lenovo Vibe K5 Plus - Технические характеристики Характеристики звука и камеры

Lenovo Vibe K5 Plus - Технические характеристики Характеристики звука и камеры

За довольно короткий период Lenovo стала одним из самых серьезных игроков на украинском рынке портативной техники. Продукты китайской компании...

Платежная система Payza (ex-Alertpay) Payza вход в личный кабинет

Платежная система Payza (ex-Alertpay) Payza вход в личный кабинет

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

feed-image RSS