» »

Готовый код на адаптеры в java. Паттерн проектирования Adapter (Адаптер) на PHP. Разница между Адаптером и Фасадом

20.03.2024

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

    У этого термина существуют и другие значения, см. Паттерн. В разработке программного обеспечения, шаблон проектирования или паттерн (англ. 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)

Название и классификация паттерна

Адаптер - паттерн, структурирующий как классы, так и объекты.

Назначение паттерна Adapter

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

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

Рассмотрим простейший пример применения паттерна Adapter. Пусть мы разрабатываем систему климат-контроля. Важным компонентом такой системы является температурный датчик, для которого уже имеется готовое программное обеспечение, представляющее собой некоторый класс с соответствующим интерфейсом для работы со шкалой Фаренгейта. Но нас интересует температура по шкале Цельсия. Задача может быть легко решена с помощью паттерна Adapter.

Рассмотрим другой пример - графический редактор, в котором пользователи могут рисовать на экране графические элементы (линии, многоугольники, текст и т. д.) и организовывать их в виде картинок и диаграмм. Основной абстракцией графического редактора является графический объект, который имеет изменяемую форму и изображает сам себя. Интерфейс графических объектов определен абстрактным классом Shape. Редактор определяет подкласс класса Shape для каждого вида графических объектов: LineShape для прямых, PolygonShape для многоугольников и т. д.

Классы для элементарных геометрических фигур, например LineShape и PolygonShape, реализовать сравнительно просто, поскольку заложенные в них возможности рисования и редактирования крайне ограниченны. Но подкласс Text Shape, умеющий отображать и редактировать текст, уже значительно сложнее. В то же время существуют готовые библиотеки для разработки пользовательских интерфейсов, предоставляющие развитый класс Text View для отображения и редактирования текста.

Можно было бы так изменить интерфейс класса Text View, чтобы он соответствовал интерфейсу Shape, только для этого нужен исходный код. Но даже если он доступен, то вряд ли разумно изменять Text View - библиотека не должна приспосабливаться к интерфейсам каждого конкретного приложения.

Вместо этого можно так определить класс Text Shape, что он будет адаптировать интерфейс Text View к интерфейсу Shape. Это допустимо сделать двумя способами: наследуя интерфейс от Shape, а реализацию от Text View; включив экземпляр Text View в Text Shape и реализовав Text Shape в терминах интерфейса Text View. Два данных подхода соответствуют вариантам паттерна Адаптер в его классовой и объектной ипостасях. Класс Text Shape (рис. 40) мы будем называть Адаптером.

На диаграмме показан Адаптер объекта. Видно, как запрос BoundingBox, объявленный в классе Shape, преобразуется в запрос Get Extent, определенный в классе Text View. Поскольку класс Text Shape адаптирует Text View к интерфейсу Shape, графический редактор может воспользоваться классом TextView, хотя тот и имеет несовместимый интерфейс.

Часто Адаптер отвечает за функциональность, которую не может предоставить адаптируемый класс. На рис. 40 показано, как Адаптер

return newTextManipulator^^

Рис. 40. UML-диаграмма Адаптера объекта

выполняет такого рода функции. У пользователя должна быть возможность перемещать любой объект класса Shape в другое место, но в классе TextView такая операция не предусмотрена. Text Shape может добавить недостающую функциональность, самостоятельно реализовав операцию CreateManipulator класса Shape, которая возвращает экземпляр подходящего подкласса Manipulator.

Manipulator - это абстрактный класс объектов, которым известно, как анимировать Shape в ответ на такие действия пользователя, как перетаскивание фигуры в другое место. У класса Manipulator имеются подклассы для различных фигур. Например, TextManipulator - подкласс для Text Shape. Возвращая экземпляр TextManipulator, объект класса TextShape добавляет новую функциональность, которой в классе TextView нет, а классу Shape требуется.

Применимость

Применяйте паттерн Адаптер, когда:

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

Описание паттерна Adapter

Адаптер класса использует множественное наследование для адаптации одного интерфейса к другому.

Пусть класс, интерфейс которого нужно адаптировать к нужному виду, имеет имя Adapt?e. Для решения задачи преобразования его интерфейса паттерн Adapter вводит следующую иерархию классов:

  • виртуальный базовый класс Target. Здесь объявляется пользовательский интерфейс подходящего вида. Только этот интерфейс доступен для пользователя;
  • производный класс Adapter , реализующий интерфейс Target. В этом классе также имеется указатель или ссылка на экземпляр Adapt?e. Паттерн Adapter использует этот указатель для перенаправления клиентских вызовов в Adapt?e. Так как интерфейсы Adapt?e и Target несовместимы между собой, то эти вызовы обычно требуют преобразования.

Структура

Структура паттерна Адаптер показана на рис. 41.

Адаптер объекта применяет композицию объектов.


adaptee->specificRequest()

Рис. 41. UML-диаграмма классов паттерна Adapter

Участники

Target (Shape) - целевой: определяет зависящий от предметной области интерфейс, которым пользуется Client.

Client (DrawingEditor) - клиент: вступает во взаимоотношения с объектами, удовлетворяющими интерфейсу Target.

Adapt?e (Textview) - адаптируемый: определяет существующий интерфейс, который нуждается в адаптации.

Adapter (Text Shape) - Адаптер: адаптирует интерфейс Adapt?e к интерфейсу Target.

Отношения

Клиенты вызывают операции экземпляра Адаптера Adapter. В свою очередь, Адаптер вызывает операции адаптируемого объекта или класса Adapt?e, который и выполняет запрос.

Результаты

Результаты применения Адаптеров объектов и классов различны.

Адаптер класса:

  • адаптирует Adapt?e к Target, перепоручая действия конкретному классу Adapt?e. Поэтому данный паттерн не будет работать, если мы захотим одновременно адаптировать класс и его подклассы;
  • позволяет Адаптеру Adapter заместить некоторые операции адаптируемого класса Adapt?e, так как Adapter есть не что иное, как подкласс Adapt?e;
  • вводит только один новый объект. Чтобы добраться до адаптируемого класса, не нужно никакого дополнительного обращения по указателю.

Адаптер объектов:

  • позволяет одному Адаптеру Adapter работать со многим адаптируемыми объектами Adapt?e, т. е. с самим Adapt?e и его подклассами (если таковые имеются). Адаптер может добавить новую функциональность сразу всем адаптируемым объектам;
  • затрудняет замещение операций класса Adapt?e. Для этого потребуется породить от Adapt?e подкласс и заставить Adapter ссылаться на этот подкласс, а не на сам Adapt?e.

Адаптеры сильно отличаются по тому объему работы, который необходим для адаптации интерфейса Adapt?e к интерфейсу Target. Это может быть как простейшее преобразование, например изменение имен операций, так и поддержка совершенно другого набора операций. Объем работы зависит от того, насколько сильно отличаются друг от друга интерфейсы целевого и адаптируемого классов.

На первом шаге реализации необходимо найти «узкий» интерфейс для Adapt?e, т. е. наименьшее подмножество операций, позволяющее выполнить адаптацию. «Узкий» интерфейс, состоящий всего из пары итераций, легче адаптировать, чем интерфейс из нескольких десятков операций.

«Узкий» интерфейс можно реализовать разными способами.

  • 1. Использование абстрактных операций. Подклассы должны реализовывать эти абстрактные операции и адаптировать иерархически структурированный объект.
  • 2. Использование объектов-уполномоченных. При таком подходе запросы на доступ к иерархической структуре переадресуются объек-ту-уполномоченному.

Классическая реализация паттерна Adapter

Приведем реализацию паттерна Adapter. Для примера выше адаптируем показания температурного датчика системы климат-контроля, переведя их из градусов Фаренгейта в градусы Цельсия (предполагается, что код этого датчика недоступен для модификации).

// Уже существующий класс температурного датчика окружающей среды

class FahrenheitSensor

// Получить показания температуры в градусах Фаренгейта float getFahrenheitTemp() { float t = 32.0;

//... какой-то код return t;

virtual ~Sensor() {}

class Adapter: public Sensor

Adapter(FahrenheitSensor* p): p_fsensor(p) {

Adapted) { delete p_fsensor;

float getTemperature() { return (p_fsensor->getFahrenheitTemp()-32.0)*5.0/9.0;

FahrenheitSensor* p_fsensor;

Sensor* p = new Adapter(new FahrenheitSensor);

cout getTemperature()

Реализация паттерна Adapter на основе закрытого наследования

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

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

Цели, преследуемые при использовании открытого и закрытого наследования, различны. Если открытое наследование применяется для наследования интерфейса и реализации, то закрытое наследование - только для наследования реализации.

class FahrenheitSensor

float getFahrenheitTempO { float t = 32.0;

void adjustQ {} // Настройка датчика (защищенный метод)

virtual ~Sensor() {}

virtual float getTemperature() = 0;

virtual void adjust() = 0;

class Adapter: public Sensor, private FahrenheitSensor

Adapter() {} float getTemperature() { return (getFahrenheitTemp()-32.0)*5.0/9.0;

FahrenheitSensor::adjust();

Sensor * p = new Adapter(); p->adjust();

cout getTemperature()

Результаты применения паттерна Adapter

Достоинства паттерна Adapter

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

Недостатки паттерна Adapter

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

Родственные паттерны

Структура паттерна Мост аналогична структуре Адаптера, но у Моста иное назначение. Он отделяет интерфейс от реализации, чтобы то и другое можно было изменять независимо. Адаптер же призван изменить интерфейс существующего объекта.

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

Заместитель определяет представителя или суррогат другого объекта, но не изменяет его интерфейс.

Назначение паттерна Adapter

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

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

Рассмотрим простой пример, когда следует применять паттерн Adapter. Пусть мы разрабатываем систему климат-контроля, предназначенной для автоматического поддержания температуры окружающего пространства в заданных пределах. Важным компонентом такой системы является температурный датчик, с помощью которого измеряют температуру окружающей среды для последующего анализа. Для этого датчика уже имеется готовое программное обеспечение от сторонних разработчиков, представляющее собой некоторый класс с соответствующим интерфейсом. Однако использовать этот класс непосредственно не удастся, так как показания датчика снимаются в градусах Фаренгейта. Нужен адаптер, преобразующий температуру в шкалу Цельсия.

Контейнеры queue, priority_queue и stack библиотеки стандартных шаблонов STL реализованы на базе последовательных контейнеров list, deque и vector, адаптируя их интерфейсы к нужному виду. Именно поэтому эти контейнеры называют контейнерами-адаптерами.

Описание паттерна Adapter

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

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

Реализация паттерна Adapter

Классическая реализация паттерна Adapter

Приведем реализацию паттерна Adapter. Для примера выше адаптируем показания температурного датчика системы климат-контроля, переведя их из градусов Фаренгейта в градусы Цельсия (предполагается, что код этого датчика недоступен для модификации).

#include // Уже существующий класс температурного датчика окружающей среды class FahrenheitSensor { public: // Получить показания температуры в градусах Фаренгейта float getFahrenheitTemp() { float t = 32.0; // ... какой то код return t; } }; class Sensor { public: virtual ~Sensor() {} virtual float getTemperature() = 0; }; class Adapter: public Sensor { public: Adapter(FahrenheitSensor* p) : p_fsensor(p) { } ~Adapter() { delete p_fsensor; } float getTemperature() { return (p_fsensor->getFahrenheitTemp()-32.0)*5.0/9.0; } private: FahrenheitSensor* p_fsensor; }; int main() { Sensor* p = new Adapter(new FahrenheitSensor); cout << "Celsius temperature = " << p->getTemperature() << endl; delete p; return 0; }

Реализация паттерна Adapter на основе закрытого наследования

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

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

Цели, преследуемые при использовании открытого и закрытого наследования различны. Если открытое наследование применяется для наследования интерфейса и реализации, то закрытое наследование - только для наследования реализации.

#include class FahrenheitSensor { public: float getFahrenheitTemp() { float t = 32.0; // ... return t; } protected: void adjust() {} // Настройка датчика (защищенный метод) }; class Sensor { public: virtual ~Sensor() {} virtual float getTemperature() = 0; virtual void adjust() = 0; }; class Adapter: public Sensor, private FahrenheitSensor { public: Adapter() { } float getTemperature() { return (getFahrenheitTemp()-32.0)*5.0/9.0; } void adjust() { FahrenheitSensor::adjust(); } }; int main() { Sensor * p = new Adapter(); p->adjust(); cout << "Celsius temperature = " << p->getTemperature() << endl; delete p; return 0; }

Результаты применения паттерна Adapter

Достоинства паттерна Adapter

  • Паттерн Adapter позволяет повторно использовать уже имеющийся код, адаптируя его несовместимый интерфейс к виду, пригодному для использования.

Недостатки паттерна Adapter

  • Задача преобразования интерфейсов может оказаться непростой в случае, если клиентские вызовы и (или) передаваемые параметры не имеют функционального соответствия в адаптируемом объекте.

Последнее обновление: 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 или англ. Wrapper -Обёртка) - структурный шаблон проектирования, предназначенный для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс.

Задача

Система поддерживает требуемые данные и поведение, но имеет неподходящий интерфейс.

Способ решения

Адаптер предусматривает создание класса-оболочки с требуемым интерфейсом.

Участники

Класс Adapter приводит интерфейс класса Adaptee в соответствие с интерфейсом класса Target (наследником которого является Adapter). Это позволяет объекту Client использовать объект Adaptee (посредством адаптера Adapter) так, словно он является экземпляром класса Target .

Таким образом Client обращается к интерфейсу Target , реализованному в наследнике Adapter, который перенаправляет обращение к Adaptee.

UML-диаграмма классов паттерна Adapter

Следствия

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

Описание

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

    Виртуальный базовый класс Target. Здесь объявляется пользовательский интерфейс подходящего вида. Только этот интерфейс доступен для пользователя.

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

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

Рассмотрим простой пример, когда следует применять паттерн Adapter. Пусть мы разрабатываем систему климат-контроля, предназначенной для автоматического поддержания температуры окружающего пространства в заданных пределах. Важным компонентом такой системы является температурный датчик, с помощью которого измеряют температуру окружающей среды для последующего анализа. Для этого датчика уже имеется готовое программное обеспечение от сторонних разработчиков, представляющее собой некоторый класс с соответствующим интерфейсом. Однако использовать этот класс непосредственно не удастся, так как показания датчика снимаются в градусах Фаренгейта. Нужен адаптер, преобразующий температуру в шкалу Цельсия.

Реализация

namespace Adapter

static void Main()

// Create adapter and place a request

Target target = new Adapter();

target.Request();

// Wait for user

public virtual void Request()

Console.WriteLine("Called Target Request()");

class Adapter: Target

private Adaptee adaptee = new Adaptee();

public override void Request()

// Possibly do some other work

// and then call SpecificRequest

adaptee.SpecificRequest();

public void SpecificRequest()

Console.WriteLine("Called SpecificRequest()");

Результаты применения паттерна Adapter

Достоинства паттерна Adapter

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

Недостатки паттерна Adapter

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