桥模式和适配器模式之间的差异

时间:2009-09-15 04:51:32

标签: design-patterns adapter bridge

Bridge和Adapter模式之间有什么区别?

11 个答案:

答案 0 :(得分:151)

  

“适配器在设计完成后使其工作; Bridge制作它们   他们之前的工作。 [GoF,p219]“

实际上,适配器模式在您拥有现有代码时非常有用,无论是第三方还是内部代码,但是您无法控制,或者无法更改以满足您需要的界面它来。例如,我们有一个SuperWeaponsArray可以控制一系列精美的世界末日设备。

public class SuperWeaponsArray {
  /*...*/

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

大。除了我们意识到我们的武器库中有一个核装置,它早于转换到武器界面。但我们真的很喜欢它在这里工作......所以我们做了什么......楔入它!

NukeWeaponsAdaptor - 基于我们的Nuke类,但导出Weapon接口。甜蜜,现在我们肯定会摧毁这个世界。它看起来像是一块混合物,但它使事情有效。


Bridge 模式是您预先实现的模式 - 如果您知道有两个正交层次结构,它提供了一种方法来解耦接口和实现,使您无法获得疯狂的课程数量。假设你有:

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

MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile

答案 1 :(得分:13)

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

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

如果您有一组公司标准的Web服务API,您希望将其提供给另一个应用程序的现有可扩展性界面,您可以考虑编写一组适配器来执行此操作。请注意,有一个灰色区域,这更多地是关于技术上如何定义模式,因为其他模式如外观是相似的。

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

Bridge模式将允许您可能具有算法或系统的替代实现。

虽然不是经典的Bridge模式示例,但想象一下,如果你有一些数据存储的实现:一个在空间有效,另一个在原始性能方面有效......并且你有一个商业案例可以在你的应用程序或框架。

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

答案 2 :(得分:9)

<强> 适配器:

  1. 这是一种结构模式
  2. 使用两个不兼容的接口
  3. 非常有用 来自dofactory文章的

    UML图表

    enter image description here

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

    适配器 :将接口Adaptee调整为目标接口。

    Adaptee :定义需要调整的现有界面。

    客户端 :与符合Target界面的对象协作。

    示例:

    正方形和矩形是两种不同的形状,每种形状的区域()都需要不同的方法。但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. 因为组合已被用来代替继承,所以有可能
    4. 编辑:(根据@quasoft建议)

      此模式中有四个组件。

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

      2. RefinedAbstraction :它实现了抽象:

      3. Implementor :它定义了一个实现接口

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

      5. 代码段:

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

        相关文章:

        When do you use the Bridge Pattern? How is it different from Adapter pattern?

        来自sourcemaking文章

        主要差异

        1. 适配器使设计完成后可以正常工作; Bridge让它们在它们工作之前就可以工作了。
        2. Bridge是预先设计的,让抽象和实现独立变化。适配器经过改装,可以使不相关的类一起工作。

答案 3 :(得分:8)

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

答案 4 :(得分:2)

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

适配器与网桥

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

这些模式之间的主要区别在于它们的意图。适配器专注于 解决两个现有接口之间的不兼容性。它不专注于如何 这些接口已实现,也未考虑它们如何独立发展。这是使两个独立设计的类一起工作的一种方式 而不重新实现其中一个。另一方面,Bridge桥接抽象及其(可能很多)实现。提供稳定的界面 甚至可以让您改变实现它的类。它还可以容纳 随着系统的发展有新的实现方式。

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

答案 5 :(得分:1)

根据另一个stackoverflow答案here,我希望得到更简短的答案:

  • 适配器在您具有抽象界面并且想要 将该接口映射到具有相似功能的另一个对象 角色,但界面不同。

  • Bridge 与Adapter非常相似,但是当您将其称为Bridge时 定义抽象接口和基础实现。 即您没有适应某些旧版或第三方代码,而是 所有代码的设计者,但您需要能够换出 不同的实现方式。

答案 6 :(得分:0)

假设您具有(通用/抽象)绘图功能的抽象Shape类和实现Shape的Circle。桥模式只是一种双向抽象方法,用于解耦实现(在Circle中绘制)和通用/抽象功能(在Shape类中绘制)。

这究竟是什么意思?乍一看,这听起来像你已经做过的事情(通过依赖倒置)。所以不用担心有一个更少的模块代码库。但它背后的哲学更为深刻。

根据我的理解,当我需要添加与当前系统密切相关的新类(如RedCircle或GreenCircle)时,可能会出现使用模式的需要,并且它们只有一个功能(如颜色)不同。而且我需要Bridge模式,特别是如果要频繁更改现有系统类(Circle或Shape)并且您不希望新添加的类受这些更改影响。这就是为什么将通用绘图功能抽象到新界面中,以便您可以独立于Shape或Circle来改变绘图行为。

答案 7 :(得分:0)

Bridge是改进的适配器。 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

答案 8 :(得分:0)

有很多答案可以区分适配器和桥接器。 但是,当ppl在寻找代码示例时,我将举一个将其设计成时间轴故事的Adapter Design Pattern示例:

    //---------------------------------------External Vendor/Provider--------------------------------
   
            //Adaptee | RussianTankInterface is adaptee | adaptee lives in is own lala land and do not care about any other class or interface
            RussianTankInterface smerch9K58 = new RussianTank("The Russian Artillery bought by India in October 2015");
                smerch9K58.aboutMyself();
                smerch9K58.stuff();
                smerch9K58.rotate();
                smerch9K58.launch();
        
   
    //---------------------------------2016 : India manufactures Bharat52 ------------------------------ 
    
            //Client_1 :: IndianTank 
            EnemyAttacker bharat52Attacker = new IndianTank("Tank built in India delivered to Army in Jul 2016"); 
   
            // behaves normally -------------------------(1)
                bharat52Attacker.aboutMe();
                bharat52Attacker.load();
                bharat52Attacker.revolve();
                bharat52Attacker.fireArtillery();
                
    //---------------------------------2019 : India mnufactures Pinaka, and thought about fusion with Russian technology - so adaption required ------------------------------ 
                  
            //Client_2 :: IndianTank 
            EnemyAttacker pinakaAttacker = new IndianTank("Tank built in India in 1998 got upgraded_v1 in 9 Sep 2019"); 
            
            
            
#####----Bilateral-Coalition happens----## 
#####       India  : I want a fusion artillery technology with 
#####                       1) Indian materials and brain-power but
#####                       2) Russian machine-parts-movement technology 
#####       Russia : Give me your Interface - at max we can help by providing an Adapter 
    
    //---------------------------------------External Vendor/Provider-----------------------------------
    
            //Adapter :: RussianTechnologyAdapter | Russia gets EnemyAttacker interface only from India & creates RussianTechnologyAdapter 
            RussianTechnologyAdapter russianTechnologyAdapter = new RussianTechnologyAdapter(smerch9K58);
           
           
            //Target | EnemyAttacker was initially ClientInterface but later becomes the Target as story evolves | <- client owns this Interface
            EnemyAttacker dhanushAttacker = russianTechnologyAdapter;
            
#####----Russia keeps her Word----## 
#####       Russia to India : Here you go! Take Dhanush, a wrapper over our encapsulated adapter, and plug-in anything conforming to your EnemyAttacker.
#####       India : Thanks a lot!

    //--------------------------------- 2020 : India returns back happily with dhanushAttacker--------------------------------------- 
            
            //Client_2 - adapted behavior -------------------------(2)
                dhanushAttacker.setNavigationCapability(pinakaAttacker.getCuttingEdgeNavigableTargets());
                dhanushAttacker.aboutMe(); //calls RussianInstance -> aboutMyself()
                dhanushAttacker.load();    //calls RussianInstance -> stuff()
                dhanushAttacker.revolve(); //calls RussianInstance -> rotate()
                dhanushAttacker.fireArtillery();  //calls RussianInstance -> launch()
                       
          

请注意:

  • (1)和(2)中的相同API行为不同
  • 印度可以将自己的大脑推向俄罗斯的WireFrame,例如dhanushAttacker.setNavigationCapability(pinakaAttacker.get(..))

值得注意的点

客户端拥有:

-  Invoker /Use(uses Adapter later point)/Client
-  ClientInterface (a.k.a Target )

稍后共享:

- ClientInterface  ( becomes Target after sharing)

接收方拥有:

- Adapter (later shared directly or as a wrapper )  
- Adaptee

     

希望有人也为Bridge内联:)

答案 9 :(得分:0)

Bridge 与 Adapter 非常相似,但是当你定义抽象接口和底层实现时我们称之为 Bridge,意味着不适应外部代码,你是所有代码的设计者,但你需要能够更换不同的实现。

答案 10 :(得分:0)

我认为这很简单。

适配器旨在允许第三方应用程序与您的应用程序配合使用。 相反,您的应用程序可以与第三方应用程序一起使用。

使用桥接模式,它应该在不实现适配器的情况下连接两个或多个应用程序。

实际上,桥接器是两个应用程序交互的接口。作为桥接的示例,这些是 PHP 中的 PSR 接口。

示例:

其他应用

<?php

interface IRequestDataOtherApp {};
 
interface IResponseDataOtherApp {};
 
class ResponseDataOtherApp implements IResponseDataOtherApp   {

}; 
 
class OtherApp {
    public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
    {
        // code
        return new ResponseDataOtherApp ();
    }
}

我的应用

<?php 

interface IResponseDataMyApp {};

interface IReqestDataMyApp {};

class ReqestDataMyApp implements IReqestDataMyApp {};

class Adapter {
   
    public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp 
    {
      
    }
    public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
    {
      
    }
};
$unformattedResponse=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
$myResponse=ResponseAdapter::convertResponseData($unformattedResponse);
//...

在之前的示例中,我们为每个请求和每个响应实现了 2 个适配器。 如果我们重写示例并尝试实现桥接。

<?php
    interface IBridgeResponse {};

其他应用

<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
 
class ResponseDataOtherApp  implements IBridgeResponse, IResponseDataOtherApp   {

}; 
 
class OtherApp {
    public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
    {
        // code
        return new ResponseDataOtherApp  ();
    }
}

我的应用

<?php 

interface IResponseDataMyApp {};

interface IReqestDataMyApp {};

class ReqestDataMyApp implements IReqestDataMyApp {};

class Adapter {

    public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp 
    {
      
    }

    public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
   {
      
   }
};

$response=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));

if($response instanceof IBridgeResponse){
  /** 
The component has implemented IBridgeResponse interface,
thanks to which our application can interact with it.
This is the bridge.

Our application does not have to create an additional adapter
 (if our application can work with IBridgeResponse).
*/
}
//...

在六边形架构中,如果您从一开始就编写应用程序并准备接受正在使用的另一个应用程序的规则,则端口(接口、合同)可以充当“桥梁”。 否则,您将不得不编写“适配器”。