it-roy-ru.com

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

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

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

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

71
AksharRoop

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

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

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

Fig. 26.1

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

Fig. 26.2

71
Fuhrmanator

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

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

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

enter image description here

42
Premraj

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

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

Вот пример, который имитирует преобразование 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>

Как проверить

Просто запустите юнит-тест.

11
Eric Wang

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

В качестве примера можно привести переводчик языка или мобильное зарядное устройство. Больше здесь в этом видео на YouTube:

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

7
babu

Один из реальных примеров - 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
dexterous_stranger

Вы можете найти PHP реализацию шаблона Adapter, используемого для защиты от атак внедрения:

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

Одним из интересных аспектов шаблона Adapter является то, что он поставляется в двух вариантах: адаптер класса, основанный на множественном наследовании, и адаптер объекта, зависящий от композиции. Приведенный выше пример основан на композиции.

2
Bill

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

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

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

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

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

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

2
Jaabax

Настоящим примером может служить отчет о документах в приложении. Простой код, как здесь. 

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

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
huseyin

Согласно книге Джудит Бишоп «Шаблоны проектирования C # 3.0», Apple использовала шаблон адаптера для адаптации Mac OS для работы с продуктами Intel (объяснение в главе № 4, выдержка здесь 2 )

0
CBU

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

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

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

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

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

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

0
Justice O.

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

0
rahil008