Я хочу продемонстрировать своей команде использование шаблона адаптера . Я прочитал много книг и статей в Интернете. Все приводят пример, который полезен для понимания концепции (форма, карта памяти, электронный адаптер и т. Д.), Но настоящего практического примера нет.
Не могли бы вы поделиться любыми примерами использования шаблона адаптера?
ps Я попытался найти существующие вопросы в stackoverflow, но не нашел ответа, поэтому разместил его как новый вопрос. Если вы знаете, что на это уже есть ответ, пожалуйста, перенаправьте.
oop
design-patterns
adapter
software-design
АкшарРуп
источник
источник
Ответы:
Многие примеры Adapter тривиальны или нереалистичны ( Rectangle vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs RoundPeg , Duck vs. Turkey ). Хуже того, многие не показывают несколько адаптеров для разных адаптеров ( кто-то привел Java Arrays.asList в качестве примера шаблона адаптера ). Адаптация интерфейса только одного класса для работы с другим кажется слабым примером шаблона адаптера GoF. Этот шаблон использует наследование и полиморфизм, поэтому можно было бы ожидать, что хороший пример покажет несколько реализаций адаптеров для разных адаптеров .
Лучший пример я нашел в главе 26 Применение UML и шаблонов: Введение в объектно-ориентированного анализа и проектирования и итерационных развития (третье издание) . Следующие изображения взяты из инструкторского материала, размещенного на FTP-сайте книги.
Первый показывает, как приложение может использовать несколько реализаций (приспособлений), которые функционально похожи (например, налоговые калькуляторы, модули учета, службы авторизации кредита и т. Д.), Но имеют разные API. Мы хотим избежать жесткого кодирования нашего кода уровня домена для обработки различных возможных способов расчета налога, послепродажного обслуживания, авторизации запросов по кредитным картам и т. Д. Это все внешние модули, которые могут различаться, и для которых мы не можем изменять код. Адаптер позволяет нам выполнять жесткое кодирование в адаптере, тогда как наш код уровня домена всегда использует один и тот же интерфейс (интерфейс IWhateverAdapter).
На приведенном выше рисунке мы не видим реальных приспособлений. Однако на следующем рисунке показано, как выполняется полиморфный вызов
postSale(...)
в интерфейсе IAccountingAdapter, что приводит к разноске продажи через SOAP в системе SAP.источник
Как превратить француза в нормального человека ...
пример
источник
Преобразование интерфейса в другой интерфейс.
Для подключения питания у нас есть разные интерфейсы по всему миру. Используя адаптер, мы можем легко подключиться.
источник
Вот пример имитации преобразования
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>
Как проверить
Просто запустите модульный тест.
источник
Примеры из реальной жизни: языковой переводчик или зарядное устройство для мобильного телефона. Подробнее здесь, в этом видео на YouTube:
Youtube - шаблон проектирования адаптера: введение
источник
Вы можете использовать шаблон проектирования адаптера, когда вам нужно иметь дело с разными интерфейсами со сходным поведением (что обычно означает классы с аналогичным поведением, но с разными методами). Примером этого может быть класс для подключения к телевизору 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
источник
Шаблоны проектирования адаптера помогают преобразовать интерфейс одного класса в интерфейс ожидаемого клиента.
Пример: у вас есть служба, которая возвращает погоду (в градусах Цельсия), передавая название города в качестве входного значения. Теперь предположим, что ваш клиент хочет передать почтовый индекс в качестве входных данных и ожидает взамен температуру в городе. Здесь для этого понадобится переходник.
источник
Один из реальных примеров - Qt-Dbus.
В qt-dbus есть утилита для генерации кода адаптера и интерфейса из предоставленного xml файла. Вот как это сделать.
Вы можете увидеть полный пример Qt-Dbus здесь -
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
источник
Здесь вы можете найти PHP-реализацию шаблона адаптера, используемого в качестве защиты от атак с использованием инъекций:
http://www.php5dp.com/category/design-patterns/adapter-composition/
Одним из интересных аспектов шаблона адаптера является то, что он бывает двух видов: адаптер класса, основанный на множественном наследовании, и адаптер объекта, основанный на композиции. В приведенном выше примере используется композиция.
источник
Реальным примером могут служить отчетные документы в приложении. Простой код, как здесь.
Я считаю, что адаптеры очень полезны для структуры программирования.
Результаты будут:
источник
Используйте адаптер, если у вас есть интерфейс, который вы не можете изменить, но который вам нужно использовать. Смотрите на это так, как будто вы новичок в офисе, и вы не можете заставить седых следовать вашим правилам - вы должны адаптироваться к их правилам. Вот реальный пример из реального проекта, над которым я когда-то работал, где пользовательский интерфейс задан.
У вас есть приложение, которое считывает все строки файла в структуру данных List и отображает их в сетке (назовем базовый интерфейс хранилища данных IDataStore). Пользователь может перемещаться по этим данным, нажимая кнопки «Первая страница», «Предыдущая страница», «Следующая страница», «Последняя страница». Все нормально работает.
Теперь приложение нужно использовать с производственными журналами, которые слишком велики для чтения в память, но пользователю все равно нужно перемещаться по ним! Одним из решений было бы реализовать кэш, в котором хранятся первая, следующая, предыдущая и последняя страницы. Мы хотим, чтобы когда пользователь нажимал «Следующая страница», мы возвращали страницу из кеша и обновляли кеш; когда они нажимают последнюю страницу, мы возвращаем последнюю страницу из кеша. На заднем плане у нас есть файловый поток, делающий всю магию. Таким образом, у нас будет только четыре страницы в памяти, а не весь файл.
Вы можете использовать адаптер, чтобы добавить эту новую функцию кеширования в ваше приложение, чтобы пользователь этого не заметил. Мы расширяем текущий IDataStore и называем его CacheDataStore. Если файл для загрузки большой, мы используем CacheDataStore. Когда мы делаем запрос для первой, следующей, предыдущей и последней страниц, информация направляется в наш кэш.
И кто знает, завтра начальник хочет начать читать файлы из таблицы базы данных. Все, что вы делаете, - это расширяете IDataStore до SQLDataStore, как вы делали для Cache, настраивая соединение в фоновом режиме. Когда они нажимают «Следующая страница», вы генерируете необходимый sql-запрос для получения следующих нескольких сотен строк из базы данных.
По сути, оригинальный интерфейс приложения не изменился. Мы просто адаптировали современные и интересные функции для работы, сохранив при этом устаревший интерфейс.
источник
Пример @Justice o не говорит четко о шаблоне адаптера. Расширение его ответа - у нас есть существующий интерфейс IDataStore, который использует наш потребительский код, и мы не можем его изменить. Теперь нас просят использовать новый классный класс из библиотеки XYZ, который делает то, что мы хотим реализовать, но но мы не можем изменить этот класс для расширения нашего IDataStore, проблема уже заметна? Создав новый класс - ADAPTER, который реализует интерфейс, которого ожидает наш потребительский код, то есть IDataStore, и используя класс из библиотеки, функции которой нам необходимы - ADAPTEE, как член нашего ADAPTER, мы можем достичь того, чего мы хотели.
источник
Согласно книге Джудит Бишоп «Шаблоны проектирования C # 3.0», Apple использовала шаблон адаптера для адаптации Mac OS для работы с продуктами Intel (объяснено в главе 4, отрывок здесь 2 )
источник
Пример из структуры Yii: Yii использует внутренний кеш, используя интерфейс ICache. https://www.yiiframework.com/doc/api/1.1/ICache
чья подпись похожа: -
Допустим, вы хотели бы использовать в проекте Yii кеш-библиотеку symfony https://packagist.org/packages/symfony/cache с ее интерфейсом кеширования, определив эту службу в конфигурации компонентов служб Yii (локатор служб) https: / /github.com/symfony/cache-contracts/blob/master/CacheInterface.php
Мы видим, что у кэша symfony есть интерфейс только с методом get, в котором отсутствует метод set и другая сигнатура для метода get, поскольку Symfony использует метод get также в качестве установщика при передаче второго вызываемого параметра.
Поскольку ядро Yii внутренне использует этот кеш / интерфейс Yii, сложно (расширить Yii / YiiBase), если вообще возможно, местами переписать вызовы этого интерфейса.
К тому же кеш Symfony не является нашим классом, поэтому мы не можем переписать его интерфейс, чтобы он соответствовал интерфейсу кеша Yii.
Итак, на помощь приходит шаблон адаптера. Мы напишем сопоставление = промежуточный адаптер, который будет сопоставлять вызовы интерфейса кеша Yii с интерфейсом кеша Symfony.
Выглядело бы так
источник
Это пример реализации адаптера:
источник