Кто-нибудь когда-либо использовал паттерн моста в реальном приложении? Если да, то как ты это использовал? Это я, или это просто паттерн адаптера с небольшим внедрением зависимостей в микс? Это действительно заслуживает своего собственного образца?
Кто-нибудь когда-либо использовал паттерн моста в реальном приложении? Если да, то как ты это использовал? Это я, или это просто паттерн адаптера с небольшим внедрением зависимостей в микс? Это действительно заслуживает своего собственного образца?
Ответы:
Классический пример шаблона Bridge используется в определении фигур в среде пользовательского интерфейса (см. Статью Wikipedia по шаблону Bridge ). Схема моста представляет собой композит из шаблона и стратегии шаблонов.
Это общий вид некоторых аспектов шаблона адаптера в шаблоне моста. Однако, чтобы процитировать из этой статьи :
На первый взгляд, паттерн Bridge очень похож на паттерн Adapter в том смысле, что класс используется для преобразования одного вида интерфейса в другой. Однако цель шаблона Adapter - сделать так, чтобы интерфейсы одного или нескольких классов выглядели так же, как и у определенного класса. Шаблон Bridge предназначен для отделения интерфейса класса от его реализации, чтобы вы могли изменять или заменять реализацию без изменения клиентского кода.
Есть комбинация ответов Федерико и Джона .
Когда:
----Shape---
/ \
Rectangle Circle
/ \ / \
BlueRectangle RedRectangle BlueCircle RedCircle
Рефакторинг для:
----Shape--- Color
/ \ / \
Rectangle(Color) Circle(Color) Blue Red
Паттерн «Мост» - это применение старого совета «предпочитайте композицию наследованию». Это становится удобным, когда вы должны создавать подклассы разного времени так, чтобы они были ортогональны друг другу. Скажем, вы должны реализовать иерархию цветных фигур. Вы бы не создали подкласс Shape с Rectangle и Circle, а затем создали бы подкласс Rectangle с RedRectangle, BlueRectangle и GreenRectangle и то же самое для Circle, не так ли? Вы бы предпочли сказать, что у каждой фигуры есть Цвет и реализовать иерархию цветов, и это - Образец Моста. Ну, я бы не реализовал "иерархию цветов", но вы поняли ...
Когда:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Рефакторинг для:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Адаптер и Мост, безусловно, связаны, и различие тонкое. Вполне вероятно, что некоторые люди, которые думают, что используют один из этих шаблонов, фактически используют другой шаблон.
Объяснение, которое я видел, заключается в том, что Adapter используется, когда вы пытаетесь объединить интерфейсы некоторых несовместимых классов, которые уже существуют . Адаптер функционирует как своего рода переводчик для реализаций, которые можно считать устаревшими .
Принимая во внимание, что паттерн Bridge используется для кода, который, скорее всего, будет новым. Вы проектируете Bridge для предоставления абстрактного интерфейса для реализации, которая должна изменяться, но вы также определяете интерфейс этих классов реализации.
Драйверы устройств - часто цитируемый пример Bridge, но я бы сказал, что это Bridge, если вы определяете спецификацию интерфейса для поставщиков устройств, но это Adapter, если вы берете существующие драйверы устройств и делаете класс-оболочку для обеспечить единый интерфейс.
Таким образом, с точки зрения кода, эти две модели очень похожи. В бизнесе они разные.
Смотрите также http://c2.com/cgi/wiki?BridgePattern
По моему опыту, Bridge является довольно часто повторяющимся шаблоном, потому что это решение, когда в области есть два ортогональных измерения . Например, формы и методы рисования, поведение и платформы, форматы файлов и сериализаторы и так далее.
И совет: всегда думайте о шаблонах проектирования с концептуальной точки зрения , а не с точки зрения реализации. С правильной точки зрения Bridge нельзя спутать с Adapter, потому что они решают другую проблему, а композиция превосходит наследование не потому, что она сама по себе, а потому, что она позволяет обрабатывать ортогональные задачи отдельно.
Назначение моста и адаптера различно, и нам нужны оба шаблона по отдельности.
Образец моста:
Используйте шаблон Bridge, когда:
@ Джон Сонмез ответ ясно показывает эффективность схемы моста в сокращении иерархии классов.
Вы можете обратиться к ссылке ниже документации, чтобы лучше понять шаблон моста с примером кода
Адаптер шаблон :
Ключевые отличия:
Связанный вопрос SE с диаграммой UML и рабочим кодом:
Разница между образцом моста и образцом адаптера
Полезные статьи:
sourcemaking адаптер шаблон статьи
journaldev мост шаблон статьи
РЕДАКТИРОВАТЬ:
Пример реального использования Bridge Pattern (в соответствии с предложением meta.stackoverflow.com, включенный в этот пост пример сайта с документацией, поскольку документация собирается заходить)
Шаблон моста отделяет абстракцию от реализации, так что оба могут варьироваться независимо. Это было достигнуто с помощью композиции, а не наследования.
Шаблон моста 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
определяют реализацию (Redefined абстракции) для абстракции: addGear()
: Он содержит Gear
- либо Manual
илиAuto
Варианты использования для шаблона 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
заголовок закрытым и сократить время компиляции.
Чтобы поместить пример формы в код:
#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
Обратите внимание на легкость, с которой новые цвета и формы могут быть добавлены в систему, не приводя к взрыву подклассов из-за перестановок.
Вы работаете в страховой компании, в которой вы разрабатываете приложение для документооборота, которое решает различные задачи: бухгалтерский учет, заключение договоров, претензии. Это абстракция. Что касается реализации, вы должны иметь возможность создавать задачи из разных источников: электронная почта, факс, электронная почта.
Вы начинаете свой дизайн с этих классов:
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 классов заранее для решения этой проблемы. Однако в реальной жизни вы можете заранее не знать количество типов источников и задач; если у вас есть только один источник и два типа задач, вы, вероятно, не отделите задачу от источника. Затем общая сложность возрастает по мере добавления новых источников и типов задач. В какой-то момент вы проведете рефакторинг и, чаще всего, получите мостоподобное решение.
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);
}
}
}