адаптер - Будь-який реальний приклад шаблону адаптера [закрито]


84

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

Чи можете ви поділитися будь-яким прикладом вивчення шаблону адаптера?

ps Я намагався шукати існуючі запитання щодо stackoverflow, але не знайшов відповіді, тому опублікував це як нове запитання. Якщо ви знаєте, що на це вже є відповідь, будь ласка, переспрямуйте.


4
Ну, якщо ви хочете демонструвати це. Ви повинні мати готовий приклад цього у своєму оточенні, насправді декілька. Інакше, чому ви хочете демонструвати це?
Тоні Гопкінсон,

1
Кілька прикладів тут. stackoverflow.com/questions/1673841/…
r4.

1
Мета @TonyHopkinson - поінформувати людей про цей шаблон дизайну на реальному прикладі.
AksharRoop

10
@AksharRoop. Шаблон дизайну призначений для вирішення проблеми, а не для пошуку проблеми. Найкращий приклад - це у вашому власному "світі".
Тоні Хопкінсон,

1
@TonyHopkinson, можливо, я використав тут неправильний термін демонстрації, але я мав на увазі пояснити концепцію цього шаблону на гарному прикладі. Я згоден, що мені слід знайти його у власній системі ...
AksharRoop

Відповіді:


77

Багато прикладів адаптера є тривіальними або нереальними ( Прямокутник проти LegacyRectangle, Храповий проти Сокет , Квадратний пег проти RoundPeg , Качка проти Туреччини ). Гірше того, що багато хто не показує кілька адаптерів для різних адаптерів ( хтось згадав Java Arrays.asList як приклад шаблону адаптера ). Адаптація інтерфейсу лише одного класу для роботи з іншим здається слабким прикладом шаблону GoF Adapter. Цей шаблон використовує успадкування та поліморфізм, тому можна очікувати, що хороший приклад показує кілька реалізацій адаптерів для різних адаптерів .

Кращий приклад я знайшов в главі 26 Застосування UML і шаблонів: Введення в об'єктно-орієнтованого аналізу і проектування та ітераційних розвитку (третє видання) . Наступні зображення взяті з інструкторського матеріалу, розміщеного на FTP-сайті для книги.

Перший показує, як програма може використовувати кілька реалізацій (адаптерів), які функціонально подібні (наприклад, податкові калькулятори, модулі бухгалтерського обліку, послуги авторизації кредитів тощо), але мають різні API. Ми хочемо уникнути жорсткого кодування нашого коду рівня домену для обробки різних можливих способів обчислення податку, післяпродажних продажів, авторизації запитів на кредитні картки тощо. Це всі зовнішні модулі, які можуть відрізнятися, і для яких ми не можемо змінити код. Адаптер дозволяє нам виконувати жорстке кодування в адаптері, тоді як наш код рівня домену завжди використовує один і той же інтерфейс (інтерфейс IWrelyAdapter).

Мал. 26.1

На наведеному малюнку ми не бачимо фактичних адаптантів. Однак на наступному малюнку показано, як здійснюється поліморфний виклик postSale(...)в інтерфейсі IAccountingAdapter, що призводить до проводки продажу через SOAP до системи SAP.

Рис.26.2


цей приклад використання сеансів теж непоганий (хоча реалізація, на мою думку, не зовсім правильна, використовуючи статику): community.sitepoint.com/t/phpunit-testing-cookies-and-sessions/…
Алехандро Морено


50

Як перетворити француза в нормальну людину ...

 public interface IPerson
    {
        string Name { get; set; }
    }

    public interface IFrenchPerson
    {
        string Nom { get; set; }
    }

    public class Person : IPerson
    {
        public string Name { get; set; }
    }

    public class FrenchPerson : IFrenchPerson
    {
        public string Nom { get; set; }
    }

    public class PersonService
    {
        public void PrintName(IPerson person)
        {
            Debug.Write(person.Name);
        }
    }

    public class FrenchPersonAdapter : IPerson
    {
        private readonly IFrenchPerson frenchPerson;

        public FrenchPersonAdapter(IFrenchPerson frenchPerson)
        {
            this.frenchPerson = frenchPerson;
        }

        public string Name 
        {
            get { return frenchPerson.Nom; }
            set { frenchPerson.Nom = value; }
        }
    } 

Приклад

    var service = new PersonService();
    var person = new Person();
    var frenchPerson = new FrenchPerson();

    service.PrintName(person);
    service.PrintName(new FrenchPersonAdapter(frenchPerson));

19
Я французька, і мене ображає те, що ви не вважаєте мене справжньою людиною. (JK)
ZeroUltimax

13
@ZeroUltimax Я майже впевнений, що цей код не буде скомпільований у Квебеку.
Fuhrmanator

Будь-який кодер без знання адаптерів легко вирішив би проблему. Як знання теорії адаптерів допомагають заощадити час або зробити рішення кращим? Чи кінцевою метою є використання спеціального класу замість використання лише методу?
Роуан Гонтьє

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

3
Це єдиний кумедний - і, можливо, один із найдоступніших - прикладів того, як використовувати шаблон адаптера, який я коли-небудь стикався.
Mass Dot Net

42

Перетворення інтерфейсу в інший інтерфейс.

Будь-який реальний приклад шаблону адаптера

Для підключення живлення ми маємо різні інтерфейси по всьому світу. Використовуючи адаптер, ми можемо легко підключатись як мудро

введіть тут опис зображення


Ось деякий код, що відповідає цьому: codeproject.com/Tips/595716/Adapter-Design-Pattern-in-Cplusplus
Raghav Navada


13

Ось приклад, що імітує перетворення analog dataв digit data.

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


Код

AnalogSignal.java

package eric.designpattern.adapter;

public interface AnalogSignal {
    float[] getAnalog();

    void setAnalog(float[] analogData);

    void printAnalog();
}

DigitSignal.java

package eric.designpattern.adapter;

public interface DigitSignal {
    byte[] getDigit();

    void setDigit(byte[] digitData);

    void printDigit();
}

FloatAnalogSignal.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FloatAnalogSignal implements AnalogSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private float[] data;

    public FloatAnalogSignal(float[] data) {
        this.data = data;
    }

    @Override
    public float[] getAnalog() {
        return data;
    }

    @Override
    public void setAnalog(float[] analogData) {
        this.data = analogData;
    }

    @Override
    public void printAnalog() {
        logger.info("{}", Arrays.toString(getAnalog()));
    }
}

BinDigitSignal.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BinDigitSignal implements DigitSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private byte[] data;

    public BinDigitSignal(byte[] data) {
        this.data = data;
    }

    @Override
    public byte[] getDigit() {
        return data;
    }

    @Override
    public void setDigit(byte[] digitData) {
        this.data = digitData;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }
}

AnalogToDigitAdapter.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * Adapter - convert analog data to digit data.
 * </p>
 * 
 * @author eric
 * @date Mar 8, 2016 1:07:00 PM
 */
public class AnalogToDigitAdapter implements DigitSignal {
    public static final float DEFAULT_THRESHOLD_FLOAT_TO_BIN = 1.0f; // default threshold,
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private AnalogSignal analogSignal;
    private byte[] digitData;
    private float threshold;
    private boolean cached;

    public AnalogToDigitAdapter(AnalogSignal analogSignal) {
        this(analogSignal, DEFAULT_THRESHOLD_FLOAT_TO_BIN);
    }

    public AnalogToDigitAdapter(AnalogSignal analogSignal, float threshold) {
        this.analogSignal = analogSignal;
        this.threshold = threshold;
        this.cached = false;
    }

    @Override
    public synchronized byte[] getDigit() {
        if (!cached) {
            float[] analogData = analogSignal.getAnalog();
            int len = analogData.length;
            digitData = new byte[len];

            for (int i = 0; i < len; i++) {
                digitData[i] = floatToByte(analogData[i]);
            }
        }

        return digitData;
    }

    // not supported, should set the inner analog data instead,
    @Override
    public void setDigit(byte[] digitData) {
        throw new UnsupportedOperationException();
    }

    public synchronized void setAnalogData(float[] analogData) {
        invalidCache();
        this.analogSignal.setAnalog(analogData);
    }

    public synchronized void invalidCache() {
        cached = false;
        digitData = null;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }

    // float -> byte convert,
    private byte floatToByte(float f) {
        return (byte) (f >= threshold ? 1 : 0);
    }
}

Код - Тестовий кейс

AdapterTest.java

package eric.designpattern.adapter.test;

import java.util.Arrays;

import junit.framework.TestCase;

import org.junit.Test;

import eric.designpattern.adapter.AnalogSignal;
import eric.designpattern.adapter.AnalogToDigitAdapter;
import eric.designpattern.adapter.BinDigitSignal;
import eric.designpattern.adapter.DigitSignal;
import eric.designpattern.adapter.FloatAnalogSignal;

public class AdapterTest extends TestCase {
    private float[] analogData = { 0.2f, 1.4f, 3.12f, 0.9f };
    private byte[] binData = { 0, 1, 1, 0 };
    private float[] analogData2 = { 1.2f, 1.4f, 0.12f, 0.9f };

    @Test
    public void testAdapter() {
        AnalogSignal analogSignal = new FloatAnalogSignal(analogData);
        analogSignal.printAnalog();

        DigitSignal digitSignal = new BinDigitSignal(binData);
        digitSignal.printDigit();

        // adapter
        AnalogToDigitAdapter adAdapter = new AnalogToDigitAdapter(analogSignal);
        adAdapter.printDigit();
        assertTrue(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));

        adAdapter.setAnalogData(analogData2);
        adAdapter.printDigit();
        assertFalse(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));
    }
}

Залежність - через maven

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>

Як тестувати

Просто запустіть модульний тест.


7

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

Прикладами з реального світу можуть бути перекладач мови або мобільний зарядний пристрій. Більше тут у цьому відео YouTube:

Youtube - Шаблон дизайну адаптера: Вступ


3

Ви можете використовувати шаблон дизайну адаптера, коли вам доводиться мати справу з різними інтерфейсами з подібною поведінкою (що зазвичай означає класи з подібною поведінкою, але з різними методами). Прикладом цього може бути клас для підключення до телевізора Samsung, а інший - для підключення до телевізора Sony. Вони поділять загальну поведінку, наприклад, відкрите меню, починають відтворення, підключаються до мережі тощо, але кожна бібліотека матиме різну реалізацію (з різними іменами методів та підписами). Ці різні реалізації конкретних постачальників в діаграмах UML називаються Adaptee .

Отже, у коді (який називається клієнтом на діаграмах UML), замість жорсткого коду виклики методів кожного постачальника (або Adaptee ), ви можете створити загальний інтерфейс ( у діаграмах UML називається Target ), щоб обернути подібні способи поведінки та працювати. лише з одним типом об’єкта.

Ці адаптери будуть потім реалізувати Target інтерфейсу делегував свої виклики методів до Adaptees , які передаються в адаптери з допомогою конструктора.

Для того, щоб ви зрозуміли це в коді Java, я написав дуже простий проект, використовуючи точно такий же приклад, згаданий вище, використовуючи адаптери для роботи з різними інтерфейсами Smart TV. Код невеликий, добре задокументований і зрозумілий, тому копайтеся на ньому, щоб побачити, як може виглядати реалізація в реальному світі.

Просто завантажте код і імпортуйте його до Eclipse (або вашої улюбленої IDE) як проект Maven. Ви можете виконати код, запустивши org.example.Main.java . Пам'ятайте, що найголовніше тут - зрозуміти, як класи та інтерфейси збираються разом для проектування шаблону. Я також створив кілька підроблених адаптерів у пакеті com.thirdparty.libs . Сподіваюся, це допоможе!

https://github.com/Dannemann/java-design-patterns


2

Одним із реальних прикладів є Qt-Dbus.

Qt-dbus має утиліту для генерації адаптера та коду інтерфейсу із наданого файлу xml. Ось кроки для цього.

 1. Create the xml file - this xml file should have the interfaces 
that can be viewed by the qdbus-view in the system either on 
the system or session bus.

    2.With the utility - qdbusxml2cpp , you generate the interface adaptor code. 
This interface adaptor does the demarshalling of the data that is 
received from the client. After demarshalling, it invokes the 
user defined - custom methods ( we can say as adaptee).

    3. At the client side, we generate the interface from the xml file. 
This interface is invoked by the client. The interface does the 
marshalling of the data and invokes the adaptor interface. As told 
in the point number 2, the adaptor interface does the demarshalling 
and calls the adaptee - user defined methods.

Повний приклад Qt-Dbus ви можете побачити тут -

http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/


2

Ви можете знайти реалізацію PHP шаблону адаптера, який використовується як захист від ін'єкційних атак тут:

http://www.php5dp.com/category/design-patterns/adapter-composition/

Одним із цікавих аспектів шаблону адаптера є те, що він має два варіанти: адаптер класу, що покладається на багаторазове успадкування, та об’єктний адаптер, що покладається на композицію. Наведений приклад спирається на склад.


Посилання php5dp.com/category/design-patterns/adapter-composition більше не працює
FantomX1

2

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

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

public interface IWetherFinder {
    public double getTemperature(String cityName);
}

class WeatherFinder implements IWetherFinder{
   @Override
   public double getTemperature(String cityName){
     return 40;
   }
}

interface IWeatherFinderClient
{
   public double getTemperature(String zipcode);
}  

public class WeatherAdapter implements IWeatherFinderClient {

    @Override
    public double getTemperature(String zipcode) {

        //method to get cityname by zipcode 
        String cityName = getCityName(zipcode);

        //invoke actual service
        IWetherFinder wetherFinder = new WeatherFinder();
        return wetherFinder.getTemperature(cityName);
    }

    private String getCityName(String zipCode) {
        return "Banaglore";
    }
}

0

Реальним прикладом можуть бути звітні документи в заявці. Простий код, як тут.

Адаптери, на мою думку, дуже корисні для структури програмування.

class WordAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Word");
    }
}

class ExcellAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Excel");
    }
}


class ReportAdapter implements IReport{
    WordAdaptee wordAdaptee=new WordAdaptee();
    @Override
    public void report(String s) {
        wordAdaptee.report(s);
    }
}

interface IReport {
    public void report(String s);
}

public class Main {
    public static void main(String[] args) {

        //create the interface that client wants
        IReport iReport=new ReportAdapter();

        //we want to write a report both from excel and world
        iReport.report("Trial report1 with one adaptee");  //we can directly write the report if one adaptee is avaliable 

        //assume there are N adaptees so it is like in our example
        IReport[] iReport2={new ExcellAdaptee(),new WordAdaptee()};

        //here we can use Polymorphism here  
        for (int i = 0; i < iReport2.length; i++) {
            iReport2[i].report("Trial report 2");
        }
    }
}

Результати будуть:

Trial report1 with one adaptee Word
Trial report 2 Excel
Trial report 2 Word

1
Це насправді проксі. Адаптер і адаптер мають різні інтерфейси. Вони не реалізують однаковий інтерфейс. Ось що робить проксі.
dvallejo

Це не шаблон адаптера. Шаблон адаптера використовується для реалізації цільового інтерфейсу, який адаптований не реалізує.
FedericoG

0

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

У вас є програма, яка зчитує всі рядки у файлі у структурі даних List і відображає їх у сітці (назвемо базовий інтерфейс сховища даних IDataStore). Користувач може переміщатися між цими даними, натискаючи кнопки "Перша сторінка", "Попередня сторінка", "Наступна сторінка", "Остання сторінка". Все працює нормально.

Тепер додаток потрібно використовувати з робочими журналами, які занадто великі для читання в пам'ять, але користувачеві все одно потрібно переходити по ньому! Одним із рішень було б застосувати кеш, який зберігає першу сторінку, наступну, попередню та останню сторінки. Що ми хочемо, це коли користувач натискає "Наступна сторінка", ми повертаємо сторінку з кешу та оновлюємо кеш; коли вони натискають останню сторінку, ми повертаємо останню сторінку з кешу. У фоновому режимі у нас є файловий потік, що робить всю магію. Таким чином ми маємо лише чотири сторінки пам’яті на відміну від цілого файлу.

Ви можете використовувати адаптер, щоб додати цю нову функцію кешу до своєї програми, не користувач цього помітив. Ми розширюємо поточний IDataStore і називаємо його CacheDataStore. Якщо файл для завантаження великий, ми використовуємо CacheDataStore. Коли ми робимо запит на першу, наступну, попередню та останню сторінки, інформація направляється в наш кеш.

І хто знає, завтра бос хоче почати читати файли з таблиці бази даних. Все, що ви робите, - це все-таки розширити IDataStore до SQLDataStore, як це було зроблено для Cache, налаштувати підключення у фоновому режимі. Коли вони натискають кнопку Далі, ви генеруєте необхідний запит sql для отримання наступних сотень рядків із бази даних.

По суті, оригінальний інтерфейс програми не змінився. Ми просто адаптували сучасні та цікаві функції для роботи, зберігаючи застарілий інтерфейс.


Я не розумію? Здається, ви щойно використали існуючий інтерфейс і застосували методи? Де різний інтерфейс, до якого потрібно адаптуватися, і клас адаптера?
berimbolo

@berimbolo Ваша плутанина дійсна, оскільки наведений вище приклад чітко не говорить про шаблон адаптера.
rahil008

0

Приклад @Justice o не говорить чітко про шаблон адаптера. Розширюючи свою відповідь - Ми маємо існуючий інтерфейс IDataStore, який використовує наш споживчий код, і ми не можемо його змінити. Тепер нас просять використовувати класний новий клас із бібліотеки XYZ, який робить те, що ми хочемо реалізувати, але але, але ми не можемо змінити цей клас, щоб розширити наш IDataStore, вже бачили проблему? Створюючи новий клас - ADAPTER, який реалізує інтерфейс, який очікує наш споживчий код, тобто IDataStore, і за допомогою класу з бібліотеки, функції якого ми повинні мати - ADAPTEE, як член нашого ADAPTER, ми можемо досягти того, що ми хотіли.



0

Прикладом із фреймворку Yii може бути: Yii використовує внутрішній кеш, використовуючи інтерфейс ICache. https://www.yiiframework.com/doc/api/1.1/ICache

чий підпис схожий на: -

abstract public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency $dependency=NULL)
abstract public mixed get(string $id)

Скажімо, ви хотіли б використовувати всередині проекту Yii бібліотеку кешу symfony https://packagist.org/packages/symfony/cache з її кеш-інтерфейсом, визначивши цю послугу в конфігурації компонентів служб Yii (локатор служб) https: / /github.com/symfony/cache-contracts/blob/master/CacheInterface.php

    public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);

Ми бачимо, що кеш symfony має інтерфейс лише з методом get, відсутність методу set та інший підпис для методу get, оскільки Symfony використовує метод get також як сеттер при наданні другого параметра, що викликається.

Оскільки ядро ​​Yii внутрішньо використовує цей кеш / інтерфейс Yii, важко (розширюючи Yii / YiiBase) місцями переписати виклики на цей інтерфейс.

Плюс кеш Symfony не є нашим класом, тому ми не можемо переписати його інтерфейс, щоб він відповідав інтерфейсу кешування Yii.

Отож, для порятунку з’являється модель адаптера Ми напишемо mapping = проміжний адаптер, який буде відображати виклики інтерфейсу кешу Yii на кеш-інтерфейс Symfony

Виглядало б так

    class YiiToSymfonyCacheAdapter implements \Yii\system\caching\ICache
    {
        private \Symfony\Contracts\Cache\CacheInterface $symfonyCache;

        public function __construct(\Symfony\Contracts\Cache\CacheInterface $symfonyCache)
        {
            $this->symfonyCache = $symfonyCache;
        }

      
      public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency 
       $dependency=NULL) 
      {

          // https://symfony.com/doc/current/cache.html
          return $this->symfonyCache->get(
              $id, 
              function($item) { 
              // some logic .. 
               return $value; 
              }
          );

//          https://github.com/symfony/cache/blob/master/Adapter/MemcachedAdapter.php
// if a class could be called statically, the adapter could call statically also eg. like this
//          return \Symfony\Component\Cache\Adapter\MemcacheAdapter::get(
//              $id, 
//              function($item) { 
//              // some logic .. 
//               return $value; 
//              }
          );
       }

       public mixed get(string $id) 
       {
           // https://github.com/symfony/cache/blob/master/Adapter/FilesystemAdapter.php 
           // if a class could be called statically, the adapter could call statically also eg. like this
           // \Symfony\Component\Cache\Adapter\FileSystemAdapter::get($id)
           return $this->symfonyCache->get($id) 
       }
    } 


-1

Це приклад реалізації адаптера:

interface NokiaInterface {
    chargementNokia(x:boolean):void
}


class SamsungAdapter implements NokiaInterface {
//nokia chargement adapted to samsung
    chargementNokia(x:boolean){
        const old= new SamsungCharger();
        let y:number = x ? 20 : 1;
        old.charge(y);
      }
}


class SamsungCharger {
      charge(x:number){
            console.log("chrgement x ==>", x);
      }
}


function main() {
      //charge samsung with nokia charger
      const adapter = new SamsungAdapter();
      adapter.chargementNokia(true);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.