адаптер-Любой реальный пример шаблона адаптера [закрыто]


85

Я хочу продемонстрировать своей команде использование шаблона адаптера . Я прочитал много книг и статей в Интернете. Все приводят пример, который полезен для понимания концепции (форма, карта памяти, электронный адаптер и т. Д.), Но настоящего практического примера нет.

Не могли бы вы поделиться любыми примерами использования шаблона адаптера?

ps Я попытался найти существующие вопросы в stackoverflow, но не нашел ответа, поэтому разместил его как новый вопрос. Если вы знаете, что на это уже есть ответ, пожалуйста, перенаправьте.


4
Хорошо, если вы хотите его демо. У вас должен быть готовый пример этого в вашей среде, на самом деле несколько. Иначе зачем вам его демо?
Тони Хопкинсон

1
Вот несколько примеров. stackoverflow.com/questions/1673841/…
r4.

1
@TonyHopkinson Цель - познакомить людей с этим шаблоном проектирования на реальном примере.
AksharRoop

10
@AksharRoop. Шаблон проектирования предназначен для решения проблемы, а не для поиска ее решения. Лучший пример - это пример из вашего собственного «мира».
Тони Хопкинсон

1
@TonyHopkinson Возможно, я использовал здесь неправильный термин «демонстрация», но я имел в виду объяснить концепцию этого шаблона на хорошем примере. Я согласен, что должен найти его в моей собственной системе ...
AksharRoop

Ответы:


77

Многие примеры Adapter тривиальны или нереалистичны ( Rectangle vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs RoundPeg , Duck vs. Turkey ). Хуже того, многие не показывают несколько адаптеров для разных адаптеров ( кто-то привел Java Arrays.asList в качестве примера шаблона адаптера ). Адаптация интерфейса только одного класса для работы с другим кажется слабым примером шаблона адаптера GoF. Этот шаблон использует наследование и полиморфизм, поэтому можно было бы ожидать, что хороший пример покажет несколько реализаций адаптеров для разных адаптеров .

Лучший пример я нашел в главе 26 Применение UML и шаблонов: Введение в объектно-ориентированного анализа и проектирования и итерационных развития (третье издание) . Следующие изображения взяты из инструкторского материала, размещенного на FTP-сайте книги.

Первый показывает, как приложение может использовать несколько реализаций (приспособлений), которые функционально похожи (например, налоговые калькуляторы, модули учета, службы авторизации кредита и т. Д.), Но имеют разные API. Мы хотим избежать жесткого кодирования нашего кода уровня домена для обработки различных возможных способов расчета налога, послепродажного обслуживания, авторизации запросов по кредитным картам и т. Д. Это все внешние модули, которые могут различаться, и для которых мы не можем изменять код. Адаптер позволяет нам выполнять жесткое кодирование в адаптере, тогда как наш код уровня домена всегда использует один и тот же интерфейс (интерфейс IWhateverAdapter).

Рис. 26.1

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

Рис. 26.2


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


51

Как превратить француза в нормального человека ...

 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));

20
Я француз, и мне обидно, что ты не считаешь меня настоящим человеком. (JK)
ZeroUltimax

13
@ZeroUltimax Я почти уверен, что этот код не будет компилироваться в Квебеке.
Fuhrmanator

1
Любой программист, не знающий адаптеров, легко решил бы проблему. Как знание теории адаптера помогает сэкономить время или улучшить решение? Лучше всего использовать специальный класс, а не просто метод?
Rowan Gontier

Что делать, если вы не контролируете интерфейс и вам нужно адаптировать один из ваших классов к сторонней библиотеке? Множество других веских причин, которые выходят за рамки этого ответа.
CountZero

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

42

Преобразование интерфейса в другой интерфейс.

Любой реальный пример шаблона адаптера

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

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


Вот код, соответствующий этому: codeproject.com/Tips/595716/Adapter-Design-Pattern-in-Cplusplus
Raghav


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 - шаблон проектирования адаптера: введение


4

Вы можете использовать шаблон проектирования адаптера, когда вам нужно иметь дело с разными интерфейсами со сходным поведением (что обычно означает классы с аналогичным поведением, но с разными методами). Примером этого может быть класс для подключения к телевизору Samsung и еще один для подключения к телевизору Sony. Они будут иметь общее поведение, такое как открытие меню, запуск воспроизведения, подключение к сети и т. Д., Но каждая библиотека будет иметь свою реализацию (с разными именами методов и сигнатурами). Эти различные реализации, зависящие от поставщика, на диаграммах UML называются Adaptee .

Итак, в вашем коде (называемом клиентом на диаграммах UML) вместо жесткого кода вызовов методов каждого поставщика (или адаптируемого ) вы могли бы затем создать общий интерфейс (называемый Target в диаграммах UML), чтобы обернуть эти похожие поведения и работать только с одним типом объекта.

Затем адаптеры реализуют интерфейс Target, делегируя вызовы своих методов адаптерам , которые передаются адаптерам через конструктор.

Чтобы вы поняли это в коде Java, я написал очень простой проект, используя точно такой же пример, упомянутый выше, с использованием адаптеров для работы с несколькими интерфейсами Smart TV. Код небольшой, хорошо документирован и не требует пояснений, поэтому покопайтесь в нем, чтобы увидеть, как будет выглядеть реальная реализация.

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

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


3

Шаблоны проектирования адаптера помогают преобразовать интерфейс одного класса в интерфейс ожидаемого клиента.

Пример: у вас есть служба, которая возвращает погоду (в градусах Цельсия), передавая название города в качестве входного значения. Теперь предположим, что ваш клиент хочет передать почтовый индекс в качестве входных данных и ожидает взамен температуру в городе. Здесь для этого понадобится переходник.

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";
    }
}

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

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 07

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

0

Используйте адаптер, если у вас есть интерфейс, который вы не можете изменить, но который вам нужно использовать. Смотрите на это так, как будто вы новичок в офисе, и вы не можете заставить седых следовать вашим правилам - вы должны адаптироваться к их правилам. Вот реальный пример из реального проекта, над которым я когда-то работал, где пользовательский интерфейс задан.

У вас есть приложение, которое считывает все строки файла в структуру данных List и отображает их в сетке (назовем базовый интерфейс хранилища данных IDataStore). Пользователь может перемещаться по этим данным, нажимая кнопки «Первая страница», «Предыдущая страница», «Следующая страница», «Последняя страница». Все нормально работает.

Теперь приложение нужно использовать с производственными журналами, которые слишком велики для чтения в память, но пользователю все равно нужно перемещаться по ним! Одним из решений было бы реализовать кэш, в котором хранятся первая, следующая, предыдущая и последняя страницы. Мы хотим, чтобы когда пользователь нажимал «Следующая страница», мы возвращали страницу из кеша и обновляли кеш; когда они нажимают последнюю страницу, мы возвращаем последнюю страницу из кеша. На заднем плане у нас есть файловый поток, делающий всю магию. Таким образом, у нас будет только четыре страницы в памяти, а не весь файл.

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

И кто знает, завтра начальник хочет начать читать файлы из таблицы базы данных. Все, что вы делаете, - это расширяете IDataStore до SQLDataStore, как вы делали для Cache, настраивая соединение в фоновом режиме. Когда они нажимают «Следующая страница», вы генерируете необходимый sql-запрос для получения следующих нескольких сотен строк из базы данных.

По сути, оригинальный интерфейс приложения не изменился. Мы просто адаптировали современные и интересные функции для работы, сохранив при этом устаревший интерфейс.


Я не понимаю? Похоже, вы просто использовали существующий интерфейс и реализовали методы? Где другой интерфейс, к которому вам нужно адаптироваться, и класс адаптера?
беримболо

@berimbolo Ваша путаница действительна, поскольку в приведенном выше примере четко не говорится о шаблоне адаптера.
rahil008 08

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.

Итак, на помощь приходит шаблон адаптера. Мы напишем сопоставление = промежуточный адаптер, который будет сопоставлять вызовы интерфейса кеша 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.