策略模式与桥接模式

40

我知道之前有人问过这个问题(例如,桥接模式和策略模式有什么区别?)。

然而,能否有人用清晰明了的例子来解释一下它们之间的区别,并在什么情况下必须选择其中一个而不是另一个?更少的概念理论,更多实际的“现实生活”场景将不胜感激。

9个回答

47
桥接模式将抽象和实现区分开来,使两者可以独立变化。您需要提供访问物理传感器的类,例如在秤、速度测量设备等中找到的传感器。每个传感器都会产生一个数字,但该数字可能意味着不同的东西。对于秤而言,它可能表示重量,而对于速度测量设备而言,它可能表示速度。
因此,您可以首先创建一个Sensor抽象类,以表示所有传感器之间的共性,并为不同类型的传感器创建各种子类。这是一个强大的设计,允许您在未来提供更多类型的传感器。
现在假设传感器由不同制造商提供。您将不得不为制造商X创建传感器类层次结构,为制造商Y创建另一个传感器类层次结构。现在的问题是客户端需要知道制造商之间的差异。如果您决定支持第三个制造商怎么办?
解决方案是提供主要的抽象类层次结构,即Sensor抽象类和子类,例如SpeedSensor和WeightSensor等。然后提供接口(Bridge),它将存在于抽象和实现之间。因此,将有一个SensorInterface、WeightSensorInterface和SpeedSensorInterface,它规定了每个具体传感器类必须提供的接口。抽象不知道实现,而是知道接口。最后,您可以为每个制造商创建一个具体的实现。也就是说,XSensor、XWeightSensor和XSpeedSensor,YSensor、YSpeedSensor和YWeightSensor。
客户端仅依赖于抽象,但是可以插入任何实现。因此,在这种设置中,可以更改抽象而不更改任何具体类,并且可以更改实现而不必担心抽象。
正如您所看到的,这描述了一种组织类的方式。
另一方面,策略模式关注的是在运行时更改对象的行为。我喜欢使用一个游戏的例子,其中一个角色拥有几种不同类型的武器。角色可以攻击,但攻击的行为取决于角色当时所持有的武器,并且在编译时无法知道这一点。
因此,使武器行为可插拔并根据需要注入角色。因此,这是一种行为模式。
这两种模式解决了不同的问题。策略模式关注于使算法可互换,而桥接模式关注于将抽象与实现分离,以便您可以为相同的抽象提供多个实现。也就是说,桥接模式关注整个结构。
以下是一些可能有用的链接:
  1. 桥接模式
  2. 策略模式

37

我知道这很难解释。许多使用并理解它的人都很难向新手解释。

对于像我这样以类比思考的人:

策略模式

因此,策略是一种一维概念。想象一下一维策略数组供选择。

示例1:管工工具

策略模式就像一个拥有多种工具来清理管道的管工。每次的任务都是相同的,即清理管道。但在不同情况下,他选择用来完成任务的工具可能会不同。也许他会尝试一种方法,如果不起作用就尝试另一种方法。

在这个类比中,“清理管道”是将实现其中一种策略的方法。蛇刷,电动钻头和Draino是具体的策略,而管工则是包含该方法的类(在大多数图表中标记为“上下文”)。

进入图像描述

示例2:带多个位的螺丝刀

或者,您可以将其看作是多位螺丝刀上的可互换位。

它们旨在在运行时更改,以适应手头的工作,即拧紧某物。

进入图像描述

桥接模式

因此,桥接是一个二维概念。想象一下一个维度(行)是需要实现的方法列表,而第二个维度(列)是将实现每个方法的实现者。

示例1:应用程序和设备

桥接模式就像一个人可以通过许多方式进行通信(电子邮件,短信,Google语音,电话,Skype),并且有许多设备可以使用这些不同的方式进行通信-个人电脑,平板电脑和智能手机。

各种通信方式(电子邮件,短信,电话)将成为抽象接口上的方法,让我们称之为“CommunicationDevice”。在这个模式中,CommunicationDevice是实现者。此类比喻中的每个设备(PC,平板电脑,智能手机)都是具体实现者,实现所有这些方法(电子邮件,短信,电话)。

enter image description here

例2:ODBC数据库驱动程序和ODBC函数

Windows中的另一个可用桥接模式的示例是ODBC或OLEDB数据库驱动程序模块。它们都在同一标准“数据库驱动程序”接口上实现了各种方法,但它们以不同的方式实现该接口。即使您使用相同的数据库,比如SQL Server,仍然有不同的驱动程序可以与SQL Server通信,虽然在底层采用了不同的方式。

例3:实现者(列)实现方法(行)

Implementors (columns) implementing methods (rows)


1
我非常喜欢这个答案,但是我不理解示例2的图表...它与ODBC甚至数据库都没有关系。 - Fuhrmanator

4

策略模式

该模式让执行算法与使用它的客户端独立变化。即,不是为给定情况下执行的固定算法,而是允许在运行时从众多算法中选择一个进行执行。这涉及将算法从其宿主类中移除并放入单独的类中。

例如,假设某人想要从一个城市到另一个城市,那么他有几种选择:乘坐公共汽车、租赁汽车、乘坐火车等。因此,选择的每种交通方式都会转化为一个单独的算法来执行。所选的交通方式将取决于运行时决定的各种因素(成本、时间等)。换句话说,选择用于执行的策略将在运行时决定。

另一个例子,假设要实现一个SortedList类(主控制器),该类基于策略进行排序。策略是用于排序的方法(如MergeSort、QuickSort)。

与桥接模式的比较

主要区别(尽管两种模式具有相同的UML)是,与桥接模式(它是一种结构模式)不同,策略模式是一种行为模式。结构模式提供了对象组合、关联或继承形成更大对象的方式,即它们关注对象组合。而行为模式处理算法或业务逻辑(而不是对象创建本身),即它们关注对象之间的协作。

请注意,大多数算法可以实现为静态或单例类,仅需要创建单个实例(即,在设置策略时不会每次调用new)。

仔细查看两种模式的实现将揭示,在桥接模式中,首先创建对象的具体实现,然后进行调用。

// Set implementation and call
// i.e. Returns (creates) the concrete implementation of the object, 
// subsequently operation is called on the concrete implementation
ab.Implementor = new ConcreteImplementorA(); 
ab.Operation();

在策略模式中,不会直接使用算法的具体实现,而是创建上下文来执行策略。

// Set the context with a strategy
// i.e. Sets the concrete strategy into the context, concrete implementation of the class not 
// directly available as a data object (only the algorithm is available).    
context = new Context (new ConcreteStrategyA());     
context.contextInterface();

// Strategy can be reused instead of creating a new instance every time it is used.
// Sort example
MergeSort mergeSort = new MergeSort();
QuickSort quickSort = new QuickSort();
...
context = new Context (mergeSort);
context.Sort();
...
context = new Context (quickSort);
context.Sort();
...
context = new Context (mergeSort);
context.Sort();

我还是很困惑。我的理解是,Bridge只实例化了一个可变算法的具体实现,这是唯一的区别吗? - S. Valmont
@S. Valmont,桥接模式创建的对象“返回”(已创建)是调用“Operation()”的对象,而在策略模式的情况下,正在创建的对象并不重要,而算法(或行为)很重要。因此,在访问“operation”(“Sort()”)时不会暴露具体的“Strategy”实现。 - Devendra D. Chavan
正如@vpit3833所指出的那样,“桥接模式并不旨在将任何东西导出到项目/应用程序之外。而导出函数/方法/API似乎是策略模式的主要目标。” - Devendra D. Chavan

3
桥接模式告诉我们如何组织类,策略模式则告诉我们如何组织算法。

2

桥接模式和策略模式的区别:

桥接模式可以让我们根据当前情况重新实现运行业务结构,而策略模式则可以让我们实现各种业务策略并将其封装起来,根据情况或同时使用它们。

两者之间的主要区别在于使用桥接模式,我们可以改变整个结构,但是使用策略模式,我们能够并行地改变我们的业务策略。

我根据自己的理解详细阐述了下面两个非常重要的设计模式。 请仔细阅读,我认为这将清楚地解释两者的区别。


桥接模式:

什么是桥接设计模式?

GoF建议的桥接模式的意义是将组件的实现与其抽象分离。

何时使用桥接设计模式?

想象一种情况,一个组件已经实现,并且按照您的业务需求正常运行。突然组织改变了他们的业务策略。为此,您需要更改或重新实现组件。在这种情况下,你会怎么做?改变之前几年运行良好的组件,还是创建新组件?在这种情况下,桥接模式能够很好地处理场景。为了更好地理解,请参见下面的示例。

// Main component
public interface Ibridge
{
    void function1();
}

// Already Implemented that are currently being using
public class Bridge1 : Ibridge 
{
    public void function1()
    {
        Console.WriteLine("Implemented function from bridge 1");
    }
}

//New Implementation as per Organisation needs
public class Bridge2 : Ibridge 
{
    public void function1()
    {
        Console.WriteLine("Implemented function from bridge2");
    }
}

//Abstract Calling functionalities
public interface IAbstractBridge  
{
    void CallFunc1();
}

// implementation of calling implemented component at a time
public class AbstractBridge:IAbstractBridge  
{
    protected Ibridge _ibridge;

    public Ibridge Ibridge
    {
        set { _ibridge = value; }
    }
    
    public void CallFunc1()
    {
        this._ibridge.function1();
    }
}

class Program
{
    static void Main(string[] args)
    {
        AbstractBridge abp = new AbstractBridge();
        /*  
            here you see that now being using the previous implemented component.
            but need change newly implemented component so here we need just changed
            the implementation of component, please see below
        */
        //Commented old one
        abp.Ibridge = new Bridge1(); 
        
        //using new one just change the "Bridge1" to "Bridge2"
        abp.Ibridge = new Bridge2(); 
        
        abp.CallFunc1();
    }
}

策略设计模式:

什么是策略设计模式?

GoF提出的策略模式是指:定义一系列算法,将每个算法封装起来,并使它们可以互换。策略让算法独立于使用它的客户端而变化。

在何时使用策略设计模式?

假设一个购物中心的所有者想要根据不同的场合为顾客提供不同的折扣优惠,并且随时可以从折扣模式切换到正常模式或相反,那么如何处理这种情况呢?在这种情况下,策略模式可以很好地处理这种情况。请看下面的示例以更好地理解。

  • 所有策略
public interface ISellingStrategy
{
    void selling();
}
      
public class BasicStrategy : ISellingStrategy
{
    public void selling()
    {
        Console.WriteLine("Buy Three get 5% discount.");
    }
}

public class ChrismasStrategy : ISellingStrategy
{
    public void selling()
    {
        Console.WriteLine("Buy Three get one offer + extra 5% discount.");
    }
}

public class HoliFestiveStrategy : ISellingStrategy
{
    public void selling()
    {
        Console.WriteLine("Buy two get one offer + extra 5% discount.");
    }
}

public class DurgapuljaStrategy : ISellingStrategy
{
    public void selling()
    {
        Console.WriteLine("Buy one get one offer + extra 5% discount.");
    }
}
  • 计费:
public class Billing
{
    private ISellingStrategy strategy;
    public void SetStrategy(ISellingStrategy _strategy)
    {
        this.strategy = _strategy;
    }

    public void ApplyStrategy()
    {
        strategy.selling();
        Console.WriteLine("Please wait offer is being applying...");
        Console.WriteLine("Offer is now Applied and ready for billing..");

    }

}
  • 创建计费类对象的工厂模式
 public class BillingFactory
 {
     public static Billing CreateBillingObject()
     {
         return new Billing();
     }
 }
  • 调用
class Program
{
    static void Main(string[] args)
    {
        Billing billing = BillingFactory.CreateBillingObject();
        billing.SetStrategy(new BasicStrategy());
        billing.ApplyStrategy();

        Console.ReadLine();
    }
}

3
当回答“桥接模式和策略模式的区别是什么?”这个问题时,一般会以某种方式提到策略模式。请注意阅读格式指南 - David Buck

1
两种模式都将接口与实现分离。我认为关键的区别在于桥接模式使用继承("是一个"),而策略模式使用组合("有一个")。
桥接模式:
class Sorter abstract
{ 
   virtual void Sort() = 0;
}

// MergeSorter IS A Sorter
class MergeSorter : public Sorter
{
   virtual void Sort() override;
}

策略模式:
class SortStrategy abstract
{
   virtual void Sort() = 0;
}

// Sorter HAS A SortStrategy
class Sorter
{ 
   Sorter(SortStragety *strategy) : mStrat(strategy) {}

   void Sort() {mStrat->Sort();}

   SortStrategy *mStrat;
}

1

0

让我引述一下链接问题的答案。

桥接模式是一种结构模式,它提出了如何构建项目组件的思路。它用于隐藏两个抽象级别。维基百科上的示例代码(http://en.wikipedia.org/wiki/Bridge_pattern)以最明确的方式解释了它。

策略模式是一种动态模式。当任何野函数都可以实现要求时,就使用策略模式。例如,任何允许开发和安装插件的程序。在维基百科页面(http://en.wikipedia.org/wiki/Strategy_pattern)上,ConcreteStrategyAdd、ConcreteStrategySubtract等是在ConcreteStrategy类中使用的插件。任何实现Strategy接口的方法都可以在那里使用。


1
我认为维基百科的例子最令人困惑。它似乎只是一个调用不同策略的变化容器类。为什么这需要一个与“策略”不同的名称的单独模式,以及为什么需要这样的模式,仍然让我感到困惑。 - S. Valmont
桥接模式不旨在将任何东西导出到项目/应用程序之外。而策略模式的主要目的似乎是导出函数/方法/API。因此,前者是一种结构模式,后者是一种动态模式。这是我目前的理解。 - vpit3833

0

策略模式:

  1. 策略模式 是一种行为设计模式,用于在算法族之间切换。

  2. 该模式包含一个抽象的策略 接口 和许多该接口的具体策略实现(算法)。

  3. 应用程序仅使用策略 接口。根据某些配置参数,将 具体策略 标记到 接口 上。

桥接模式:

  1. 它允许抽象和实现独立变化
  2. 它使用组合而非继承。
  3. 桥接是一种结构型模式

然而,请有人使用明确的示例解释两者之间的区别,并在何种情况下必须选择其中之一?

请参考以下文章以了解策略模式和桥接模式的用例:

策略模式的真实世界例子

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

简单说明:

  1. 使用策略模式通过将一种策略替换为另一种策略来动态更改实现。

    一个真实的例子是:航空公司在淡季提供折扣。在高峰期间,只需使用无折扣策略替换票价折扣策略即可。

  2. 抽象和实现未在编译时确定且可以独立变化时使用桥接模式

汽车行业中的一个真实世界例子:不同类型的齿轮可以组装成不同类型的汽车。汽车和齿轮的规格和实现都可以独立变化。


3
我不明白这如何回答原帖的问题。 - SmileBot
这两个嵌入的帖子包含了真实世界的例子,以及当前帖子中引用的差异。 - Ravindra babu

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