译-设计模式-结构模式之Bridge

来源:互联网 发布:office办公软件价格 编辑:程序博客网 时间:2024/05/21 10:01

更多请移步: 我的博客

目的

Bridge是结构模式的一种,它可以帮你分离一个巨大的类或者将一组关系相近的类分离成为两个独立的层次结构,抽象和实现,可以各自独立开发。

问题

抽象?实现?听起来害怕?我们首先来看个简单的例子。

你有一个几何Shap类,他有一对子类:Square和Triangle。你希望扩展这个层次结构来融入颜色以便创建红色和蓝色的形状。但是因为你已经有子类,你需要创建4个类来组合,比如BlueSquare和RedTriangle。

shapeCombination

添加更多的形状类型和颜色将会导致层级结构变的更大。比如,添加一个Circles,你必须创建两个子类,每个对应一种颜色。之后,再添加新的颜色就需要为每个形状添加一个子类。再往后发展,将会变的更糟。

解决

我们每次在扩展类层次到几个独立纬度时都会碰到这个问题。

Bridge模式尝试通过用委托替换继承来解决这个问题。你必须将其中一个维度抽离到不同的层次结构中。原类将通过持有新层次结构中的一个对象的引用的方式来替换在一个类中保留它所有的状态和行为的方式。

shapeSolution

通过这种方式我们抽离出Color和它的两个子类,Red和Blue。Shape类将持有一个颜色类的引用域。当我们需要时通过这个引用把工作委托给color对象。这个引用在Shapps和Color之间像桥一样提供服务。从现在开始,添加color将不再需要改变shape类,反之亦然。

抽象(Abstraction)和实现(Implementation)

在GoF的书中把“抽象和实现”一词作为Bridge模式定义的一部分。在我看来,那样太过学术并且使得这个模式变的更加难以理解。通过上面那个简单的例子,让我们看下GoF的真正意思。

抽象(Abstraction),也叫做接口(Interface),是一些实体的控制层。它们自己并不做任何真正事情,而是把大多工作委托给实现(Implementation)层,有时叫做平台(Platform)。不要把接口和抽象类和你的编程语言混淆,他们不是一回事。

比如,当我们谈到真正的应用是,抽象可以表示用户图形界面(GUI),实现可以表示响应用户交互的GUI层调用系统底层操作的API。

有两个方向来将应用扩展:
- 有一些不同的GUI(用户GUI和管理GUI)
- 支持一些不同的API(可以工作在Windows,Linux和MacOS下)

这个程序的代码看起来像“巨大的意大利面条碗”,有着成吨的连接不同GUI和API行为的操作条件。

compare

可以通过对所有接口-平台的变体进行子类化来改进代码。但实际上,这个将导致我们已经在形状例子中看到的同样的问题。类层次将爆发时增长,每个新GUI或者API类型将需要增加一些组合类。

Bridge模式建议把这些类分成两个层次:
- 抽象层(Abstraction):应用的GUI层。
- 实现层(Implementation):操作系统API。

抽象对象持有一个具体实现对象的引用。只要遵循通用的接口,使同一个GUI能够在Windows和Linux下工作,不同的实现将是可互换的。

exampleApp

更重要的是,你可以在不触碰操作系统代码的情况下开始在GUI类中工作,反之亦然。比如,添加一个对新操作系统的支持,将仅需要在实现层中创建一个子类。

结构

structure

  1. 抽象(Abstraction)主要包含一个像用户接口一样的控制逻辑。抽象代码依赖具体实现对象来完成任务。

  2. 实现(Implementation)为所有具体的实现声明了通用的接口。抽象层可以和任何一个符合这个接口的具体实现工作。
    抽象和实现接口在一些程序中是相等的。但是大多情况下,实现包含基本的原语操作,抽象层用它们来处理一些复杂的行为。

  3. 具体实现包含具体平台的代码。

  4. 精制抽象(Refined Abstractions)可以用来创建一些控制逻辑的变种。这些类和它们的父类一样,应该用实现接口(Implementation inteface)来和不同的实现协作。

  5. 客户端(Client)只和抽象类有一个地方不同。在构建抽象对象时,客户端会传递一个具体实现对象。然而,如果需要,具体实现可以动态替换。

伪代码

在这个例子中,Bridge将设备(Devices)和遥控器(Remotes)的代码分成几部分:

  • 设备(看作实现)
  • 遥控器(看作抽象)

umlExample

遥控器的基类有一个域来持有一个要控制设备的对象引用。遥控器通过设备提供的通用接口工作。它允许一个遥控器可以和几个不同的设备协作(译者注:控制几个不同类型的设备)。

你可以独立的改变控制类。例如,你可以创建一个仅有两个按钮的遥控器或者带有触摸屏的复杂遥控器。

因此,Bridge模式允许你将一个实体分成几个不同的实体,它们可以独立发展。客户端代码总是保持简单。它只需要选择一个抽象并且配置给它一个具体实现。

// All remote classes contain reference to the device they controls. Remote's// methods delegate most of the work to the device methods.class Remote is    protected field device: Device    constructor BasicRemote(device: Device) is        this.device = device    method togglePower() is        if device.isEnabled() then device.disable()        else device.enable()    method volumeDown() is        device.setVolume(device.getVolume() - 10)    method volumeUp() is        device.setVolume(device.getVolume() + 10)    method channelDown() is        device.setChannel(device.getChannel() - 1)    method channelUp() is        device.setChannel(device.getChannel() + 1)// You can extend remote hierarchy independently from device classes.class AdvancedRemote extends BasicRemote is    method mute() is        device.setVolume(0)// All devices have the common interface. This makes them compatible with// all remotes.interface Device is    method isEnabled()    method enable()    method disable()    method getVolume()    method setVolume(percent)    method getChannel()    method setChannel(channel)// But each concrete device may have its own implementation.class Tv implements Device is    // ...class Radio implements Device is    // ...// Somewhere in client code.tv = new Tv();remote = new Remote(tv)remote.pover()radio = new Radio();remote = new AdvancedRemote(radio)

适用性

  • 当你有一个包含一些功能变种的大类(比如,工作在几个不同的数据库服务上)
    这个类会变的很难维护,因为任何一个人触碰她的人都需要花费大量的时间去完全理解它。更改功能的某个变种会导致编辑整个类,这可能会引起讨厌的被忽视的错误。

    Bridge模式将单体类分成几个层次,一个包含另外一个的引用。这些层次中的类可以独立的编辑。它简化了支持,并最大限度地减少了修改现有代码的风险。

  • 当你需要在正交(独立)的维度扩展一个类
    取代单层次的增长,Bridge模式建议为每个维度创建一个分离的类层次,并且通过引用域来关联这些层次。

  • 当你需要在运行时改变实现
    尽管它是可选的,Bridge模式允许改变抽象中的实现对象。这就像为一个字段分配一个新值一样简单。

    顺便说下,这也是为什么许多人对Bridge模式和Strategy模式分不清楚。记住,模式不仅仅是类结构,而是意图(译者注:或者目的)。Bridge模式的目的就是结构化代码。

如何实现

  1. 确定你的类是正交维度。这些独立的概念可以是:abstraction/platform, or domain/infrastructure, or front-end/back-end, or interface/implementation.

  2. 考虑客户端想要干什么,然后把他们描述在基本抽象类中。

  3. 确定所有平台的能力和抽象需要什么。然后把它们描述在实现接口中。

  4. 在你的领域中为所有平台创建具体实现类,确定它们都遵循了实现接口。

  5. 在抽象类中增加一个实现类型的域。然后实现所有抽象方法,同时将大部分工作委托给该域中引用的实现对象。

  6. 客户端代码应将实现对象传递给抽象的构造函数。它可以根据需要使用抽象对象。

优点

  • 允许建立平台独立代码。
  • 符合开闭原则。
  • 对客户端隐藏实现细节。

缺点

  • 创建多个附加类导致总体代码复杂性增加。

和其他模式的关系

  • Bridge是前置设计,让抽象和实现相互独立。Adapter是通过改装使得没有关系的类协作。Adapter通常是在设计完成后使用;Bridge通常是设计时采用。

  • State, Strategy, Bridge (和某种程度上的Adapter) 具有相似的解决结构。它们都是采用分享“句柄/身体”的方式。它们的意图不同,因此它们解决不同的问题。

  • Abstract Factory可与Bridge模式一起使用。当Bridge“接口”的一部分只能与特定的“实现”工作时,这很有用。在这种情况下,工厂可以封装这些关系,对客户端隐藏复杂性。

  • Builder可以构造为Bridge模式:Director将作为接口,Builders将扮演实现角色。

Java中模式的使用

用例:Bridge模式对这些情况特别适用:跨平台应用,支持多类型的数据库服务,和一些特定类型的API提供者(例如,云平台,社交网络等)合作。

坚定:可以通过区分一些控制实体和它所依赖的几个不同的平台来识别Bridge模式。

参考

翻译整理自:https://refactoring.guru/design-patterns/bridge

原创粉丝点击