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


Классический пример паттерна «Мост» используется при определении фигур в среде пользовательского интерфейса (см. Образец моста запись в Википедии). Шаблон моста - это составной из шаблонов Шаблон и Стратегия.
Это обычное представление о некоторых аспектах паттерна «Адаптер» в паттерне «Мост». Однако, если процитировать эта статья:
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.
Мост не имеет ничего общего с шаблоном или стратегией. Мост - это структурный образец. Шаблон и Стратегия - это модели поведения.
Паттерн «Мост» - это применение старого совета «предпочитать композицию наследованию». Это становится удобным, когда вы должны создавать подклассы для разных времен ортогонально друг другу. Скажем, вы должны реализовать иерархию цветных фигур. Вы бы не стали подклассом Shape с Rectangle и Circle, а затем подклассом Rectangle с RedRectangle, BlueRectangle и GreenRectangle и то же самое для Circle, не так ли? Вы бы предпочли сказать, что каждая форма имеет имеет цвет, и реализовать иерархию цветов, и это шаблон моста. Ну, я бы не стал реализовывать «иерархию цветов», но вы поняли ...
См. Также диаграмму Антона Щастного ниже для графической иллюстрации этого объяснения.
Я не думаю, что цвет является хорошим примером иерархии реализации, это довольно сбивает с толку. В разделе «Шаблоны проектирования» GoF есть хороший пример шаблона Bridge, реализация которого зависит от платформы: PM IBM, X UNIX и т. д.
Адаптер и мост, безусловно, связаны между собой, и разница между ними невелика. Вероятно, что некоторые люди, которые думают, что используют один из этих шаблонов, на самом деле используют другой шаблон.
Объяснение, которое я видел, заключается в том, что адаптер используется, когда вы пытаетесь унифицировать интерфейсы некоторых несовместимых классов, которые уже существует. Адаптер функционирует как своего рода транслятор для реализаций, которые можно рассматривать как наследие.
В то время как шаблон Bridge используется для кода, который, скорее всего, будет новым. Вы проектируете мост, чтобы предоставить абстрактный интерфейс для реализации, которая должна изменяться, но вы также определяете интерфейс этих классов реализации.
Драйверы устройств - это часто цитируемый пример моста, но я бы сказал, что это мост, если вы определяете спецификацию интерфейса для поставщиков устройств, но это адаптер, если вы берете существующие драйверы устройств и создаете класс-оболочку для обеспечить единый интерфейс.
Итак, с точки зрения кода эти два шаблона очень похожи. С точки зрения бизнеса они разные.
См. Также http://c2.com/cgi/wiki?BridgePattern
Привет, Билл. Я не понимаю, почему мы обязательно используем шаблон Bridge в драйверах устройств. Я имею в виду, что мы можем легко делегировать реализацию (чтения, записи, поиска и т. д.) Правильному классу с помощью полиморфизма, верно? Или, может быть, с посетителем? Почему это должен быть мост? Заранее спасибо.
@zgulser, да, вы действительно используете полиморфизм. Шаблон «Мост» описывает один из видов использования подклассов для отделения реализации от абстракции.
Вы имели в виду разделение реализации формы (например, прямоугольника) от абстракции цвета Let's Day, верно? И я полагаю, вы говорите, что есть множество способов сделать это, и Bridge - лишь один из них.
Да, у подклассов есть и другие применения. Именно этот способ использования подклассов и делает его шаблоном моста.
Я имею в виду развязку от абстрактного интерфейса Shape к конкретной реализации Rectangle. Таким образом, вы можете написать код, которому нужен объект типа «Shape», даже если конкретный объект на самом деле является подклассом Shape.
Важно отметить, что шаблон «Мост» может быть реализован по-разному в зависимости от возможностей языка / системы. Любой язык, допускающий определение класса, обязательно должен иметь способ определения интерфейса этого класса. Но не все языки позволяют определять интерфейсы независимо от классов. Для тех, кто это делает (например, C#), шаблон Bridge может быть реализован напрямую с использованием таких определений интерфейсов и при этом пропустить по крайней мере один уровень подкласса и создания экземпляров частного объекта.
Я использовал схему моста в работе. Я программирую на 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
Ваш разработчик является частным лицом. У меня новый вопрос по этому поводу, см. stackoverflow.com/questions/17680762/…
По моему опыту, Bridge - довольно часто повторяющийся шаблон, потому что это решение, когда в области есть два ортогональных измерения. Например. формы и методы рисования, поведения и платформы, форматы файлов и сериализаторы и так далее.
И совет: всегда думайте о шаблонах проектирования с концептуальной точки зрения, а не с точки зрения реализации. С правильной точки зрения, Bridge нельзя путать с адаптером, потому что они решают другую проблему, а композиция превосходит наследование не ради себя, а потому, что позволяет обрабатывать ортогональные проблемы по отдельности.
Когда:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Выполните рефакторинг для:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Я думаю, что это очень прагматичный подход к шаблонам: 1) описывать неоптимальный простой дизайн 2) рефакторинг дизайна / кода для лучшего факторизации
Используйте математическую концепцию, чтобы объяснить шаблон проектирования моста. Очень заинтересован.
Это всего лишь рефакторинг. Намерение шаблона моста: «Отделить абстракцию от ее реализации, чтобы они могли различаться независимо». Где абстракция и где здесь реализация?
Джон красиво изложил это в сообщении блог. Считал, что это хорошее чтение для общего обзора.
Есть комбинация ответов Федерико и Джона.
Когда:
----Shape---
/ \
Rectangle Circle
/ \ / \
BlueRectangle RedRectangle BlueCircle RedCircle
Выполните рефакторинг для:
----Shape--- Color
/ \ / \
Rectangle(Color) Circle(Color) Blue Red
Зачем делать наследование цветов?
@vainolo, потому что цвет - это интерфейс, а синий, красный - конкретные цвета
Это всего лишь рефакторинг. Намерение шаблона моста: «Отделить абстракцию от ее реализации, чтобы они могли различаться независимо». Где абстракция и где здесь реализация?
Разве Rectangle (Color) не более абстрактный, чем BlueRectangle?
@clapas, Абстракция - это свойство "Shape.color", поэтому класс Red и класс Blue являются реализацией, а интерфейс Color является мостом.
Я читал десятки неуместных примеров, в которых просто неправильно используется наследование, т.е. что-то, что кажется естественным, как «имеет», представлено как «есть». Это лучший ответ, который я нашел. Браво! Кратко, информативно, обрисовано в общих чертах.
для меня я думаю об этом как о механизме, в котором вы можете поменять местами интерфейсы. В реальном мире у вас может быть класс, который может использовать более одного интерфейса, 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);
}
}
}
Я проголосовал против, потому что считаю, что это запутанный, плохо отформатированный ответ.
Полностью согласен, как можно публиковать ответы на этом сайте без минимального внимания к отступам и ясности кода?
Вы работаете в страховой компании, где разрабатываете приложение для рабочего процесса, которое управляет различными задачами: бухгалтерский учет, договор, претензии. Это абстракция. Что касается реализации, вы должны иметь возможность создавать задачи из разных источников: электронная почта, факс, электронные сообщения.
Вы начинаете свой дизайн с этих классов:
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 классов заранее для решения этой проблемы. Однако в реальной жизни вы можете не знать заранее количество источников и типов задач; если у вас есть только один источник и два типа задач, вы, вероятно, не будете отделять задачу от источника. Затем общая сложность растет по мере добавления новых источников и типов задач. В какой-то момент вы проведете рефакторинг и, чаще всего, получите решение, подобное мосту.
Пожалуйста, подумайте о том, чтобы принять другой ответ на этот вопрос. Принятый в настоящее время ответ неверен и бесполезен. Новые ответы намного лучше.