桥接模式和适配器模式之间的区别


Answers:


173

“适配器使事情在设计之初就起作用; Bridge使事情在其先于事物起作用。[GoF,p219]”

实际上,当您拥有现有代码(无论是第三方代码还是内部代码)但不受控制,或者无法更改以完全满足您所需的接口时,适配器模式就非常有用。例如,我们有一个SuperWeaponsArray,它可以控制世界末日设备的精细阵列。

public class SuperWeaponsArray {
  /*...*/

  public void destroyWorld() {
    for (Weapon w : armedWeapons) {
      w.fire();
    }
  }
}

大。除非我们意识到我们的武器库中有一个核武器,该核武器大大早于转换为武器界面。但是我们真的希望它能在这里工作...所以我们该怎么做...把它塞进去!

NukeWeaponsAdaptor-基于我们的Nuke类,但是导出了Weapon接口。亲爱的,现在我们肯定可以摧毁世界。似乎有点麻烦,但它使事情正常进行。


大桥的图案是你实现了前面-如果你知道你有两个正交的层次结构,它提供了一种分离的界面并以这样的方式,你没有得到的类的疯狂数量的实施。假设您有:

文件对象的MemoryMappedFile和DirectReadFile类型。假设您希望能够从各种来源读取文件(可能是Linux与Windows的实现等)。Bridge可帮助您避免出现以下情况:

MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile


9
否决了,您可以使用更抽象的代码清单吗?该示例过于具体,令人困惑。

36
@omouse赞成,示例代码实际上并不是使这个答案正确的原因。对于细心的读者来说,有足够的指针来开始区分模式,所以总而言之-这一个很好的答案。
维克多·法拉兹达吉

15
您能提供一些实际的桥接模式代码示例吗?
Jaime Hablutzel

2
我想象许多人以与我相同的方式来解决这个问题-他们可能已经在寻找这两种模式的代码,但是认识到一些相似之处,并意识到可以通过并置这两种模式来进一步巩固他们的理解。至少对我而言,关于网桥的帮助可避免您结束Windows和Linux专用文件的这一行至少有助于理解Bridge Pattern的“ Implementor”(dofactory.com/net/bridge-design-pattern)与“适配器”。
乔丹

3
“适配器使事情在设计之初就起作用了; Bridge使事情在其先于事物起作用。” 在我读过的书中根本没有指定,因此很难区分两者。我想阅读GOF毕竟是值得的努力...
Alexander Derck '16

15

http://en.wikipedia.org/wiki/Adapter_pattern

适配器模式更多地是关于使现有代码与更新的系统或接口一起使用。

如果您要向另一个应用程序的现有扩展接口提供一组公司标准的Web服务API,则可以考虑编写一组适配器来实现。请注意,存在一个灰色区域,这更多地是关于技术上定义图案的方式,因为其他图案(如外墙)是相似的。

http://en.wikipedia.org/wiki/Bridge_pattern

桥接模式将使您可能拥有算法或系统的替代实现。

尽管这不是经典的Bridge模式示例,但请想象一下,如果您有一些数据存储的实现:一种在空间上高效,另一种在原始性能上高效...并且您有一个在应用程序或框架中同时提供的业务案例。

对于您的问题,“我可以在哪里使用哪种模式”,答案是,无论您的项目对哪里有意义!也许考虑提供一个澄清的编辑来指导您认为需要使用其中一种的讨论。


14

适配器:

  1. 这是一种结构模式
  2. 使用两个不兼容的接口很有用

UML图:来自工厂文章:

在此处输入图片说明

Target :定义客户端使用的特定于域的接口。

适配器:使接口Adaptee适应目标接口。

Adaptee:定义需要调整的现有接口。

客户端 :与符合Target接口的对象协作。

例:

正方形和矩形是两种不同的形状,获取它们的area()需要不同的方法。但是Square仍然可以通过转换某些属性在Rectangle接口上工作。

public class AdapterDemo{
    public static void main(String args[]){
        SquareArea s = new SquareArea(4);
        System.out.println("Square area :"+s.getArea());
    }
}

class RectangleArea {
    public int getArea(int length, int width){
        return length * width;
    }
}

class SquareArea extends RectangleArea {

    int length;
    public SquareArea(int length){
        this.length = length;
    }
    public int getArea(){
        return getArea(length,length);
    }
}

桥:

  1. 是结构样式
  2. 它将抽象与其实现分离,并且两者可以独立变化
  3. 这是可能的,因为使用了合成来代替继承

编辑:(根据@quasoft建议)

在此模式中,您有四个组成部分。

  1. 抽象:它定义了一个接口

  2. RefinedAbstraction:它实现抽象:

  3. 实现者:定义实现的接口

  4. ConcreteImplementor:它实现了Implementor接口。

程式码片段:

Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();

gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();

相关文章:

什么时候使用桥接模式?与适配器模式有何不同?

主要区别:来源制作文章相关

  1. 适配器使事情在设计之后就可以正常工作。桥让他们先于工作。
  2. Bridge是预先设计的,以使抽象和实现独立变化。对适配器进行了改造,以使不相关的类一起工作。

在答案中包括来自文档的汽车/卡车/齿轮示例。很好的例子和类比。
quasoft

8

这篇文章已经存在了一段时间。但是,重要的是要了解立面在某种程度上类似于适配器,但并不是完全一样的。适配器将现有类“适应”到通常不兼容的客户端类。假设您有一个旧的工作流系统,您的应用程序将其用作客户端。您的公司可能会用新的“不兼容”系统(就界面而言)替换工作流系统。在大多数情况下,您可以使用适配器模式并编写实际调用新工作流引擎接口的代码。桥通常以不同的方式使用。如果您实际上有一个需要使用不同文件系统(例如本地磁盘,NFS等)的系统,则可以使用桥接模式并创建一个抽象层来与所有文件系统一起使用。对于桥接模式,这基本上是一个简单的用例。Facade和适配器确实共享一些属性,但是Facade通常用于简化现有的interface / class。在EJB的早期,没有对EJB的本地调用。开发人员总是获得该存根,将其缩小并称为“伪远程”。这通常会导致性能问题(尤其是在通过电线真正调用时)。有经验的开发人员将使用外观模式为客户端提供非常粗粒度的界面。然后,该外观将依次调用多个不同的更细粒度的方法。总而言之,这大大减少了所需的方法调用次数并提高了性能。


但是,似乎不在这个问题的范围内,权衡Adapter&Bridge与Facade可能是非常合适的。
科迪2014年

1

桥是改进的适配器。Bridge包括适配器,并为其增加了额外的灵活性。这是Ravindra答案中的元素如何在模式之间映射:

      Adapter  |    Bridge
    -----------|---------------
    Target     | Abstraction
    -----------|---------------
               | RefinedAbstraction
               |
               |   This element is Bridge specific. If there is a group of 
               |   implementations that share the same logic, the logic can be placed here.
               |   For example, all cars split into two large groups: manual and auto. 
               |   So, there will be two RefinedAbstraction classes.
    -----------|--------------- 
    Adapter    | Implementor
    -----------|---------------
    Adaptee    | ConcreteImplementor

1

在最上面的答案中,@ James引用了GoF的第219页中的一句话。我认为在这里复制完整的解释是值得的。

适配器与桥接器

适配器和桥接器模式具有一些公共属性。两者都通过提供对另一个对象的间接级别来提高灵活性。两者都涉及从接口而非其自身向该对象转发请求。

这些模式之间的主要区别在于它们的意图。适配器专注于解决两个现有接口之间的不兼容性。它既不关注这些接口的实现方式,也不考虑它们如何独立发展。这是一种使两个独立设计的类一起工作而不重新实现另一个的方法。另一方面,Bridge桥接抽象及其(可能很多)实现。它为客户端提供了稳定的接口,尽管它使您可以更改实现该接口的类。随着系统的发展,它也适应新的实现。

由于这些差异,在软件生命周期的不同时间点经常使用Adapter和Bridge。当您发现两个不兼容的类应该一起工作时,通常会需要一个适配器,通常是避免复制代码。不可预见的耦合。相反,网桥的用户预先了解抽象必须具有多种实现,并且两者都可以独立发展。适配器模式使事情在设计之后就可以工作;大桥使他们的工作之前,他们是。这并不意味着适配器在某种程度上不如Bridge;每个模式仅解决一个不同的问题。


0

假设您具有一个抽象的Shape类,该类具有(通用/抽象的)绘图功能,以及一个实现Shape的Circle。桥接模式只是一种将实现(Circle中的绘图)和通用/抽象功能(Shape类中的绘图)分离的双向方法。

到底是什么意思 乍一看,这听起来像是您已经在制作的东西(通过依赖反转)。因此,不必担心使用更少的ridig或更多的模块化代码库。但这背后有一个更深层的哲学。

根据我的理解,当我需要添加与当前系统密切相关的新类(例如RedCircle或GreenCircle)并且它们之间只有一个功能(例如color)不同时,可能会出现使用模式的需求。而且我将需要桥接模式,特别是如果现有系统类(Circle或Shape)要经常更改,并且您不希望新添加的类受到这些更改的影响时,尤其如此。因此,通用绘图功能被抽象到一个新界面中,因此您可以独立于Shape或Circle更改绘图行为。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.