何时使用桥接模式以及它与适配器模式有何不同?

发布于 2024-07-10 02:20:03 字数 256 浏览 8 评论 0 原文

有没有人在现实世界的应用程序中使用过桥接模式? 如果有,你是如何使用它的? 是我,还是只是抛出了一点依赖注入的 适配器模式混合? 它真的值得拥有自己的模式吗?

Has anyone ever used the Bridge pattern in a real world application? If so, how did you use it? Is it me, or is it just the Adapter pattern with a little dependency injection thrown into the mix? Does it really deserve its own pattern?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

一袭水袖舞倾城 2024-07-17 02:20:03

Federico 的 的组合约翰的答案。

何时:

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

重构为:

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

There's a combination of Federico's and John's answers.

When:

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

Refactor to:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red
失眠症患者 2024-07-17 02:20:03

桥接模式是旧建议“更喜欢组合而不是继承”的应用。
当您必须以彼此正交的方式对不同时间进行子类化时,它会变得很方便。 假设您必须实现彩色形状的层次结构。 您不会用 Rectangle 和 Circle 来子类化 Shape,然后再用 RedRectangle、BlueRectangle 和 GreenRectangle 来子类化 Rectangle,对于 Circle 来说也是如此,对吗? 您更愿意说每个形状都有一个颜色并实现颜色层次结构,这就是桥接模式。 好吧,我不会实现“颜色层次结构”,但你明白了......

The Bridge pattern is an application of the old advice, "prefer composition over inheritance".
It becomes handy when you must subclass different times in ways that are orthogonal with one another. Say you must implement a hierarchy of colored shapes. You wouldn't subclass Shape with Rectangle and Circle and then subclass Rectangle with RedRectangle, BlueRectangle and GreenRectangle and the same for Circle, would you? You would prefer to say that each Shape has a Color and to implement a hierarchy of colors, and that is the Bridge Pattern. Well, I wouldn't implement a "hierarchy of colors", but you get the idea...

没有伤那来痛 2024-07-17 02:20:03

何时:

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

重构为:

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

When:

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

Refactor to:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2
○闲身 2024-07-17 02:20:03

桥接模式的一个经典示例用于 UI 环境中形状的定义(请参阅 桥接模式 Wikipedia 条目)。 桥接模式是 复合 < a href="http://en.wikipedia.org/wiki/Template_method_pattern" rel="noreferrer">模板 和 策略模式。

这是桥接模式中适配器模式某些方面的常见观点。 不过,引用这篇文章

乍一看,桥接模式看起来很像适配器模式,因为类用于将一种接口转换为另一种接口。 然而,适配器模式的目的是使一个或多个类的接口看起来与特定类的接口相同。 桥接模式旨在将类的接口与其实现分开,以便您可以在不更改客户端代码的情况下改变或替换实现。

A classic example of the Bridge pattern is used in the definition of shapes in an UI environment (see the Bridge pattern Wikipedia entry). The Bridge pattern is a composite of the Template and Strategy patterns.

It is a common view some aspects of the Adapter pattern in the Bridge pattern. However, to quote from this article:

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.

榆西 2024-07-17 02:20:03

根据我的经验,桥接是一种经常重复出现的模式,因为只要域中存在两个正交维度,它就是解决方案。 例如形状和绘图方法、行为和平台、文件格式和序列化器等等。

还有一个建议:始终从概念角度考虑设计模式,而不是从实现角度。 从正确的角度来看,Bridge 不能与 Adapter 混淆,因为它们解决了不同的问题,并且组合优于继承不是因为其本身,而是因为它允许单独处理正交关注点。

In my experience, Bridge is a quite often recurring pattern, because it's the solution whenever there are two orthogonal dimensions in the domain. E.g. shapes and drawing methods, behaviours and platforms, file formats and serializers and so forth.

And an advice: always think of design patterns from the conceptual perspective, not from the implementation perspective. From the right point of view, Bridge cannot be confused with Adapter, because they solve a different problem, and composition is superior to inheritance not because of the sake of itself, but because it allows to handle orthogonal concerns separately.

你列表最软的妹 2024-07-17 02:20:03

适配器和桥接器当然是相关的,而且区别很微妙。 一些认为自己正在使用其中一种模式的人可能实际上正在使用另一种模式。

我看到的解释是,当您尝试统一一些已经存在的不兼容类的接口时,会使用适配器。 适配器充当一种可被视为遗留的实现的转换器。

而桥接模式则用于更有可能是未开发的代码。 您设计桥是为了为需要变化的实现提供抽象接口,但您还定义了这些实现类的接口。

设备驱动程序是经常引用的 Bridge 示例,但如果您正在为设备供应商定义接口规范,我会说它是一个 Bridge,但如果您采用现有设备驱动程序并制作一个包装类来定义它,那么它就是一个适配器。提供统一的接口。

因此从代码角度来看,这两种模式非常相似。 从商业角度来看,它们是不同的。

另请参阅http://c2.com/cgi/wiki?BridgePattern

Adapter and Bridge are certainly related, and the distinction is subtle. It's likely that some people who think they are using one of these patterns are actually using the other pattern.

The explanation I've seen is that Adapter is used when you're trying to unify the interfaces of some incompatible classes that already exist. The Adapter functions as a kind of translator to implementations that could be considered legacy.

Whereas the Bridge pattern is used for code that is more likely to be greenfield. You're designing the Bridge to provide an abstract interface for an implementation that needs to vary, but you also define the interface of those implementation classes.

Device drivers is an often-cited example of Bridge, but I'd say it's a Bridge if you're defining the interface spec for device vendors, but it's an Adapter if you're taking existing device drivers and making a wrapper-class to provide a unified interface.

So code-wise, the two patterns are very similar. Business-wise, they're different.

See also http://c2.com/cgi/wiki?BridgePattern

我为君王 2024-07-17 02:20:03

我在工作中使用过桥接模式。 我用 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 的用户无需包含该头文件。 您所要做的就是在定义 A 之前使用 Aimpl 的前向声明,并将引用 pImpl 的所有成员函数的定义移动到.cpp 文件。 这使您能够将 Aimpl 标头保持私有,并减少编译时间。

I have used the bridge pattern at work. I program in C++, where it is often called the PIMPL idiom (pointer to implementation). It looks like this:

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

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

In this example class A contains the interface, and class Aimpl contains the implementation.

One use for this pattern is to expose only some of the public members of the implementation class, but not others. In the example only Aimpl::foo() can be called through the public interface of A, but not Aimpl::bar()

Another advantage is that you can define Aimpl in a separate header file that need not be included by the users of A. All you have to do is use a forward declaration of Aimpl before A is defined, and move the definitions of all the member functions referencing pImpl into the .cpp file. This gives you the ability to keep the Aimpl header private, and reduce the compile time.

属性 2024-07-17 02:20:03

将形状示例放入代码中:

#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

请注意,可以轻松地将新颜色和形状添加到系统中,而不会因排列而导致子类爆炸。

To put shape example in code:

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

The output is:

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

Note the ease with which new colors and shapes can be added to the system without leading to an explosion of subclasses due to permutations.

杯别 2024-07-17 02:20:03

您在一家保险公司工作,开发一个工作流程应用程序来管理不同类型的任务:会计、合同、索赔。 这就是抽象。 在实施方面,您必须能够从不同来源创建任务:电子邮件、传真、电子消息。

您从这些类开始设计:

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 个类层次结构来处理此问题。 然而,在现实生活中,您可能事先不知道源和任务类型的数量; 如果您只有一种源和两种任务类型,您可能不会将任务与源分离。 然后,随着新来源和任务类型的添加,整体复杂性会增加。 在某些时候,您将进行重构,并且最常见的是最终得到一个类似桥的解决方案。

You're working for an insurance company where you develop a workflow application that manages different kind of tasks: accounting, contract, claims. This is the abstraction. On the implementation side, you must be able to create tasks from different sources: email, fax, e-messaging.

You begin your design with these classes:

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

Now, since each sources must be handled in a specific way, you decide to specialize each task type:

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 {...}

You end up with 13 classes. Adding a task type or a source type becomes challenging. Using the bridge pattern produces something easier to maintain by decoupling the task (the abstraction) from the source (which is an implementation concern):

// 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 {...}

Adding a task type or a source is now much more easier.

Note: Most developers would not create the 13 classes hierarchy upfront to handle this problem. However, in real life, you might not know in advance the number of source and task types ; if you have only one source and two task types, you would probably not decouple Task from Source. Then, the overall complexity grows as new sources and task types are added. At some point, you will refactor and, most often, end up with a bridge-like solution.

幸福还没到 2024-07-17 02:20:03

如果您获得相同旧形状和颜色示例的板,我将为您提供一个桥接图案的新示例。

假设您有不同的付款方式,例如卡付款和网上银行。 还有不同的支付网关,如 CITI 银行和汇丰银行。

然后您只需将支付网关成员添加到支付模式即可。 并在运行时将此信息传递给支付模式对象。 并付款。

例如,它将在 CITI 银行支付网关上进行卡支付。

I’ll give you one new example for a bridge pattern if you get board of the same old Shape and Color example.

Let’s say you have different ways to make payments like Card payment and net banking. And there are different payment gateways like CITI bank and HSBC bank.

Then you can just add the payment gateway member to the payment modes. And at runtime pass this information to the payment mode object. And make the payment.

So for example it will make the Card payment on the CITI bank payment gateway.

清风挽心 2024-07-17 02:20:03

适配器和桥设计模式之间的主要区别在于它们的意图。 来自设计模式,第 4 章,“桥梁”部分,“相关模式”段落 (Gamma 等人 1994):

适配器 (139) 模式旨在使不相关的类一起工作。 它通常在设计后应用于系统。 另一方面,桥在设计中预先使用,以使抽象和实现独立变化。

  1. “独立”一词意味着桥设计模式适用于这种情况,因为形状和颜色是独立的:
             ------Shape-----                           Shape       Colour
            /                \              Bridge       / \         / \
       Circle                Square         ----->  Circle Square  Red Blue
        / \                   / \
RedCircle BlueCircle  RedSquare BlueSquare
  1. 但它不适用于这种情况,因为颜色取决于形状:
             ------Shape-----
            /                \
       Circle                Square
        / \                   / \
RedCircle BlueCircle  RedSquare GreenSquare
  1. 在这种情况下它是无用的,因为有单一的形状和颜色单一颜色:
Shape
  |
Circle
  |
RedCircle

情况 1 的表格表示:

| Shape  | Colour |          | Shape  |  | Colour |
| ------ | ------ |          | ------ |  | ------ |
| circle | red    |  Bridge  | circle |  | red    |
| circle | blue   |  ----->  | square |  | blue   |
| square | red    |
| square | blue   |

情况 2 的表格表示:

| Shape  | Colour |
| ------ | ------ |
| circle | red    |
| circle | blue   |
| square | red    |
| square | green  |

情况 3 的表格表示:

| Shape  | Colour |
| ------ | ------ |
| circle | red    |

因此,面向对象编程中的 Bridge 设计模式等价于投影连接范式的规范化,表示为 PJ/NF (Fagin 1979),在关系数据库中。

在情况 1 中,关系模式 R(Shape, Colour) 具有多值依赖关系 ∅ ↠ {Shape} (独立形状)和 ∅ ↠ {Colour} (独立颜色),这些依赖关系并不由关键依赖集 {KEY({Shape,颜色})},所以它不在 PJ/NF 中。 它的投影采用 PJ/NF,因为 R1(Shape) 具有简单的函数依赖项 {Shape} → {Shape},这是由一组关键依赖项 {KEY 隐含的({Shape})} 和 R2(Colour) 具有简单的函数依赖 {Colour} → {Colour},这是由一组关键依赖项 {KEY( {颜色})}。

在情况 2 中,关系模式 R(Shape, Colour) 具有简单的多值依赖项 {Shape} ↠ {Colour},它由一组键依赖项 {KEY({Shape, Colour}) 隐含},所以它已经在 PJ/NF 中了。

在情况 3 中,关系模式R(形状,颜色)具有函数依赖性 ∅ → {形状}(单一形状)和 ∅ → {颜色}(单一颜色),它们由一组隐含的关键依赖项 {KEY({Shape, Colour}), KEY(∅)},因此它已经在 PJ/NF 中。

The key difference between the Adapter and Bridge design patterns lies in their intents. From Design Patterns, chapter 4, section ‘Bridge’, paragraph ‘Related Patterns’ (Gamma et al. 1994):

The Adapter (139) pattern is geared toward making unrelated classes work together. It is usually applied to systems after they’re designed. Bridge, on the other hand, is used up-front in a design to let abstractions and implementations vary independently.

  1. The word ‘independently’ means that the Bridge design pattern applies in this situation because shapes and colours are independent:
             ------Shape-----                           Shape       Colour
            /                \              Bridge       / \         / \
       Circle                Square         ----->  Circle Square  Red Blue
        / \                   / \
RedCircle BlueCircle  RedSquare BlueSquare
  1. But it does not apply in this situation because colours depend on shapes:
             ------Shape-----
            /                \
       Circle                Square
        / \                   / \
RedCircle BlueCircle  RedSquare GreenSquare
  1. And it is useless in this situation because there are a single shape and a single colour:
Shape
  |
Circle
  |
RedCircle

Tabular representation of situation 1:

| Shape  | Colour |          | Shape  |  | Colour |
| ------ | ------ |          | ------ |  | ------ |
| circle | red    |  Bridge  | circle |  | red    |
| circle | blue   |  ----->  | square |  | blue   |
| square | red    |
| square | blue   |

Tabular representation of situation 2:

| Shape  | Colour |
| ------ | ------ |
| circle | red    |
| circle | blue   |
| square | red    |
| square | green  |

Tabular representation of situation 3:

| Shape  | Colour |
| ------ | ------ |
| circle | red    |

Thus the Bridge design pattern in object-oriented programming is equivalent to normalisation to projection–join normal form, denoted PJ/NF (Fagin 1979), in relational databases.

In situation 1, the relation schema R(Shape, Colour) has the multivalued dependencies ∅ ↠ {Shape} (independent shapes) and ∅ ↠ {Colour} (independent colours) which are not implied by the set of key dependencies {KEY({Shape, Colour})}, so it is not in PJ/NF. Its projections are in PJ/NF because R1(Shape) has the trivial functional dependency {Shape} → {Shape} which is implied by the set of key dependencies {KEY({Shape})} and R2(Colour) has the trivial functional dependency {Colour} → {Colour} which is implied by the set of key dependencies {KEY({Colour})}.

In situation 2, the relation schema R(Shape, Colour) has the trivial multivalued dependency {Shape} ↠ {Colour} which is implied by the set of key dependencies {KEY({Shape, Colour})}, so it is already in PJ/NF.

In situations 3, the relation schema R(Shape, Colour) has the functional dependencies ∅ → {Shape} (single shape) and ∅ → {Colour} (single colour) which are implied by the set of key dependencies {KEY({Shape, Colour}), KEY(∅)}, so it is already in PJ/NF.

握住我的手 2024-07-17 02:20:03

对我来说,我认为它是一种可以交换接口的机制。 在现实世界中,您可能有一个可以使用多个接口的类,Bridge 可以让您进行交换。

for me i think of it as a mechanism where you can swap interfaces. In the real world you might have a class that can use more then one interface, Bridge lets you swap.

三生殊途 2024-07-17 02:20:03
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);
}
}
}
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);
}
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文