Топ питань
Часова шкала
Чат
Перспективи

Адаптер (шаблон проєктування)

З Вікіпедії, вільної енциклопедії

Remove ads

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

Призначення

Адаптує інтерфейс одного класу в інший, очікуваний клієнтом. Адаптер забезпечує роботу класів з несумісними інтерфейсами, та найчастіше застосовується тоді, коли система підтримує необхідні дані і поведінку, але має невідповідний інтерфейс.

Застосування

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

Структура

Thumb
UML діаграма, що ілюструє структуру шаблону проєктування Адаптер (з використанням множинного наслідування)

Учасники

Клас Adapter приводить інтерфейс класу Adaptee у відповідність з інтерфейсом класу Target (спадкоємцем якого є Adapter). Це дозволяє об'єктові Client використовувати об'єкт Adaptee так, немов він є екземпляром класу Target.

Наслідки

Шаблон Адаптер дозволяє включати вже наявні об'єкти в нові об'єктні структури, незалежно від відмінностей в їхніх інтерфейсах.

Переваги та недоліки

Переваги

  • Допомагає досягти багаторазового використання та гнучкості.
  • Клас клієнта не ускладнюється необхідністю використовувати інший інтерфейс і може використовувати поліморфізм для обміну між різними реалізаціями адаптерів.

Недоліки

  • Всі запити пересилаються, тому спостерігається незначне збільшення накладних витрат.
  • Іноді багато адаптацій потрібні по ланцюгу адаптера, щоб досягти потрібного типу.
Remove ads

Зв'язок з іншими патернами

  • Фасад створює новий інтерфейс доступу, адаптер — використовує старий

Реалізація

Узагальнити
Перспектива

Інтерфейс класу Adaptee, тобто того, який адаптується, приводиться у відповідність з новими вимогами класу Target, а виклики його методів перетворяться у виклики методів класу Target.

Шаблон Адаптер для адаптації інтерфейсу методу/ів Adaptee до інтерфейсу Target в Adapter можна реалізувати як мінімум двома способами: використовуючи композицію+успадкування (Адаптер об'єкта), або використовуючи множинне успадкування (Адаптер класу).

  • Адаптер об'єкта: Adapter наслідує інтерфейс від Target (успадкування) та містить примірник (здебільшого як вказівник) класу Adaptee (композиція) і делегує виклики своїх методів (які збігаються з інтерфейсом Target) до Adaptee
  • Адаптер класу: Adapter наслідує інтерфейси обох класів Target та Adaptee (множинне успадкування). В ООП-мовах, які не підтримують множинне успадкування, реалізація цього варіанту адаптера дещо складніша (наприклад в Java за допомогою інтерфейсів).

C++

Приклад реалізації на мові С++
#include <iostream>

using namespace std;

// Target Ціль
struct CShape
{
	virtual ~CShape() {};
	virtual void Draw() { cout << "Rectangle" << endl; };
};
// Adaptee Будемо адаптувати
struct CTextViewer
{
	CTextViewer() {};
	virtual ~CTextViewer() {};
	void DrawText() { cout << "Text" << endl; };
};
// Adapter
// Адаптер класу
struct CTextShape : public CShape, protected CTextViewer
{
	CTextShape() {};
	virtual ~CTextShape() {};
	virtual void Draw()
	{
		DrawText(); // Виклик метода класу, що адаптуємо
	};
};
// Адаптер об’єкта
struct CTextShapeOBJ // : public CShape
{
	CTextViewer shape;
	virtual void Draw()
	{
		shape.DrawText(); // Виклик метода класу, що адаптуємо
	};
};
// Client
void DrawObject(CShape* p)
{
	p->Draw();
}
void main()
{
	// Створюємо адаптер і малюємо об'єкт
	CShape* obj = new CTextShape();
	DrawObject(obj);
	delete obj;
	CTextShapeOBJ adapt;
	CTextViewer obj2;
	adapt.shape = obj2;
	adapt.Draw();
}

Java

Приклад реалізації на мові Java
interface LightningPhone {
    void recharge();
    void useLightning();
}

interface MicroUsbPhone {
    void recharge();
    void useMicroUsb();
}

class Iphone implements LightningPhone {
    private boolean connector;

    @Override
    public void useLightning() {
        connector = true;
        System.out.println("Lightning connected");
    }

    @Override
    public void recharge() {
        if (connector) {
            System.out.println("Recharge started");
            System.out.println("Recharge finished");
        } else {
            System.out.println("Connect Lightning first");
        }
    }
}

class Android implements MicroUsbPhone {
    private boolean connector;

    @Override
    public void useMicroUsb() {
        connector = true;
        System.out.println("MicroUsb connected");
    }

    @Override
    public void recharge() {
        if (connector) {
            System.out.println("Recharge started");
            System.out.println("Recharge finished");
        } else {
            System.out.println("Connect MicroUsb first");
        }
    }
}
/* exposing the target interface while wrapping source object */
class LightningToMicroUsbAdapter implements MicroUsbPhone {
    private final LightningPhone lightningPhone;

    public LightningToMicroUsbAdapter(LightningPhone lightningPhone) {
        this.lightningPhone = lightningPhone;
    }

    @Override
    public void useMicroUsb() {
        System.out.println("MicroUsb connected");
        lightningPhone.useLightning();
    }

    @Override
    public void recharge() {
        lightningPhone.recharge();
    }
}

public class AdapterDemo {
    static void rechargeMicroUsbPhone(MicroUsbPhone phone) {
        phone.useMicroUsb();
        phone.recharge();
    }

    static void rechargeLightningPhone(LightningPhone phone) {
        phone.useLightning();
        phone.recharge();
    }

    public static void main(String[] args) {
        Android android = new Android();
        Iphone iPhone = new Iphone();

        System.out.println("Recharging android with MicroUsb");
        rechargeMicroUsbPhone(android);

        System.out.println("Recharging iPhone with Lightning");
        rechargeLightningPhone(iPhone);

        System.out.println("Recharging iPhone with MicroUsb");
        rechargeMicroUsbPhone(new LightningToMicroUsbAdapter (iPhone));
    }
}

Output

Recharging android with MicroUsb
MicroUsb connected
Recharge started
Recharge finished
Recharging iPhone with Lightning
Lightning connected
Recharge started
Recharge finished
Recharging iPhone with MicroUsb
MicroUsb connected
Lightning connected
Recharge started
Recharge finished
Remove ads

Зауваження і коментарі

Узагальнити
Перспектива

Шаблон Адаптер дозволяє в процесі проєктування не брати до уваги можливі відмінності в інтерфейсах вже наявних класів. Якщо є клас, що володіє необхідними методами і властивостями (принаймні, концептуально), то при необхідності завжди можна скористатися шаблоном Адаптер для приведення його інтерфейсу до потрібного вигляду.

Більш прийнятним є адаптер об'єкта, в якому використовується композиція+успадкування, оскільки це більш відповідає правилу "надавайте перевагу композиції, а не успадкуванню". Цей адаптер можна використовувати тільки односторонньо — як заміну для Target. Однак, у випадку, коли ми створюємо двосторонній адаптер, або ж адаптер, який адаптує одночасно кілька Adaptee класів, слід надавати перевагу шаблону адаптера класу. Також адаптер класу дозволяє більш ефективно використовувати вже реалізований код з Target та Adaptee. Однак недоліки адаптера класу випливають з множинного успадкування, коли зміни в деяких базових класах викликають непередбачливі зміни в похідних, а особливо, коли це відбувається одночасно в кількох успадкованих адаптером класах.

Близьким Адаптеру є шаблон Фасад, не завжди можна відрізнити один від другого. Різниця полягає в тому, що шаблон Фасад призначений для спрощення інтерфейсу і створює новий інтерфейс, тоді як шаблон Адаптер використовує з обох сторін інтерфейси, які є в наявності, і забезпечує їх функціювання.

Відомі застосування

Типовим прикладом використання шаблону Адаптер можна назвати створення класів, що приводять до єдиного інтерфейсу функції мови PHP що забезпечують доступ до різних СУБД[1].

Примітки

Джерела

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads