Паттерн "Адаптер" - Южно-Уральский государственный

advertisement
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Паттерн Адаптер
(Adapter)
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Определение
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Определение
Адаптер — структурный шаблон проектирования,
предназначенный для организации использования
функций объекта, нежелательного для
модификации, через уже существующий
интерфейс.
Реализация
 адаптер класса (множественное наследование
классов или интерфейсов);
 адаптер объекта (композиция).
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Мотивация
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Мотивация

Часто меняющиеся версии или реализации (сторонних)
библиотечных классов
Возможно, что библиотека, которую вы используете, часто
изменяется или вы планируете переход на другую реализацию
требуемой функциональности (другую библиотеку).

Недоступный для модификации код с неподходящим интерфейсом
Возможно, что определенной части нашего кода требуется другой
интерфейс от объекта, модификация которого нежелательна или
невозможна.

Несуществующий ещѐ код с известной наперед
функциональностью
Возможно, что код, обеспечивающий требуемую
функциональность, ещѐ не написан (сторонними разработчиками),
однако общие принципы его работы уже ясны.
Национальный исследовательский Южно-Уральский государственный университет
Плюсы
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Плюсы:
 Инкапсуляция реализации внешних классов
(компонентов, библиотек). Таким образом,
система становится независимой от
интерфейса внешних классов.

Переход на использование других
(несовместимых по интерфейсу) внешних
классов не требует переделки самой
системы, достаточно заменить реализацию
соответствующих адаптеров.
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Адаптер
класса
ДЛЯ РЕШЕНИЯ
ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Адаптер
объекта
ДЛЯ РЕШЕНИЯ
ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Участники
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Target — целевой: определяет зависящий от
предметной области интерфейс, которым
пользуется Client.
Client — клиент: вступает во взаимоотношения с
объектами, удовлетворяющими интерфейсу Target.
Adaptee — адаптируемый: определяет
существующий интерфейс, который нуждается в
адаптации.
Adapter — адаптер: адаптирует интерфейс
Adaptee к интерфейсу Target.
Национальный исследовательский Южно-Уральский государственный университет
Пример №1
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
ДЛЯ РЕШЕНИЯ ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
Пусть есть унаследованный класс прямоугольника:
typedef int Coordinate;
class LegacyRectangle
{
public:
LegacyRectangle(Coordinate x1, Coordinate y1, Coordinate x2, Coordinate y2)
: x1_(x1), x2_(x2_), y1_(y1), y2_(y2)
{
std::cout << "LegacyRectangle: create. (" << x1_ << "," << y1_ <<
") => (" << x2_ << "," << y2_ << ")" << std::endl;
}
void OldDraw() {
std::cout << "LegacyRectangle: OldDraw. (" << x1_ << "," << y1_ <<
") => (" << x2_ << "," << y2_ << ")" << std::endl;
}
private:
Coordinate x1_, y1_, x2_, y2_;
};
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Пример
№1ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
ДЛЯ РЕШЕНИЯ
Ожидаемый клиентом интерфейс выглядит
следующим образом:
class Rectangle
{
public:
virtual void Draw() = 0;
};
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Пример
№1ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
ДЛЯ РЕШЕНИЯ
Класс адаптера наследует интерфейс Rectangle и
реализацию LegacyRectangle:
typedef int Dimension;
class RectangleAdapter: public Rectangle, private LegacyRectangle
{
public:
RectangleAdapter(Coordinate x, Coordinate y, Dimension w, Dimension h)
: LegacyRectangle(x, y, x + w, y + h)
{
std::cout << "RectangleAdapter: create. (" << x << "," << y <<
"), width = " << w << ", height = " << h << std::endl;
}
virtual void Draw()
{
std::cout << "RectangleAdapter: draw." << std::endl;
OldDraw();
}
};
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Пример
№2ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
ДЛЯ РЕШЕНИЯ
Пусть есть класс SSH-подключения, который
используется в системе:
public abstract class SSHTunnel
{
public abstract int LocalPort
{ get; set; }
public abstract string RemoteHost
{ get; set; }
public abstract int RemotePort
{ get; set; }
public abstract void Open();
public abstract void Close();
}
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Пример
№2ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
ДЛЯ РЕШЕНИЯ
Адаптируем библиотечный класс ElSimpleSSHClient из набора
SecureBlackBox к требуемому интерфейсу:
public class SecureBlackboxSSHTunnelAdapter : SSHTunnel {
ElSimpleSSHClient sshClient;
public SecureBlackboxSSHTunnelAdapter() {
sshClient = new ElSimpleSSHClient();
sshClient.UseInternalSocket = true;
}
public override void Open()
{
sshClient.Open();
}
public override void Close()
{
sshClient.Close();
}
}
Национальный исследовательский Южно-Уральский государственный университет
ПНР 5 "СУПЕРКОМПЬЮТЕРНЫЕ И ГРИД-ТЕХНОЛОГИИ
Пример
№2ПРОБЛЕМ ЭНЕРГО- И РЕСУРСОСБЕРЕЖЕНИЯ”
ДЛЯ РЕШЕНИЯ
public class SecureBlackboxSSHTunnelAdapter : SSHTunnel
{
public override int LocalPort {
get { return sshClient.SocksPort; }
set { sshClient.SocksPort = value; }
}
public override string RemoteHost {
get { return sshClient.Address; }
set { sshClient.Address = value; }
}
public override int RemotePort
{
get { return sshClient.Port; }
set { sshClient.Port = value; }
}
}
Паттерны
проектирования
Что такое паттерны проектирования
Кристофер Александр: «шаблон описывает задачу, которая снова и снова
возникает в работе, а также принцип ее решения, таким образом, что это
решение можно использовать многократно без изменений».
Шаблоны проектирования (паттерн, pattern) ― это эффективные
способы решения характерных задач проектирования, в частности
проектирования компьютерных программ. Паттерн не является
законченным образцом проекта, который может быть прямо преобразован
в код, скорее это описание или образец для того, как решить задачу,
таким образом, чтобы это можно было использовать в различных
ситуациях.
Шаблон проектирования ― описание взаимодействия объектов и
классов, адаптированных для решения общей задачи проектирования в
конкретном контексте.
Алгоритмы не являются шаблонами, так как они решают задачи
вычисления, а не проектирования.
Каркасы приложений не являются шаблонами, так как они

относятся к какой-то конкретной предметной области

состоят из нескольких шаблонов.
Причины использования и классификация




Возможность многократного использования
Использование чужого опыта взамен самостоятельного изобретения
велосипеда
Единая терминология
Выделение уровня абстракции
Порождающие шаблоны (Creational patterns) абстрагируют процесс
инстанцирования. Они позволяют сделать систему независимой от
способа создания, композиции и представления объектов.
Структурные паттерны (Structural patterns) решают вопрос о том, как из
уже имеющихся классов и объектов оптимально получить более крупные
структуры и образования.
Паттерны поведения (Behavioral patterns) отвечают за инкапсуляцию
алгоритмов и распределение обязанностей между объектами.
Также выделяют паттерны параллельной обработки (concurrency patterns),
системные паттерны (system patterns), интеграционные паттерны (integral
patterns) и т.д.
Изображение класса
Защищенный метод
Открытый метод
Закрытый метод
Отношение ассоциации
Покупатель имеет много счѐтов:
Класс приложения использует класс подключения:
Отношение обобщения
Класс окружности является наследником класса фигуры:
Отношение агрегации
Класс покупателя содержит ссылку на коллекцию
заказов в поле Orders (отношение один ко многим):
Класс заказа содержит ссылку на продукт в поле
Product (отношение один к одному):
Схема описания паттерна

Определение
Содержит краткий ответ на вопросы: каковы функции и
назначение паттерна.

Мотивация
Описание ситуаций, в которых можно использовать данный
паттерн.

Плюсы
Преимущества, получаемые при решении задачи с
использованием данного паттерна.

Диаграмма классов

Участники
Описание классов, задействованных в данном паттерне
проектирования, и их функции.

Пример(ы) кода
Паттерн Одиночка
(Singleton)
Определение и мотивация
Определение
Одиночка — порождающий паттерн, который
гарантирует, что у класса есть только один
экземпляр, и предоставляет к нему глобальную
точку доступа.
Мотивация
 Должен быть ровно один экземпляр некоторого
класса, легко доступный всем клиентам.
 Единственный экземпляр должен расширяться
путем порождения подклассов, и клиентам нужно
иметь возможность работать с расширенным
экземпляром без модификации своего кода.
Плюсы

Контролируемый доступ к единственному экземпляру.
Поскольку класс Singleton инкапсулирует свой единственный
экземпляр, он полностью контролирует то, как и когда клиенты
получают доступ к нему.

Уменьшение числа имен.
Паттерн позволяет избежать засорения пространства имен
глобальными переменными, в которых хранятся уникальные
экземпляры.

Допускает уточнение операций.
Можно параметризировать приложение экземпляром того класса,
который необходим во время выполнения.

Допускает переменное число экземпляров.

Большая гибкость, чем у операций класса (статических методов).
Паттерн одиночка. Диаграмма
Участники
Участники
Singleton ― определяет операцию
получения экземпляра ― статический
метод, который позволяет клиентам
получать доступ к единственному
экземпляру. Может нести ответственность
за создание собственного уникального
экземпляра.
Пример №1
Стандартная реализация:
public class Singleton
{
protected Singleton() { }
private static Singleton instance;
public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
Пример №2
Реализация с использованием шаблонов в языке C#:
public class Singleton<T>
where T : class, new()
// T является классом, имеет конструктор без параметров
{
protected Singleton() { }
private static T instance;
public static T Instance {
get {
if (instance == null)
instance = new T();
return instance;
}
}
}
Пример №2
Данный шаблонный класс используется следующим
образом:
public class TestClass : Singleton<TestClass>
{
public TestClass() { }
public void Operation()
{
// implementation
}
}
Пример №2`
public class Singleton<T> where T : class {
//
Защищенный конструктор по умолчанию необходим для того, чтобы
// предотвратить создание экземпляра класса Singleton
protected Singleton() { }
// Фабрика используется для отложенной инициализации экземпляра класса
private sealed class SingletonCreator<S> where S : class {
// Используется Reflection для создания экземпляра
// класса без публичного конструктора
private static readonly S instance = (S)typeof(S).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null, new Type[0],
new ParameterModifier[0]).Invoke(null);
public static S CreatorInstance {
get { return instance; }
}
}
public static T Instance {
get { return SingletonCreator<T>.CreatorInstance; }
}
Пример №3
При необходимости использования наследников класса
одиночки наиболее гибким методом является
использование реестра одиночек.
public class RegSingleton {
private static RegSingleton instance;
private static Dictionary<string, RegSingleton> registry =
new Dictionary<string, RegSingleton>();
protected RegSingleton() { }
public static void Register(String name, RegSingleton instance)
{
registry.Add(name, instance);
}
protected static RegSingleton Lookup(String name)
{
return registry[name];
}
}
Пример №3
Теперь информацию о том, какой именно экземпляр
одиночки использовать, можно определить на этапе
работы программы:
public class RegSingleton
{
private static RegSingleton instance;
public static RegSingleton Instance()
{
if (instance == null)
{
string name = System.GetProperty("SINGLETON_NAME");
instance = RegSingleton.Lookup(name);
}
return instance;
}
}
Пример №3
Основная проблема использования реестра одиночек
состоит в регистрации экземпляров одиночек.
Например, в языке C# для регистрации используется
статический конструктор.
public class Singleton : RegSingleton
{
static Singleton()
{
new Singleton();
}
protected Singleton()
{
RegSingleton.Register(this.GetType().Name, this);
}
}
Пример №3
В языке Java с той же целью используется статический
блок инициализации:
public class Singleton extends RegSingleton
{
static
{
new Singleton();
}
protected Singleton()
{
RegSingleton.Register(this.getClass().getName(), this);
}
}
Пример №4
В языке C++ все реализации одиночки являются
вариациями его основной формы:
class Singleton
{
public:
static Singleton& Instance()
{
if (!pInstance)
pInstance = new Singleton();
return *pInstance;
}
private:
Singleton() { }
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
~Singleton() { }
static Singleton* pInstance;
};
Пример №4




Все конструкторы объекта закрыты, что не
позволяет пользовательскому коду напрямую
создавать объекты класса Singleton.
Для защиты от удаления метод Instance
возвращает ссылку, а не указатель. Также
деструктор класса объявлен закрытым.
Защиту от копирования обеспечивает закрытый
конструктор копирования.
Для защиты от присваивания (т. к. экземпляр
объекта единственный и любое присваивание
является самоприсваиванием), оператор
присваивания объявлен закрытым.
Пример №5. Синглтон Мейерса
Несмотря на то, что выделенная для объекта-одиночки
память будет освобождена операционной системой,
деструктор объекта должен быть вызван во избежание
утечки ресурсов, которые мог запросить объект.
Первое решение проблемы: Синглтон Мейерса, который
использует локальную статическую переменную:
Singleton& Singleton::Instance()
{
static Singleton obj;
return obj;
}
Пример №6
Синглтон Мейерса работает в большинстве случаев, однако
не решает проблемы висячей ссылки, которая может
возникнуть в случае обращения к объекту-одиночке после
его уничтожения.
Первое решение проблемы висячей ссылки состоит в
создании флага destroyed и генерации исключения при
попытке доступа к уничтоженному объекту:
class Singleton {
public:
static Singleton& Instance() {
if (!pInstance) {
if (destroyed)
OnDeadReference();
else
Create();
}
return *pInstance;
}
};
Пример №6
class Singleton
{
static void OnDeadReference()
{
throw std::runtime_error("Висячая ссылка");
}
static void Create()
{
static Singleton obj;
pInstance = &obj;
}
static bool destroyed;
static Singleton * pInstance;
};
Пример №6. Синглтон Феникс
Второе решение проблемы висячей ссылки состоит в использовании
расширенного варианта оператора new, который позволяет создать
объект заново при обращении к висячей ссылке:
class Singleton {
static void OnDeadReference() {
Create();
// Теперь pInstance указывает на ячейку памяти, где
// ранее размешался объект
new(pInstance) Singleton();
// На месте этих данных вновь создаётся объект
atexit(KillPhoenixSingleton);
// новый объект ставится в очередь на уничтожение
destroyed = false;
}
static void KillPhoenixSingleton() {
pInstance->~Singleton();
// Избегаем освобождения памяти оператором delete
}
};
Функция SetLongevity
Третье решение проблемы висящей ссылки: создать
объект с явно заданным временем жизни.
Время жизни объекта можно задать при помощи
функции SetLongevity из библиотеки Loki:
template<typename T>
void SetLongevity(T * object, unsigned int longevity);
Данная функция гарантирует, что объект object будет
уничтожен не ранее, чем объекты с меньшей
продолжительностью жизни.
Пример №7.
Задание времени жизни синглтона
Пример реализации паттерна одиночка с
применением функции SetLongevity:
class Singleton
{
static void Create()
{
pInstance = new Singleton();
SetLongevity(pInstance, longevity);
}
static const int longevity = 2;
static Singleton * pInstance;
};
Пример №8.
Синглтон в многопоточной среде
Для обеспечения уникальности объекта-одиночки в
многопоточной среде применяется блокировка с двойной
проверкой.
Реализация функции Instance изменяется следующим
образом:
class Singleton {
public:
static Singleton& Instance() {
if (!pInstance) {
// Первая проверка
Lock guard(mutex);
if (!pInstance)
// Вторая проверка
pInstance = new Singleton();
}
return *pInstance;
}
private:
static Mutex mutex;
};
Пример №9.
Синглтон в многопоточной среде
В языке C# блокировка с двойной проверкой
реализуется при помощи оператора lock:
public sealed class Singleton {
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() { }
public static Singleton GetInstance()
{
if (instance == null)
// Первая проверка
{
lock (syncRoot)
{
if (instance == null) // Вторая проверка
instance = new Singleton();
}
}
return instance;
}
}
Download