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

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

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

jaco0646 05.02.2020 02:44

Книга GoF отвечает на этот вопрос напрямую.

jaco0646 25.03.2020 04:24
Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Когда мы говорим о том, как сделать следующий шаг в качестве разработчика, мы должны понимать, что качество кода всегда является основным фокусом на...
Принципы SOLID - лучшие практики
Принципы SOLID - лучшие практики
SOLID - это аббревиатура, обозначающая пять ключевых принципов проектирования: принцип единой ответственности, принцип "открыто-закрыто", принцип...
162
2
86 057
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Ответ принят как подходящий

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

Это обычное представление о некоторых аспектах паттерна «Адаптер» в паттерне «Мост». Однако, если процитировать эта статья:

At first sight, the Bridge pattern looks a lot like the Adapter pattern in that a class is used to convert one kind of interface to another. However, the intent of the Adapter pattern is to make one or more classes' interfaces look the same as that of a particular class. The Bridge pattern is designed to separate a class's interface from its implementation so you can vary or replace the implementation without changing the client code.

Мост не имеет ничего общего с шаблоном или стратегией. Мост - это структурный образец. Шаблон и Стратегия - это модели поведения.

jaco0646 05.02.2020 02:39

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

См. Также диаграмму Антона Щастного ниже для графической иллюстрации этого объяснения.

NomadeNumerique 03.02.2014 21:51

Я не думаю, что цвет является хорошим примером иерархии реализации, это довольно сбивает с толку. В разделе «Шаблоны проектирования» GoF есть хороший пример шаблона Bridge, реализация которого зависит от платформы: PM IBM, X UNIX и т. д.

clapas 06.09.2017 19:12

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

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

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

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

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

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

Привет, Билл. Я не понимаю, почему мы обязательно используем шаблон Bridge в драйверах устройств. Я имею в виду, что мы можем легко делегировать реализацию (чтения, записи, поиска и т. д.) Правильному классу с помощью полиморфизма, верно? Или, может быть, с посетителем? Почему это должен быть мост? Заранее спасибо.

stdout 01.12.2016 02:31

@zgulser, да, вы действительно используете полиморфизм. Шаблон «Мост» описывает один из видов использования подклассов для отделения реализации от абстракции.

Bill Karwin 01.12.2016 02:47

Вы имели в виду разделение реализации формы (например, прямоугольника) от абстракции цвета Let's Day, верно? И я полагаю, вы говорите, что есть множество способов сделать это, и Bridge - лишь один из них.

stdout 01.12.2016 10:42

Да, у подклассов есть и другие применения. Именно этот способ использования подклассов и делает его шаблоном моста.

Bill Karwin 01.12.2016 11:21

Я имею в виду развязку от абстрактного интерфейса Shape к конкретной реализации Rectangle. Таким образом, вы можете написать код, которому нужен объект типа «Shape», даже если конкретный объект на самом деле является подклассом Shape.

Bill Karwin 01.12.2016 11:23

Важно отметить, что шаблон «Мост» может быть реализован по-разному в зависимости от возможностей языка / системы. Любой язык, допускающий определение класса, обязательно должен иметь способ определения интерфейса этого класса. Но не все языки позволяют определять интерфейсы независимо от классов. Для тех, кто это делает (например, C#), шаблон Bridge может быть реализован напрямую с использованием таких определений интерфейсов и при этом пропустить по крайней мере один уровень подкласса и создания экземпляров частного объекта.

C Perkins 20.01.2017 17:38

Я использовал схему моста в работе. Я программирую на 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 и сократить время компиляции.

Если вы используете этот шаблон, AImpl даже не нуждается в заголовке. Я просто вставил его в файл реализации для класса A

1800 INFORMATION 26.11.2008 09:05

Ваш разработчик является частным лицом. У меня новый вопрос по этому поводу, см. stackoverflow.com/questions/17680762/…

Roland 16.07.2013 19:28

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

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

Когда:

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

Выполните рефакторинг для:

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

Я думаю, что это очень прагматичный подход к шаблонам: 1) описывать неоптимальный простой дизайн 2) рефакторинг дизайна / кода для лучшего факторизации

Alexey 10.07.2012 01:44

Используйте математическую концепцию, чтобы объяснить шаблон проектирования моста. Очень заинтересован.

Jian Huang 18.02.2015 00:34

Это всего лишь рефакторинг. Намерение шаблона моста: «Отделить абстракцию от ее реализации, чтобы они могли различаться независимо». Где абстракция и где здесь реализация?

clapas 06.09.2017 18:58

Джон красиво изложил это в сообщении блог. Считал, что это хорошее чтение для общего обзора.

Vaibhav Bhalla 17.01.2019 12:17

Есть комбинация ответов Федерико и Джона.

Когда:

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

Выполните рефакторинг для:

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

Зачем делать наследование цветов?

vainolo 03.02.2013 13:32

@vainolo, потому что цвет - это интерфейс, а синий, красный - конкретные цвета

Weltschmerz 01.03.2014 01:46

Это всего лишь рефакторинг. Намерение шаблона моста: «Отделить абстракцию от ее реализации, чтобы они могли различаться независимо». Где абстракция и где здесь реализация?

clapas 06.09.2017 18:58

Разве Rectangle (Color) не более абстрактный, чем BlueRectangle?

Anton Shchastnyi 07.09.2017 13:27

@clapas, Абстракция - это свойство "Shape.color", поэтому класс Red и класс Blue являются реализацией, а интерфейс Color является мостом.

reco 29.12.2017 12:13

Я читал десятки неуместных примеров, в которых просто неправильно используется наследование, т.е. что-то, что кажется естественным, как «имеет», представлено как «есть». Это лучший ответ, который я нашел. Браво! Кратко, информативно, обрисовано в общих чертах.

egelev 07.10.2019 16:31

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

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

#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

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

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);
}
}
}

Я проголосовал против, потому что считаю, что это запутанный, плохо отформатированный ответ.

Zimano 29.03.2018 15:27

Полностью согласен, как можно публиковать ответы на этом сайте без минимального внимания к отступам и ясности кода?

Massimiliano Kraus 10.07.2018 14:24

Вы работаете в страховой компании, где разрабатываете приложение для рабочего процесса, которое управляет различными задачами: бухгалтерский учет, договор, претензии. Это абстракция. Что касается реализации, вы должны иметь возможность создавать задачи из разных источников: электронная почта, факс, электронные сообщения.

Вы начинаете свой дизайн с этих классов:

public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

Теперь, поскольку каждый источник должен обрабатываться определенным образом, вы решаете специализировать каждый тип задачи:

public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}

public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}

public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}

У вас осталось 13 классов. Добавление типа задачи или типа источника становится сложной задачей. Использование шаблона моста упрощает обслуживание, отделяя задачу (абстракцию) от источника (что является проблемой реализации):

// Source
public class Source {
   public string GetSender();
   public string GetMessage();
   public string GetContractReference();
   (...)
}

public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}

// Task
public class Task {
   public Task(Source source);
   (...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

Добавить тип задачи или источник стало намного проще.

Примечание. Большинство разработчиков не создают иерархию из 13 классов заранее для решения этой проблемы. Однако в реальной жизни вы можете не знать заранее количество источников и типов задач; если у вас есть только один источник и два типа задач, вы, вероятно, не будете отделять задачу от источника. Затем общая сложность растет по мере добавления новых источников и типов задач. В какой-то момент вы проведете рефакторинг и, чаще всего, получите решение, подобное мосту.

Другие вопросы по теме