'

Паттерн Адаптер

Понравилась презентация – покажи это...





Слайд 0

Паттерн Адаптер (Adapter)


Слайд 1

Определение Определение Адаптер — структурный шаблон проектирования, предназначенный для организации использования функций объекта, нежелательного для модификации, через уже существующий интерфейс. Реализация ? адаптер класса (множественное наследование классов или интерфейсов); ? адаптер объекта (композиция).


Слайд 2

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


Слайд 3

Плюсы Плюсы: ? Инкапсуляция реализации внешних классов (компонентов, библиотек). Таким образом, система становится независимой от интерфейса внешних классов. ? Переход на использование других (несовместимых по интерфейсу) внешних классов не требует переделки самой системы, достаточно заменить реализацию соответствующих адаптеров.


Слайд 4

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


Слайд 5

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


Слайд 6

Участники Target — целевой: определяет зависящий от предметной области интерфейс, которым пользуется Client. Client — клиент: вступает во взаимоотношения с объектами, удовлетворяющими интерфейсу Target. Adaptee — адаптируемый: определяет существующий интерфейс, который нуждается в адаптации. Adapter — адаптер: адаптирует интерфейс Adaptee к интерфейсу Target.


Слайд 7

Пример №1 Пусть есть унаследованный класс прямоугольника: 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_; };


Слайд 8

Пример №1 Ожидаемый клиентом интерфейс выглядит следующим образом: class Rectangle { public: virtual void Draw() = 0; };


Слайд 9

Пример №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(); } };


Слайд 10

Пример №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(); }


Слайд 11

Пример №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(); } }


Слайд 12

Пример №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; } } }


Слайд 13

Паттерны проектирования


Слайд 14

Что такое паттерны проектирования Кристофер Александр: «шаблон описывает задачу, которая снова и снова возникает в работе, а также принцип ее решения, таким образом, что это решение можно использовать многократно без изменений». Шаблоны проектирования (паттерн, pattern) ? это эффективные способы решения характерных задач проектирования, в частности проектирования компьютерных программ. Паттерн не является законченным образцом проекта, который может быть прямо преобразован в код, скорее это описание или образец для того, как решить задачу, таким образом, чтобы это можно было использовать в различных ситуациях. Шаблон проектирования ? описание взаимодействия объектов и классов, адаптированных для решения общей задачи проектирования в конкретном контексте. Алгоритмы не являются шаблонами, так как они решают задачи вычисления, а не проектирования. Каркасы приложений не являются шаблонами, так как они ? ? относятся к какой-то конкретной предметной области состоят из нескольких шаблонов.


Слайд 15

Причины использования и классификация ? ? ? ? Возможность многократного использования Использование чужого опыта взамен самостоятельного изобретения велосипеда Единая терминология Выделение уровня абстракции Порождающие шаблоны (Creational patterns) абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Структурные паттерны (Structural patterns) решают вопрос о том, как из уже имеющихся классов и объектов оптимально получить более крупные структуры и образования. Паттерны поведения (Behavioral patterns) отвечают за инкапсуляцию алгоритмов и распределение обязанностей между объектами. Также выделяют паттерны параллельной обработки (concurrency patterns), системные паттерны (system patterns), интеграционные паттерны (integral patterns) и т.д.


Слайд 16

Изображение класса Открытый метод Защищенный метод Закрытый метод


Слайд 17

Отношение ассоциации Покупатель имеет много сч?тов: Класс приложения использует класс подключения:


Слайд 18

Отношение обобщения Класс окружности является наследником класса фигуры:


Слайд 19

Отношение агрегации Класс покупателя содержит ссылку на коллекцию заказов в поле Orders (отношение один ко многим): Класс заказа содержит ссылку на продукт в поле Product (отношение один к одному):


Слайд 20

Схема описания паттерна ? ? ? ? ? ? Определение Содержит краткий ответ на вопросы: каковы функции и назначение паттерна. Мотивация Описание ситуаций, в которых можно использовать данный паттерн. Плюсы Преимущества, получаемые при решении задачи с использованием данного паттерна. Диаграмма классов Участники Описание классов, задействованных в данном паттерне проектирования, и их функции. Пример(ы) кода


Слайд 21

Паттерн Одиночка (Singleton)


Слайд 22

Определение и мотивация Определение Одиночка — порождающий паттерн, который гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа. Мотивация ? Должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам. ? Единственный экземпляр должен расширяться путем порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.


Слайд 23

Плюсы ? ? ? ? ? Контролируемый доступ к единственному экземпляру. Поскольку класс Singleton инкапсулирует свой единственный экземпляр, он полностью контролирует то, как и когда клиенты получают доступ к нему. Уменьшение числа имен. Паттерн позволяет избежать засорения пространства имен глобальными переменными, в которых хранятся уникальные экземпляры. Допускает уточнение операций. Можно параметризировать приложение экземпляром того класса, который необходим во время выполнения. Допускает переменное число экземпляров. Большая гибкость, чем у операций класса (статических методов).


Слайд 24

Паттерн одиночка. Диаграмма


Слайд 25

Участники Участники Singleton ? определяет операцию получения экземпляра ? статический метод, который позволяет клиентам получать доступ к единственному экземпляру. Может нести ответственность за создание собственного уникального экземпляра.


Слайд 26

Пример №1 Стандартная реализация: public class Singleton { protected Singleton() { } private static Singleton instance; public static Singleton Instance { get { if (instance == null) instance = new Singleton(); return instance; } } }


Слайд 27

Пример №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; } } }


Слайд 28

Пример №2 Данный шаблонный класс используется следующим образом: public class TestClass : Singleton<TestClass> { public TestClass() { } public void Operation() { // implementation } }


Слайд 29

Пример №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; } } }


Слайд 30

Пример №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]; } }


Слайд 31

Пример №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; } }


Слайд 32

Пример №3 Основная проблема использования реестра одиночек состоит в регистрации экземпляров одиночек. Например, в языке C# для регистрации используется статический конструктор. public class Singleton : RegSingleton { static Singleton() { new Singleton(); } protected Singleton() { RegSingleton.Register(this.GetType().Name, this); } }


Слайд 33

Пример №3 В языке Java с той же целью используется статический блок инициализации: public class Singleton extends RegSingleton { static { new Singleton(); } protected Singleton() { RegSingleton.Register(this.getClass().getName(), this); } }


Слайд 34

Пример №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; };


Слайд 35

Пример №4 ? ? ? ? Все конструкторы объекта закрыты, что не позволяет пользовательскому коду напрямую создавать объекты класса Singleton. Для защиты от удаления метод Instance возвращает ссылку, а не указатель. Также деструктор класса объявлен закрытым. Защиту от копирования обеспечивает закрытый конструктор копирования. Для защиты от присваивания (т. к. экземпляр объекта единственный и любое присваивание является самоприсваиванием), оператор присваивания объявлен закрытым.


Слайд 36

Пример №5. Синглтон Мейерса Несмотря на то, что выделенная для объекта-одиночки память будет освобождена операционной системой, деструктор объекта должен быть вызван во избежание утечки ресурсов, которые мог запросить объект. Первое решение проблемы: Синглтон Мейерса, который использует локальную статическую переменную: Singleton& Singleton::Instance() { static Singleton obj; return obj; }


Слайд 37

Пример №6 Синглтон Мейерса работает в большинстве случаев, однако не решает проблемы висячей ссылки, которая может возникнуть в случае обращения к объекту-одиночке после его уничтожения. Первое решение проблемы висячей ссылки состоит в создании флага destroyed и генерации исключения при попытке доступа к уничтоженному объекту: class Singleton { public: static Singleton& Instance() { if (!pInstance) { if (destroyed) OnDeadReference(); else Create(); } return *pInstance; } };


Слайд 38

Пример №6 class Singleton { static void OnDeadReference() { throw std::runtime_error("Висячая ссылка"); } static void Create() { static Singleton obj; pInstance = &obj; } static bool destroyed; static Singleton * pInstance; };


Слайд 39

Пример №6. Синглтон Феникс Второе решение проблемы висячей ссылки состоит в использовании расширенного варианта оператора new, который позволяет создать объект заново при обращении к висячей ссылке: class Singleton { static void OnDeadReference() { Create(); // Теперь pInstance указывает на ячейку памяти, где // ранее размешался объект new(pInstance) Singleton(); // На месте этих данных вновь создаётся объект atexit(KillPhoenixSingleton); // новый объект ставится в очередь на уничтожение destroyed = false; } static void KillPhoenixSingleton() { pInstance->~Singleton(); // Избегаем освобождения памяти оператором delete } };


Слайд 40

Функция SetLongevity Третье решение проблемы висящей ссылки: создать объект с явно заданным временем жизни. Время жизни объекта можно задать при помощи функции SetLongevity из библиотеки Loki: template<typename T> void SetLongevity(T * object, unsigned int longevity); Данная функция гарантирует, что объект object будет уничтожен не ранее, чем объекты с меньшей продолжительностью жизни.


Слайд 41

Пример №7. Задание времени жизни синглтона Пример реализации паттерна одиночка с применением функции SetLongevity: class Singleton { static void Create() { pInstance = new Singleton(); SetLongevity(pInstance, longevity); } static const int longevity = 2; static Singleton * pInstance; };


Слайд 42

Пример №8. Синглтон в многопоточной среде Для обеспечения уникальности объекта-одиночки в многопоточной среде применяется блокировка с двойной проверкой. Реализация функции Instance изменяется следующим образом: class Singleton { public: static Singleton& Instance() { if (!pInstance) { Lock guard(mutex); if (!pInstance) // Первая проверка // Вторая проверка pInstance = new Singleton(); } return *pInstance; } private: static Mutex mutex; };


Слайд 43

Пример №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; } }


×

HTML:





Ссылка: