了解桥梁设计模式


24

我完全不了解“桥梁”设计模式。我浏览了各种网站,但没有帮助。

有人可以帮助我理解这一点吗?


2
我也不明白。期待看到答案:)
紫罗兰色长颈鹿

有很多网站和书籍介绍了设计模式。我认为重复已写的内容没有任何价值,也许您可​​以提出一个具体的问题。我也花了一些时间来理解它,不断在不同的来源和示例之间进行切换。
海伦娜

Answers:


16

在OOP中,我们使用多态性,因此抽象可以具有多种实现。让我们看下面的例子:

//trains abstraction
public interface Train
{ 
    move();
}
public class MonoRail:Train
{
    public override move()
    {
        //use one track;
    }
}
public class Rail:Train
{
    public override move()
    {
        //use two tracks;
    }
}

引入了新的要求,并且需要引入列车的加速角度,因此请更改以下代码。

    public interface Train
    { 
        void move();
    }
    public class MonoRail:Train
    {
        public override void move()
        {
            //use one track;
        }
    }
    public class ElectricMonoRail:MonoRail
    {
        public override void move()
        {
            //use electric engine on one track.
        }
    }
    public class DieselMonoRail: MonoRail
    {
        public override void move()
        {
            //use diesel engine on one track.
        }
    }
    public class Rail:Train
    {
        public override void move()
        {
            //use two tracks;
        }
    }
    public class ElectricRail:Rail
    {
        public override void move()
        {
            //use electric engine on two tracks.
        }
    }
    public class DieselRail: Rail
    {
        public override void move()
        {
            //use diesel engine on two tracks.
        }
    }

上面的代码不可维护,并且缺乏可重用性(假设我们可以在同一轨道平台上重用加速机制)。以下代码应用了桥梁模式,并将火车运输加速度这两个不同的抽象分开。

public interface Train
{ 
    void move(Accelerable engine);
}
public interface Accelerable
{
    public void accelerate();
}
public class MonoRail:Train
{
    public override void move(Accelerable engine)
    {
        //use one track;
        engine.accelerate(); //engine is pluggable (runtime dynamic)
    }
}
public class Rail:Train
{
    public override void move(Accelerable engine)
    {
        //use two tracks;
        engine.accelerate(); //engine is pluggable (runtime dynamic)
    }
}
public class ElectricEngine:Accelerable{/*implementation code for accelerable*/}
public class DieselEngine:Accelerable{/*implementation code for accelerable*/}

3
很好的例子。我要加两分钱:这是一个很好的例子,它更喜欢构图而不是继承
zzfima 2013年

1
对于它的价值,我认为应该是Monorail因为它不是真正的两个单词,而是一个(复合)单词。MonoRail将是Rail的某个子类,而不是其他类型的Rail(它是)。就像我们不会使用SunShine或者CupCake,他们会SunshineCupcake
ErikE

这行“两种不同的抽象,火车的运输和加速”将有所帮助,指出什么是两个层次结构,我们试图解决的是使它们独立变化。
wangdq

11

尽管大多数设计模式都有有用的名称,但我发现名称“ Bridge”在功能上并不直观。

从概念上讲,您将类层次结构使用的实现详细信息推入另一个对象(通常具有自己的层次结构)。这样,您将消除对这些实现细节的严格依赖,并允许更改该实现的细节。

在小范围内,我将其比喻为使用策略模式来插入新行为。但是,实现对象通常具有更多功能,而不仅仅是像在策略中常见的那样包装算法。当您将该概念应用于整个类层次结构时,较大的模式将成为桥梁。(再次,讨厌这个名字)。

这不是您每天都会使用的一种模式,但是我发现它在管理潜在的类爆炸时很有用,当您(明显)需要多重继承时,这些类可能发生。

这是一个真实的示例:

我有一个RAD工具,可让您在设计图面上放置和配置控件,因此我有一个这样的对象模型:

Widget // base class with design surface plumbing
+ Top
+ Left
+ Width
+ Height
+ Name
+ SendToBack
+ BringToFront
+ OnPropertyEdit
+ OnSelect
+ Validate
+ ShowEditor
+ Paint
+ Etc

TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor // override base to show a property editor form specific to a Textbox
+ Paint // override to render a textbox onto the surface    
+ Etc

ListWidget : Widget // list specific
+ Items
+ SelectedItem
+ ShowEditor // override base to show a property editor form specific to a List
+ Paint // override to render a list onto the surface
+ Etc

依此类推,也许有十几个控件。

但是随后添加了新要求以支持多个主题(look-n-feel)。比方说,我们有以下主题: Win32WinCEWinPPCWinMo50WinMo65。对于与渲染相关的操作,每个主题将具有不同的值或实现,例如DefaultFont,DefaultBackColor,BorderWidth,DrawFrame,DrawScrollThumb等。

我可以创建一个这样的对象模型:

Win32TextboxWidget : TextboxWidget

Win32ListWidget : ListWidget

等,用于一种控制类型

WinCETextboxWidget : TextboxWidget

WinCEListWidget : ListWidget

等等,针对彼此的控件类型(再次)

您了解了这一点–您得到了小部件数量乘以主题数量的类爆炸式增长。使RAD设计人员意识到每个主题都会使它复杂化。另外,添加新主题会强制修改RAD设计器。此外,在主题内有很多通用实现,可以很好地继承,但是控件已经从通用基(Widget)继承了。

因此,我要做的是创建一个单独的对象层次结构来实现主题。每个小部件都将包含对实现渲染操作的对象的引用。在许多课本中,此类都带有一个后缀,Impl但我偏离了该命名约定。

所以现在我TextboxWidget看起来像这样:

TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor
+ Painter // reference to the implementation of the widget rendering operations
+ Etc

而且我可以让我的各种画家继承我特定主题的基础,这是我以前做不到的:

Win32WidgetPainter
+ DefaultFont
+ DefaultFontSize
+ DefaultColors
+ DrawFrame
+ Etc

Win32TextboxPainter : Win32WidgetPainter

Win32ListPainter : Win32WidgetPainter

一件好事是,我可以在运行时动态加载实现,从而可以在不更改核心软件的情况下添加任意数量的主题。换句话说,我的“实现可以独立于抽象而变化”。


我不知道这怎么可能是桥梁模式?当您将新组件添加到Widget层次结构时,您将被迫将该新Widget添加到所有Painters(Win32NewWidgetPainter,PPCNewWidgetPainter)。这不是两个独立增长的层次结构。对于适当的桥接模式,您不必为每个小部件都继承PlatformWidgetPainter类的子类,而是让它收到一个小部件“绘制描述符”。
Mazyod

感谢您的反馈,您说得对。这是几年前,我贴这个,和审查,现在我会说,它描述了桥以及直到在那里我得到的最后一位Win32TextboxPainter,并Win32ListPainterWin32WidgetPainter。您可以对实施方的继承树,但它应该是更通用的(或许StaticStyleControlPainterEditStyleControlPainterButtonStyleControlPainter),并根据需要重写任何需要的基本操作。这更接近于我基于示例的真实代码。
tcarvin

3

该桥打算将抽象与其具体实现分离开来,以便两者可以独立变化:

  • 通过子类完善抽象
  • 也可以通过子类提供不同的实现,而不必知道抽象及其改进。
  • 如果需要,请在运行时选择最合适的实现

桥通过使用composition实现此目的:

  • 抽象引用(引用或指针)到实现对象
  • 抽象及其改进只知道实现接口

在此处输入图片说明

关于频繁混乱的补充说明

这种模式与适配器模式非常相似:抽象实现提供了不同的接口,并使用合成来实现。但:

这些模式之间的主要区别在于它们的意图-Gamma
等人,设计模式,可重用的OO软件的组成部分,1995年。

在这本关于设计模式的极好的开创性著作中,作者还注意到:

  • 发现不兼容且无法预料的耦合时,通常使用适配器
  • 从设计的开始就使用桥,这是因为预计类会独立发展。
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.