Когда вы используете шаблон моста? Как он отличается от шаблона адаптера?

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

Здесь отображается комбинация Федерико и John's.

Когда:

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

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

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red

Ответ 4

Когда:

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

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

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2

Ответ 5

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

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

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

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

Таким образом, по коду, эти два шаблона очень похожи. По-деловому, они разные.

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

Ответ 6

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

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

Ответ 7

Я использовал шаблон моста на работе. Я программирую на С++, где его часто называют идиомой 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 private и сократить время компиляции.

Ответ 8

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

Мост:

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

Используйте шаблон Bridge, если:

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

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

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

Шаблон адаптера:

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

Основные отличия:

  • Адаптер позволяет работать после того, как они разработаны; Мост заставляет их работать до того, как они будут.
  • Мост разработан заранее, чтобы абстракция и реализация менялись независимо. Адаптер модернизирован для совместной работы не связанных между собой классов.
  • Цель: адаптер позволяет двум несвязанным интерфейсам работать вместе. Мост позволяет абстракции и реализации меняться независимо.

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

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

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

sourcemaking bridge pattern article

sourcemaking adapter образец статьи

журналdev bridge шаблон статьи

EDIT:

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

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

Мост-шаблон UML из Википедии:

Мост-шаблон UML из Википедии

У вас есть четыре компонента в этом шаблоне.

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

Объяснение:

  • Vehicle - абстракция.
  • Car и Truck - две конкретные реализации Vehicle.
  • Vehicle определяет абстрактный метод: addGear().
  • Gear - интерфейс разработчика
  • ManualGear и AutoGear являются двумя реализациями Gear
  • Vehicle содержит интерфейс Implementor, а не интерфейс. Compositon интерфейса конструктора является ключевым в этом шаблоне: он позволяет абстракции и реализации меняться независимо.
  • Car и Truck определяют реализацию (переопределенную абстракцию) для абстракции: addGear(): содержит Gear - либо Manual, либо Auto

Использовать случай для шаблона Bridge:

  • Абстракция и Реализация может изменять независимые друг от друга и не привязываться во время компиляции
  • Карта ортогональных иерархий - одна для абстракции и одна для реализации.

Ответ 9

Чтобы добавить пример формы в код:

#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

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

Ответ 10

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