Разница между образцом моста и образцом адаптера
В чем разница между шаблонами моста и адаптера?
11 ответов
"Адаптер заставляет вещи работать после того, как они спроектированы; Bridge заставляет их работать раньше, чем они будут. [GoF, p219]"
По сути, шаблон Adapter полезен, когда у вас есть существующий код, будь то сторонний или внутренний, но не зависящий от вас или не изменяемый другим образом, чтобы полностью соответствовать интерфейсу, который вам необходим. Например, у нас есть SuperWeaponsArray, который может управлять множеством устройств конца света.
public class SuperWeaponsArray {
/*...*/
public void destroyWorld() {
for (Weapon w : armedWeapons) {
w.fire();
}
}
}
Отлично. За исключением того, что мы понимаем, что в нашем арсенале есть ядерное устройство, которое значительно предшествует переходу на интерфейс с оружием. Но нам бы очень хотелось, чтобы это работало здесь... так что же нам делать... вклинивать это!
NukeWeaponsAdaptor - основан на нашем классе Nuke, но экспортирует интерфейс оружия. Сладкий, теперь мы можем уничтожить мир. Это кажется чем-то вроде клуджа, но это заставляет вещи работать.
Паттерн Bridge - это то, что вы реализуете заранее - если вы знаете, что у вас есть две ортогональные иерархии, он предоставляет способ разделить интерфейс и реализацию таким образом, что вы не получите безумное количество классов. Допустим, у вас есть:
MemoryMappedFile и DirectReadFile типы файловых объектов. Допустим, вы хотите иметь возможность читать файлы из различных источников (возможно, Linux или Windows и т. Д.). Bridge поможет вам избежать:
MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile
http://en.wikipedia.org/wiki/Adapter_pattern
Шаблон Adapter больше о том, как заставить существующий код работать с более новой системой или интерфейсом.
Если у вас есть набор стандартных API веб-сервисов компании, которые вы хотели бы предложить существующему интерфейсу расширяемости другого приложения, вы можете написать набор адаптеров для этого. Обратите внимание, что есть серая область, и это больше о том, как вы технически определяете шаблон, так как другие шаблоны, такие как фасад, похожи.
http://en.wikipedia.org/wiki/Bridge_pattern
Шаблон Bridge позволит вам иметь альтернативные реализации алгоритма или системы.
Хотя это и не классический пример паттерна Bridge, представьте, что у вас есть несколько реализаций хранилища данных: одно эффективно в пространстве, другое эффективно в сырой производительности... и у вас есть экономическое обоснование для предложения как в вашем приложении, так и в среде.,
С точки зрения вашего вопроса, "где я могу использовать какой шаблон", ответ таков, где это имеет смысл для вашего проекта! Возможно, стоит подумать о том, чтобы предложить редактирование с разъяснениями, чтобы вести дискуссию о том, где, по вашему мнению, вам нужно использовать одно или другое.
адаптер:
- Это структурный паттерн
- Полезно работать с двумя несовместимыми интерфейсами
Диаграмма UML: из справочной статьи:
Цель: определяет специфичный для домена интерфейс, который использует Клиент.
Адаптер: адаптирует интерфейс Adaptee к целевому интерфейсу.
Adaptee: определяет существующий интерфейс, который необходимо адаптировать.
Клиент: взаимодействует с объектами, соответствующими интерфейсу Target.
Пример:
Квадрат и Прямоугольник - две разные фигуры, и для получения области () каждой из них требуются разные методы. Но все-таки Square работает над интерфейсом Rectangle с преобразованием некоторых свойств.
public class AdapterDemo{
public static void main(String args[]){
SquareArea s = new SquareArea(4);
System.out.println("Square area :"+s.getArea());
}
}
class RectangleArea {
public int getArea(int length, int width){
return length * width;
}
}
class SquareArea extends RectangleArea {
int length;
public SquareArea(int length){
this.length = length;
}
public int getArea(){
return getArea(length,length);
}
}
мост:
- Это структурная схема
- он отделяет абстракцию от своей реализации, и оба могут варьироваться независимо
- Это возможно, потому что композиция была использована вместо наследования
РЕДАКТИРОВАТЬ: (согласно предложению @quasoft)
У вас есть четыре компонента в этом шаблоне.
Абстракция: определяет интерфейс
RefinedAbstraction: реализует абстракцию:
Реализатор: он определяет интерфейс для реализации
ConcreteImplementor: Он реализует интерфейс Implementor.
Фрагмент кода:
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
Связанный пост:
Когда вы используете шаблон моста? Чем он отличается от шаблона адаптера?
Ключевые отличия: от исходной статьи
- Адаптер заставляет вещи работать после того, как они разработаны; Бридж заставляет их работать раньше, чем они.
- Мост спроектирован заранее, чтобы позволить абстракции и реализации варьироваться независимо. Адаптер модернизирован для совместной работы несвязанных классов.
В верхнем ответе @James цитирует предложение из GoF, стр. 219. Я думаю, что стоит воспроизвести здесь полное объяснение.
Адаптер против моста
У шаблонов адаптера и моста есть несколько общих атрибутов. Оба способствуют гибкости, обеспечивая уровень косвенного обращения к другому объекту. Оба включают пересылку запросов к этому объекту из интерфейса, отличного от его собственного.
Ключевое различие между этими шаблонами заключается в их назначении. Адаптер фокусируется на устранении несовместимости между двумя существующими интерфейсами. Он не фокусируется на том, как реализованы эти интерфейсы, и не рассматривает, как они могут развиваться независимо. Это способ заставить два независимо разработанных класса работать вместе, не переопределяя один или другой. Bridge, с другой стороны, соединяет абстракцию и ее (потенциально многочисленные) реализации. Он предоставляет клиентам стабильный интерфейс, даже если позволяет вам изменять классы, которые его реализуют. Он также поддерживает новые реализации по мере развития системы.
В результате этих различий адаптер и мост часто используются на разных этапах жизненного цикла программного обеспечения. Адаптер часто становится необходимым, когда вы обнаруживаете, что два несовместимых класса должны работать вместе, как правило, чтобы избежать репликации кода. Муфта непредвиденная. Напротив, пользователь моста заранее понимает, что абстракция должна иметь несколько реализаций, и обе могут развиваться независимо. Шаблон адаптера заставляет вещи работать после того, как они спроектированы; Мост заставляет их работать раньше, чем они есть. Это не значит, что Adapter чем-то уступает Bridge; каждый шаблон просто обращается к другой проблеме.
Этот пост был уже давно. Тем не менее, важно понимать, что фасад чем-то похож на адаптер, но это не совсем то же самое. Адаптер "адаптирует" существующий класс к обычно несовместимому клиентскому классу. Допустим, у вас есть старая система рабочих процессов, которую ваше приложение использует в качестве клиента. Ваша компания может заменить систему документооборота новой "несовместимой" (с точки зрения интерфейсов). В большинстве случаев вы можете использовать шаблон адаптера и написать код, который фактически вызывает интерфейсы нового механизма рабочего процесса. Мост, как правило, используется по-другому. Если у вас действительно есть система, которая должна работать с различными файловыми системами (например, локальный диск, NFS и т. Д.), Вы можете использовать шаблон моста и создать один уровень абстракции для работы со всеми вашими файловыми системами. В основном это будет простой вариант использования шаблона моста. Фасад и адаптер имеют некоторые общие свойства, но фасады обычно используются для упрощения существующего интерфейса / класса. В первые дни EJB не было местных вызовов для EJB. Разработчики всегда получали заглушку, сужали ее и называли "псевдо-удаленно". Это часто вызывало проблемы с производительностью (особенно, когда звонили по проводам). Опытные разработчики будут использовать шаблон фасада для обеспечения очень грубого интерфейса с клиентом. Этот фасад, в свою очередь, будет выполнять несколько вызовов различных более детальных методов. В целом это значительно сократило количество требуемых вызовов методов и повысило производительность.
Похоже на более короткий и ясный ответ мне в соответствии с другим ответом stackru здесь:
Адаптер используется, когда у вас есть абстрактный интерфейс, и вы хотите сопоставить этот интерфейс с другим объектом, который имеет аналогичную функциональную роль, но с другим интерфейсом.
Мост очень похож на Адаптер, но мы называем его Мостом, когда вы определяете как абстрактный интерфейс, так и базовую реализацию. Т.е. вы не адаптируетесь к какому-то унаследованному или стороннему коду, вы являетесь разработчиком всего кода, но вам необходимо иметь возможность менять различные реализации.
Мост является улучшенным адаптером. Мост включает адаптер и добавляет дополнительную гибкость к нему. Вот как элементы из ответов Равиндры отображаются между шаблонами:
Adapter | Bridge
-----------|---------------
Target | Abstraction
-----------|---------------
| RefinedAbstraction
|
| This element is Bridge specific. If there is a group of
| implementations that share the same logic, the logic can be placed here.
| For example, all cars split into two large groups: manual and auto.
| So, there will be two RefinedAbstraction classes.
-----------|---------------
Adapter | Implementor
-----------|---------------
Adaptee | ConcreteImplementor
Мост очень похож на Адаптер, но мы называем его Мостом, когда вы определяете как абстрактный интерфейс, так и базовую реализацию, это означает, что вы не адаптируетесь к внешнему коду, вы являетесь разработчиком всего кода, но вам нужно иметь возможность менять разные реализации.
Есть множество ответов, чтобы различать адаптер и мост. Но поскольку люди ищут примеры кода, я бы привел один пример для шаблона проектирования адаптера, созданного в хронологии:
//---------------------------------------External Vendor/Provider--------------------------------
//Adaptee | RussianTankInterface is adaptee | adaptee lives in is own lala land and do not care about any other class or interface
RussianTankInterface smerch9K58 = new RussianTank("The Russian Artillery bought by India in October 2015");
smerch9K58.aboutMyself();
smerch9K58.stuff();
smerch9K58.rotate();
smerch9K58.launch();
//---------------------------------2016 : India manufactures Bharat52 ------------------------------
//Client_1 :: IndianTank
EnemyAttacker bharat52Attacker = new IndianTank("Tank built in India delivered to Army in Jul 2016");
// behaves normally -------------------------(1)
bharat52Attacker.aboutMe();
bharat52Attacker.load();
bharat52Attacker.revolve();
bharat52Attacker.fireArtillery();
//---------------------------------2019 : India mnufactures Pinaka, and thought about fusion with Russian technology - so adaption required ------------------------------
//Client_2 :: IndianTank
EnemyAttacker pinakaAttacker = new IndianTank("Tank built in India in 1998 got upgraded_v1 in 9 Sep 2019");
#####----Bilateral-Coalition happens----##
##### India : I want a fusion artillery technology with
##### 1) Indian materials and brain-power but
##### 2) Russian machine-parts-movement technology
##### Russia : Give me your Interface - at max we can help by providing an Adapter
//---------------------------------------External Vendor/Provider-----------------------------------
//Adapter :: RussianTechnologyAdapter | Russia gets EnemyAttacker interface only from India & creates RussianTechnologyAdapter
RussianTechnologyAdapter russianTechnologyAdapter = new RussianTechnologyAdapter(smerch9K58);
//Target | EnemyAttacker was initially ClientInterface but later becomes the Target as story evolves | <- client owns this Interface
EnemyAttacker dhanushAttacker = russianTechnologyAdapter;
#####----Russia keeps her Word----##
##### Russia to India : Here you go! Take Dhanush, a wrapper over our encapsulated adapter, and plug-in anything conforming to your EnemyAttacker.
##### India : Thanks a lot!
//--------------------------------- 2020 : India returns back happily with dhanushAttacker---------------------------------------
//Client_2 - adapted behavior -------------------------(2)
dhanushAttacker.setNavigationCapability(pinakaAttacker.getCuttingEdgeNavigableTargets());
dhanushAttacker.aboutMe(); //calls RussianInstance -> aboutMyself()
dhanushAttacker.load(); //calls RussianInstance -> stuff()
dhanushAttacker.revolve(); //calls RussianInstance -> rotate()
dhanushAttacker.fireArtillery(); //calls RussianInstance -> launch()
Внимательно обратите внимание:
- Тот же API в (1) и (2) ведет себя по-разному
- Индия может вставить свой мозг в каркас России, например
dhanushAttacker.setNavigationCapability(pinakaAttacker.get(..))
Примечательные моменты
Клиентская сторона владеет:
- Invoker /Use(uses Adapter later point)/Client
- ClientInterface (a.k.a Target )
Отправлено позже:
- ClientInterface ( becomes Target after sharing)
Принимающая сторона владеет:
- Adapter (later shared directly or as a wrapper )
- Adaptee
Надеюсь, что кто-то даст инлайн и для Bridge:)
Предположим, у вас есть абстрактный класс Shape с (общей / абстрактной) функциональностью рисования и кругом, который реализует Shape. Мостовая модель - это просто двусторонний подход к абстракции для разделения реализации (рисование в круге) и универсальной / абстрагированной функциональности (рисование в классе Shape).
Что это на самом деле значит? На первый взгляд, это звучит как то, что вы уже делаете (путем инверсии зависимостей). Так что не стоит беспокоиться о том, что у вас есть менее ридиг или более модульная база кода. Но за этим стоит более глубокая философия.
Насколько я понимаю, необходимость использования шаблона может возникнуть, когда мне нужно добавить новые классы, которые тесно связаны с текущей системой (например, RedCircle или GreenCircle) и которые отличаются только одной функциональностью (например, цветом). И мне понадобится паттерн Bridge, особенно, если существующие системные классы ( Circle или Shape) нужно часто менять, и вы не хотите, чтобы на вновь добавленные классы влияли эти изменения. Вот почему общие функциональные возможности рисования абстрагированы в новый интерфейс, так что вы можете изменять поведение чертежа независимо от формы или круга.
Думаю, это просто.
Адаптер разработан, чтобы позволить стороннему приложению работать с вашим приложением. И наоборот, чтобы ваше приложение могло работать со сторонними приложениями.
Используя шаблон моста, предполагается соединить два или более приложений без использования адаптера.
Фактически мост - это интерфейс, через который будут взаимодействовать два приложения. В качестве примера моста это интерфейсы PSR в PHP.
Пример:
Другое
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
MyApp
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$unformattedResponse=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
$myResponse=ResponseAdapter::convertResponseData($unformattedResponse);
//...
В предыдущем примере мы реализовали 2 адаптера на запрос и на ответ. Если мы перепишем пример и попробуем реализовать мост.
<?php
interface IBridgeResponse {};
Другое
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IBridgeResponse, IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
MyApp
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$response=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
if($response instanceof IBridgeResponse){
/**
The component has implemented IBridgeResponse interface,
thanks to which our application can interact with it.
This is the bridge.
Our application does not have to create an additional adapter
(if our application can work with IBridgeResponse).
*/
}
//...
В гексагональной архитектуре порты (интерфейсы, контракты) могут действовать как «мосты», если вы пишете приложение с самого начала и готовы принять правила другого используемого приложения. В противном случае вам придется написать «Адаптеры».