- •Лабораторная работа №4 Шаблоны проектирования
- •Теоретические сведения
- •Отношения между классами. На диаграммах классов языка uml
- •Суть паттерна
- •Решение
- •Адаптер объектов
- •Адаптер классов
- •Адаптер классов не нуждается во вложенном объекте, так как он может одновременно наследовать и часть существующего класса, и часть сервиса.
- •Фасад (Facade)
- •Концептуальный пример
- •Program.Cs:
- •Output.Txt: Результат выполнения
- •Изолирует клиентов от компонентов подсистемы Уменьшая тем самым число объектов, с которыми клиентам приходится иметь дело, упрощая работу с подсистемой.
- •Позволяет ослабить связанность между подсистемой и ее клиентами.
- •Фасад не исключает возможности приложениям напрямую обращаться к классам подсистемы, если это необходимо.
- •Заместитель (Proxy)
- •3. Локальный запуск сервиса (удалённый прокси). Когда настоящий сервисный объект находится на удалённом сервере.
- •Концептуальный пример
- •Декоратор (Decorator)
- •// Объект
- •} //Ptr2 выходит из области видимости, но объект не //освобождается, потому что есть ptr, который по-прежнему //ссылается на него } //ptr выходит из области видимости, и объект уничтожается
- •Пример на языке c#
- •Порождающие шаблоны Абстрактная фабрика (Abstract Factory)
- •1. Шаблон реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы.
- •3. Таким образом, еще раз - предоставляет интерфейс для создания семейств, связанных между собой, или зависимых объектов.
- •Клиент пользуется только интерфейсами, заданными в классах «Абстрактная фабрика» и «Абстрактный продукт».
- •Фабричный метод (Factory Method)
- •Void info() {
- •Void info() {
- •Void info() {
- •Int main()
- •Одиночка (Singleton) Суть паттерна
- •If(!p_instance)
- •Поведенческие шаблоны Стратегия (Strategy)
- •Стратегии построения пути.
- •Структура
- •Концептуальный пример
- •Program.Cs: Пример структуры паттерна
- •Output.Txt: Результат выполнения
- •Void useStrategy(void)
- •Void setStrategy(Strategy* o)
- •Int main(int /*argc*/, char* /*argv*/[])
- •Наблюдатель (Observer) Суть паттерна
- •Решение
- •Структура
- •Шаги реализации
- •Концептуальный пример
- •Program.Cs: Пример структуры паттерна
- •// Random.Next(…) - Метод, возвращает случайное целое число //в указанном диапазоне.
- •Output.Txt: Результат выполнения
- •Использование паттерна Observer
- •Команда (Command)
- •Структура
- •Output.Txt: Результат выполнения
- •Задания для лабораторной работы
Адаптер объектов
Эта реализация использует агрегацию: объект адаптера «оборачивает», то есть содержит ссылку на служебный объект. Такой подход работает во всех языках программирования.
1. Клиент — это класс, который содержит существующую бизнес-логику программы.
2. Клиентский интерфейс описывает протокол, через который клиент может работать с другими классами.
3. Сервис – это какой-то полезный класс, обычно сторонний. Клиент не может использовать этот класс напрямую, так как сервис имеет непонятный ему интерфейс.
4. Адаптер — это класс, который может одновременно работать и с клиентом, и с сервисом. Он реализует клиентский интерфейс и содержит ссылку на объект сервиса. Адаптер получает вызовы от клиента через методы клиентского интерфейса, а затем переводит их в вызовы методов обёрнутого объекта в правильном формате.
5. Работая с адаптером через интерфейс, клиент не привязывается к конкретному классу адаптера. Благодаря этому, вы можете добавлять в программу новые виды адаптеров, независимо от клиентского кода. Это может пригодиться, если интерфейс сервиса вдруг изменится, например, после выхода новой версии сторонней библиотеки.
Адаптер классов
Эта реализация базируется на наследовании: адаптер наследует оба интерфейса одновременно. Такой подход возможен только в языках, поддерживающих множественное наследование, например, C++.
Адаптер классов не нуждается во вложенном объекте, так как он может одновременно наследовать и часть существующего класса, и часть сервиса.
Адаптер уровня класса использует множественное наследование (интерфейс наследуется от одного класса, а реализация — от другого),
В Адаптере уровня объекта применяется композиция объектов (как правило, в объекте определяется ссылка на другой объект).
Паттерн применяется,
- если требуется использовать существующий класс с интерфейсом, не подходящим к нашим требованиям;
- а также, если требуется создать повторно используемый класс, который должен взаимодействовать с заранее не известными или не связанными с ним классами, имеющими несовместимые интерфейсы.
Проблема (решаемая задача): необходимо обеспечить взаимодействие несовместимых интерфейсов или создать единый устойчивый интерфейс для нескольких компонентов с разными интерфейсами.
Решение: преобразовать исходный интерфейс компонента к другому виду с помощью промежуточного объекта адаптера, то есть, добавить специальный объект с общим интерфейсом в рамках данного приложения и перенаправить связи от внешних объектов к этому объекту адаптеру.
Класс Adapter приводит интерфейс класса Adaptee в соответствие с интерфейсом класса Target (наследником которого является Adapter ).
Это позволяет объекту Client использовать объект Adaptee (посредством адаптера Adapter) так, словно он является экземпляром класса Target.
Таким образом Client обращается к интерфейсу Target, реализованному в наследнике Adapter, который перенаправляет обращение к Adaptee .
Шаблон Адаптер позволяет включать уже существующие объекты в новые объектные структуры, независимо от различий в их интерфейсах.
Этот шаблон позволяет в процессе проектирования не принимать во внимание возможные различия в интерфейсах уже существующих классов.
Если есть класс, обладающий требуемыми методами и свойствами (по крайней мере, концептуально), то при необходимости всегда можно воспользоваться шаблоном Адаптер для приведения его интерфейса к нужному виду.
Реализация на C#
Пример реализации адаптера в C#:
1. Создайте интерфейс, который будет использоваться в качестве адаптера:
|
public interface ITarget { void Request(); }
|
2. Создайте класс, который нужно адаптировать (Adaptee):
|
public class Adaptee { public void SpecificRequest() { Console.WriteLine("Adaptee method called"); } }
|
3. Создайте класс-адаптер, который будет использовать интерфейс ITarget и связывать его с классом Adaptee:
|
public class Adapter : ITarget { private readonly Adaptee _adaptee;
public Adapter(Adaptee adaptee) { _adaptee = adaptee; }
public void Request() { _adaptee.SpecificRequest(); } } |
4. Используйте класс-адаптер, чтобы вызывать метод Adaptee из интерфейса ITarget:
|
static void Main(string[] args) { Adaptee adaptee = new Adaptee();
ITarget target = new Adapter(adaptee);
target.Request(); } |
В результате выполнения программы будет вызван метод SpecificRequest класса Adaptee через интерфейс ITarget, что позволяет работать с двумя объектами с несовместимыми интерфейсами.
using System;
namespace DesignPatterns.Adapter.Conceptual
{
// Целевой класс объявляет интерфейс, с которым может работать клиентский
// код.
public interface ITarget
{
string GetRequest();
}
// Адаптируемый класс содержит некоторое полезное //поведение, но его
// интерфейс несовместим с существующим клиентским //кодом. Адаптируемый
// класс нуждается в некоторой доработке, прежде чем //клиентский код сможет
// его использовать.
class Adaptee
{
public string GetSpecificRequest()
{
return "Specific request.";
}
}
// Адаптер делает интерфейс Адаптируемого класса //совместимым с целевым
// интерфейсом.
class Adapter : ITarget
{
private readonly Adaptee _adaptee;
public Adapter(Adaptee adaptee)
{
this._adaptee = adaptee;
}
public string GetRequest()
{
return $"This is '{this._adaptee.GetSpecificRequest()}'";
}
}
class Program
{
static void Main(string[] args)
{
Adaptee adaptee = new Adaptee();
ITarget target = new Adapter(adaptee);
Console.WriteLine("Adaptee interface is incompatible with the client.");
Console.WriteLine("But with adapter client can call it's method.");
Console.WriteLine(target.GetRequest());
}
}
}
Output.txt: Результат выполнения
Adaptee interface is incompatible with the client.
But with adapter client can call it's method.
This is 'Specific request.'
Интерфейс Адаптивного приложения несовместим с клиентом.
Но с адаптером клиент может вызвать это метод.
Это 'Specific request.' //"Конкретный запрос"
using System;
namespace Adapter
{
class MainApp
{
static void Main()
{
// Create adapter and place a request
Target target = new Adapter();
target.Request();
// Wait for user
Console.Read();
}
}
// "Target"
class Target
{
public virtual void Request()
{
Console.WriteLine("Called Target Request()");
}
}
// "Adapter"
class Adapter : Target
{
private Adaptee adaptee = new Adaptee();
public override void Request() /* Модификатор override требуется для расширения или изменения абстрактной или виртуальной реализации унаследованного метода, свойства, индексатора или события */
{
// Possibly do some other work
// and then call SpecificRequest
adaptee.SpecificRequest();
}
}
// "Adaptee"
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Called SpecificRequest()");
}
}
}
Типичным примером использования шаблона Адаптер можно назвать создание классов, приводящих к единому интерфейсу функции языка PHP обеспечивающие доступ к различным СУБД.
Вариант решения данной проблемы с использованием шаблона Адаптер показан на рисунке.
Рис. 1
