桥接模式和策略模式有什么区别?

136
我尝试阅读了许多有关dofactory、维基百科和其他网站的文章,但我仍然不知道桥接模式和策略模式之间的区别。
我知道它们都将抽象与其实现分离,并可以在运行时更改实现。但是我仍然不知道在哪种情况下应该使用策略模式,在哪种情况下应该使用桥接模式。
15个回答

82
语义学。 来自 维基百科

策略模式的 UML 类图与桥接模式相同。但是,这两种设计模式在意图上不同。虽然策略模式用于行为,但桥接模式用于结构。

上下文和策略之间的耦合比桥接模式中抽象和实现之间的耦合要紧密。

据我所知,当您需要抽象可能来自外部来源的行为时(例如,配置可以指定加载某个插件程序集),您使用策略模式;而当您使用相同的结构使您的代码更整洁时,则使用桥接模式。 实际代码看起来非常相似-您只是出于略有不同的原因应用这些模式。

5
我可以使用策略模式来抽象行为,同时使代码看起来更加整洁,就像桥接模式一样。或者说,我使用桥接模式使代码更加整洁,同时也能像策略模式一样抽象行为。这样说我是正确的吗? - user20358
1
两者之间的区别仅在于它们的意图。因此,我想我们可以安全地说,因为它们都使用相同的思想并提供相同的灵活性,这两种模式在功能上是相同的。 - Elz
5
“Bridge的UML”在我的GoF书中与现有版本相当不同这个工具可以区分Bridge和Strategy模式。 - Fuhrmanator
1
维基百科通常是一个糟糕的参考来源。正确地,该页面上的错误信息已被删除。https://en.wikipedia.org/w/index.php?title=Strategy_pattern&oldid=516390466 - Fuhrmanator
2
我理解的是,同样的技术被用来抽象实现(策略)或者抽象接口(桥梁)。策略交换行为,桥梁交换接口(这最终允许具有这些接口的实现进行交换)。换句话说,桥梁在一侧创建标准化接口,并在另一侧插入具有不同接口的实现。 - Nikaas
显示剩余4条评论

64

桥接模式是一种结构性模式(如何构建软件组件?)。策略模式是一种动态模式(如何在软件中运行行为?)。

语法相似但目标不同:

  • 策略:您有更多的操作方式;使用策略,您可以在运行时选择算法,并且可以在编译时修改单个策略而不会产生很多副作用;
  • 桥接:您可以拆分接口和类层次结构,将其与抽象引用连接起来(请参见解释)。

3
如果语法相似,那么我是否可以说我正在使用这些模式之一来以特定的方式运行软件行为,而且也是因为我想以那种方式构建组件,使它看起来整洁漂亮? - user20358

21

我也曾经这样想,但最近我不得不使用桥接模式并意识到桥接模式会在上下文中使用策略并添加抽象化,以便您稍后可以进行更多更改而无需更改客户端。如果没有抽象化地使用策略,则设计不够灵活,并且可能需要稍后更改客户端。但是,当使用整个桥连接时,设计变得更加灵活。在此处,您可以了解如何从策略转换到桥接模式会提供更多的灵活性。另外,我们假设现在“visa”和“master”不仅适用于卡片,还适用于手机和芯片;如果使用桥接模式,则更容易添加该支持。

Strategy VS Bridge


12

策略:

  • 与策略相关的上下文:上下文类(可能是抽象的,但不一定是接口!因为您希望封装特定的行为而不是整个实现)将知道/包含策略接口引用和要在其上调用策略行为的实现
  • 意图是在运行时交换行为能力。

class Context {

     IStrategy strategyReference;

     void strategicBehaviour() {

        strategyReference.behave();
     }

}

桥接模式

  • 抽象化不与实现绑定:抽象化接口(或大部分行为都是抽象的抽象类)不会知道/包含实现接口引用。
  • 目的是完全将抽象化与实现解耦。

interface IAbstraction {

    void behaviour1();

    .....

}

interface IImplementation {

     void behave1();

     void behave2();

     .....

}

class ConcreteAbstraction1 implements IAbstraction {

      IImplementation implmentReference;

      ConcreteAbstraction1() {

           implmentReference = new ImplementationA() // Some implementation

      }

      void behaviour1() {

            implmentReference.behave1();

      }

      .............

}

class ConcreteAbstraction2 implements IAbstraction {

      IImplementation implmentReference;

      ConcreteAbstraction1() {

           implmentReference = new ImplementationB() // Some Other implementation

      }

      void behaviour1() {

            implmentReference.behave2();

      }

      .............

}

9

桥接模式: (一种结构型模式)

桥接模式将抽象和实现分离,使得两者可以独立变化。

使用此模式的情况包括:

  1. 在编译时未确定抽象和实现
  2. 抽象和实现应该独立变化
  3. 抽象的实现变化不应影响调用应用程序
  4. 客户端应与实现细节隔离。

策略模式: (一种行为型模式)

策略模式允许您在运行时从算法族中切换多个算法。

使用策略模式的情况包括:

  1. 需要多个算法版本
  2. 类的行为必须在运行时动态更改
  3. 避免条件语句

相关帖子:

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

策略模式的实际示例


4

设计模式类型

  • 行为型:模式描述类或对象之间的交互方式和责任分配
  • 结构型:模式处理类或对象的组合。
  • 创建型:模式关注对象创建的过程。

桥接模式(结构型)

将抽象与其实现分离,使得它们可以独立地变化。 enter image description here

以遥控器为例。遥控器有1-6个按钮,这是上面图中的具体类。每个按钮会根据是用于电视还是DVD而有所不同。通过实现者接口将每个按钮的功能从实现中抽象出来。

这样我们就可以更改每个设备的遥控器工作方式。

策略模式(行为型)

定义一组算法,将每个算法都封装起来,并使它们之间可以互换。 enter image description here

在策略模式中,如果我们看遥控器的情况。"状态"是整个遥控器,我们通过更改上下文的状态引用来交换它。"concreteStateA"(电视遥控器)和"concreteStateB"(DVD遥控器)。

附加阅读:


3
  1. 策略模式 用于行为决策,而 桥接模式 用于结构决策。

  2. 桥接模式 将抽象元素与实现细节分离,而策略模式 则关注使算法更具可互换性。

UML 中的策略模式

UML 中的桥接模式

Swift 中的策略模式:

protocol PrintStrategy {
   func print(_ string: String) -> String
}

class Printer {
   let strategy: PrintStrategy

   init(strategy: PrintStrategy) {
      self.strategy = strategy
    }

  func print(_ string: String) -> String {
     return self.strategy.print(string)
  }
}

class UpperCaseStrategy: PrintStrategy {
    internal func print(_ string: String) -> String {
        return string.uppercased()
    }
}

class LowerCaseStrategy: PrintStrategy {
    internal func print(_ string: String) -> String {
        return string.lowercased()
    }
}

var lower = Printer(strategy: LowerCaseStrategy())
lower.print("I love Software Patterns")

var upper = Printer(strategy: UpperCaseStrategy())
upper.print("I love Software Patterns")

Swift中的桥接模式:

protocol Appliance {
   func run()
}

protocol Switch {
   let appliance: Appliance {get set}
   func turnOn()
}

class RemoteControl: Switch {
   var appliance: Appliance

   init(appliance: Appliance) {
       self.appliance = appliance
   }

   internal func turnOn() {
      appliance.run()
   }
}

class TV: Appliance {
   internal func run() {
      print("TV is ON")
   }
}

class Stereo: Appliance {
   internal func run() {
      print("Stereo is ON")
   }
}

var tvRemote = RemoteControl.init(appliance: TV())
tvRemote.turnOn()

var stereoRemote = RemoteControl.init(appliance: Stereo())
stereoRemote.turnOn()

为什么只有策略模式更“可互换”呢?因为我们编写的是接口而不是实现,所以我们可以在策略或桥接中交换实现,就像您在代码示例中演示的那样,将“立体声”与“电视”进行交换,代码仍然可以正常工作。 - denis631
在桥接模式中,某些东西从左侧的基类继承。但这会带来什么后果?它与策略模式有何不同之处?拥有一个基类和一个派生类的重要性是什么,而不仅仅是左侧的单个类。 - FreelanceConsultant

2

补充willcodejavaforfood的回答,它们在实现上可以相同。然而,你使用策略模式来交换策略,例如排序策略,而你使用桥接模式来桥接两个对象的实现,比如数据库包装器和网络适配器,这样客户端代码就可以使用任何一个针对相同API的工作。因此,命名实际上已经说明了一切。


1
仅作为对模式比较已经提到的内容的补充(意图的差异等):桥接模式也有意地结构化,以允许抽象层次结构的一侧变化。在像C#这样的语言中,这可能意味着您拥有一个包含虚拟方法的抽象基类,作为一种允许预期变化而不会对现有消费者造成问题的方式。除此之外,两种模式在大部分情况下可能看起来相同。

1
策略模式用于在运行时插入算法或策略。正如模式的类别所暗示的那样,它处理对象的行为。另一方面,桥接是一种结构模式,处理对象的结构层次。它通过在抽象与实现之间引入精炼的抽象来解耦抽象与实现。精炼的抽象可以与运行时策略插入(在策略模式中)混淆。桥接模式通过提供机制来避免创建n个类来处理结构方面的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接