Когда вы используете шаблон моста? Чем он отличается от шаблона адаптера? - PullRequest
143 голосов
/ 26 ноября 2008

Кто-нибудь когда-либо использовал Bridge Pattern в реальных приложениях? Если да, то как ты это использовал? Это я, или это просто паттерн адаптера с небольшим внедрением зависимостей в микс? Это действительно заслуживает своего собственного образца?

Ответы [ 11 ]

233 голосов
/ 21 мая 2012

Существует комбинация ответов Федерико и Джона .

Когда:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

Refactor to:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red
220 голосов
/ 26 ноября 2008

Шаблон Bridge - это применение старого совета, «предпочитаю композицию наследованию». Это становится удобным, когда вы должны создавать подклассы разного времени так, чтобы они были ортогональны друг другу. Скажем, вы должны реализовать иерархию цветных фигур. Вы бы не создали подкласс Shape с Rectangle и Circle, а затем создали бы подкласс Rectangle с RedRectangle, BlueRectangle и GreenRectangle и то же самое для Circle, не так ли? Вы бы предпочли сказать, что каждый Shape имеет Color и реализует иерархию цветов, и это паттерн Bridge. Ну, я бы не реализовал "иерархию цветов", но вы поняли ...

202 голосов
/ 23 февраля 2012

Когда:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

Рефакторинг к:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2
76 голосов
/ 26 ноября 2008

Классический пример шаблона Мост используется в определении фигур в среде пользовательского интерфейса (см. Запись в Википедии Шаблон моста). Шаблон Мост представляет собой составной шаблонов Шаблон и Стратегия .

Это общий вид некоторых аспектов шаблона Adapter в шаблоне Bridge. Однако, чтобы процитировать эту статью :

На первый взгляд, паттерн Bridge очень похож на паттерн Adapter в том смысле, что класс используется для преобразования одного вида интерфейса в другой. Однако цель шаблона Adapter - сделать так, чтобы интерфейсы одного или нескольких классов выглядели так же, как и у определенного класса. Шаблон Bridge предназначен для отделения интерфейса класса от его реализации, поэтому вы можете изменять или заменять реализацию без изменения клиентского кода.

29 голосов
/ 26 ноября 2008

Адаптер и Мост, безусловно, связаны, и различие тонкое. Вполне вероятно, что некоторые люди, которые думают, что используют один из этих шаблонов, на самом деле используют другой шаблон.

Объяснение, которое я видел, заключается в том, что Adapter используется, когда вы пытаетесь объединить интерфейсы некоторых несовместимых классов, которые уже существуют . Адаптер функционирует как своего рода переводчик для реализаций, которые можно считать legacy .

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

Драйверы устройств - это часто цитируемый пример Bridge, но я бы сказал, что это Bridge, если вы определяете спецификации интерфейса для поставщиков устройств, но это Adapter, если вы берете существующие драйверы устройств и создаете оболочку. -класс для предоставления унифицированного интерфейса.

Итак, с точки зрения кода, эти две модели очень похожи. В бизнесе они разные.

См. Также http://c2.com/cgi/wiki?BridgePattern

26 голосов
/ 25 мая 2010

По моему опыту, Bridge является довольно часто повторяющимся шаблоном, потому что это решение, когда есть два ортогональных измерения в области . Например. формы и методы рисования, поведение и платформы, форматы файлов и сериализаторы и т. д.

И совет: всегда думайте о шаблонах проектирования с концептуальной точки зрения , а не с точки зрения реализации. С правильной точки зрения, Bridge нельзя спутать с Adapter, потому что они решают другую проблему, а композиция превосходит наследование не потому, что она сама по себе, а потому, что она позволяет обрабатывать ортогональные задачи отдельно.

20 голосов
/ 29 мая 2016

Цель Мост и Адаптер различна, и нам нужны оба шаблона по отдельности.

Схема моста:

  1. Это структурный паттерн
  2. Абстракция и реализация не связаны во время компиляции
  3. Абстракция и реализация - оба могут меняться без влияния на клиента
  4. Использует композицию вместо наследования.

Использовать шаблон Мост, когда:

  1. Требуется привязка реализации во время выполнения,
  2. У вас есть множество классов, возникающих в результате связанного интерфейса и многочисленных реализаций,
  3. Вы хотите поделиться реализацией среди нескольких объектов,
  4. Вам необходимо отобразить ортогональные иерархии классов.

@ Ответ Джона Сонмеза наглядно показывает эффективность паттерна моста в сокращении иерархии классов.

Вы можете обратиться к ссылке на документацию ниже, чтобы лучше понять шаблон моста с примером кода

Схема адаптера :

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

Ключевые отличия:

  1. Адаптер заставляет вещи работать после того, как они спроектированы; Мост заставляет их работать раньше, чем они.
  2. Мост спроектирован заранее, чтобы позволить абстракции и реализации варьироваться независимо . Адаптер переоборудован для совместной работы несвязанных классов.
  3. Назначение: Адаптер позволяет двум несвязанным интерфейсам работать вместе. Мост позволяет абстракции и реализации варьироваться независимо.

Связанный вопрос SE с UML-диаграммой и рабочим кодом:

Разница между шаблоном моста и шаблоном адаптера

Полезные статьи:

исходный мост шаблон артикула

исходный адаптер шаблон артикула

Мост журналов образец статьи

EDIT:

Пример реального использования Bridge Pattern (в соответствии с предложением meta.stackoverflow.com, включенным примером сайта документации в этом посте, так как документация собирается заходить)

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

Шаблон моста UML из Википедии:

Bridge pattern UML from Wikipedia

В этом шаблоне четыре компонента.

Abstraction: определяет интерфейс

RefinedAbstraction: Реализует абстракцию:

Implementor: определяет интерфейс для реализации

ConcreteImplementor: Реализует интерфейс реализатора.

The crux of Bridge pattern : Две ортогональные иерархии классов с использованием композиции (без наследования). Иерархия абстракций и иерархия реализации могут варьироваться независимо. Реализация никогда не относится к абстракции. Абстракция содержит интерфейс реализации в качестве члена (через композицию). Эта композиция уменьшает еще один уровень иерархии наследования.

Реальное слово Вариант использования:

Разрешить различным транспортным средствам иметь как версии с ручной, так и автоматической системой передач.

Пример кода:

/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}

выход:

Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear

Объяснение:

  1. Vehicle - это абстракция.
  2. Car и Truck - две конкретные реализации Vehicle.
  3. Vehicle определяет абстрактный метод: addGear().
  4. Gear является интерфейсом разработчика
  5. ManualGear и AutoGear - две реализации Gear
  6. Vehicle содержит интерфейс implementor вместо реализации интерфейса. Compositon интерфейса реализатора является сутью этого шаблона: Это позволяет абстракции и реализации варьироваться независимо.
  7. Car и Truck определяют реализацию (переопределенную абстракцию) для абстракции: addGear(): содержит Gear - либо Manual, либо Auto

Варианты использования для схемы моста :

  1. Абстракция и Реализация может изменяться независимо друг от друга, и они не связаны во время компиляции
  2. Карта ортогональных иерархий - одна для Абстракция и одна для Реализация .
9 голосов
/ 26 ноября 2008

Я использовал шаблон моста на работе. Я программирую на C ++, где его часто называют идиомой PIMPL (указатель на реализацию). Это выглядит так:

class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  

В этом примере class A содержит интерфейс, а class Aimpl содержит реализацию.

Одним из применений этого шаблона является предоставление только некоторых открытых членов класса реализации, но не других. В этом примере только Aimpl::foo() можно вызвать через открытый интерфейс A, но не Aimpl::bar()

Еще одним преимуществом является то, что вы можете определить Aimpl в отдельном заголовочном файле, который не должен быть включен пользователями A. Все, что вам нужно сделать, это использовать предварительное объявление Aimpl до определения A и переместить определения всех функций-членов, ссылающихся на pImpl, в файл .cpp. Это дает вам возможность сохранить заголовок Aimpl в секрете и сократить время компиляции.

6 голосов
/ 15 сентября 2013

Поместить пример формы в код:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

Вывод:

Drawn a Square of Red Color
Drawn a Circle of Blue Color

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

0 голосов
/ 25 декабря 2017
Bridge design pattern we can easily understand helping of service and dao layer.

Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
    void save(T t);
}
concrete  implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
 private Dao<Account> accountDao;
 public AccountService(AccountDao dao){
   this.accountDao=dao;
   }
public void save(Account){
   accountDao.save(Account);
 }
}
login service- 
public class LoginService<Login> implement BasicService<Login>{
 private Dao<Login> loginDao;
 public AccountService(LoginDao dao){
   this.loginDao=dao;
   }
public void save(Login){
   loginDao.save(login);
 }
}

public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}
...