苏飞论坛

 找回密码
 马上注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

分布式系统框架(V2.0) 轻松承载百亿数据,千万流量!讨论专区 - 源码下载 - 官方教程

HttpHelper万能框架(V2.3-含.netcore) HttpHelper官方出品,无敌框架讨论区 - 源码下载 - 在线测试和代码生成

HttpHelper爬虫类(V2.0) 最牛的爬虫类,没有爬不到只有想不到 源码 - 代码生成器 - 讨论区 - 教程- 例子

查看: 555|回复: 5

[学习心得] C#桥梁模式介绍

[复制链接]
发表于 2019-1-9 14:47:30 | 显示全部楼层 |阅读模式
一 、  桥梁 (Bridge ) 模式
      桥梁模式是一个非常有用的模式,也是比较复杂的一个模式。熟悉这个模式对于理解面向对象的设计原则,包括
"开-闭"原则(OCP)以及组合/聚合复用原则(CARP)都很有帮助。理解好这两个原则,有助于形成正确的设计思
想和培养良好的设计风格。

桥梁模式的用意

【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化
(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。

抽象化

存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的
实体当做同样的实体对待【LISKOV94】。

实现化

抽象化给出的具体实现,就是实现化。

脱耦

      所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,
脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。
将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓
脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对
独立地变化。这就是桥梁模式的用意。

二 、  桥梁模式的结构

桥梁模式【GOF95】是对象的结构模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

下图所示就是一个实现了桥梁模式的示意性系统的结构图。

1.png

可以看出,这个系统含有两个等级结构,也就是:

 由抽象化角色和修正抽象化角色组成的抽象化等级结构。

 由实现化角色和两个具体实现化角色所组成的实现化等级结构。


桥梁模式所涉及的角色有:

 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。

 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。

 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义
相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。

 具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。

三 、  桥梁模式的示意性源代码

[C#] 纯文本查看 复制代码
using System;
// "Abstraction"
class Abstraction
{
    // Fields
    protected Implementor implementor;
    // Properties
    public Implementor Implementor
    {
        set { implementor = value; }
    }
    // Methods
    public virtual void Operation()
    {
        implementor.Operation();
    }
}
// "Implementor"
abstract class Implementor
{
    // Methods
    public abstract void Operation();
}
// "RefinedAbstraction"
class RefinedAbstraction : Abstraction
{
    // Methods
    public override void Operation()
    {
        implementor.Operation();
    }
}
// "ConcreteImplementorA"
class ConcreteImplementorA : Implementor
{
    // Methods
    public override void Operation()
    {
        Console.WriteLine("ConcreteImplementorA Operation");
    }
}
// "ConcreteImplementorB"
class ConcreteImplementorB : Implementor
{
    // Methods
    public override void Operation()
    {
        Console.WriteLine("ConcreteImplementorB Operation");
    }
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
    public static void Main(string[] args)
    {
        Abstraction abstraction = new RefinedAbstraction();
        // Set implementation and call
        abstraction.Implementor = new ConcreteImplementorA();
        abstraction.Operation();
        // Change implemention and call
        abstraction.Implementor = new ConcreteImplementorB();
        abstraction.Operation();
    }
}


四 、  调制解调器问题

      感觉《敏捷软件开发-原则、模式与实践》中关于 Bridge 模式的例子很好。(《Java 与模式》一书 33 章的对
变化的封装一节也写得很不错,推荐大家读一读。它深入的阐述了《Design Patterns Explained》一书中"1)
Design to interfaces. 2)Favor composition over inheritance. 3)Find what varies and encapsulate it"
的三个观点。)。

2.png

      如图所示,有大量的调制解调器客户程序在使用 Modem 接口。Modem 接口被几个派生类 HayesModem、
USRoboticsModem 和 EarniesModem 实现。它很好地遵循了 OCP、LSP 和 DIP。当增加新种类的调制解调
器时,调制解调器的客户程序不会受影响。

      假定这种情形持续了几年,并有许多调制解调器的客户程序都在使用着 Modem 接口。现出现了一种不拨号的
调制解调器,被称为专用调制解调器。它们位于一条专用连接的两端。有几个新应用程序使用这些专用调制解调
器,它们无需拨号。我们称这些使用者为 DedUser。但是,客户希望当前所有的调制解调器客户程序都可以使
用这些专用调制解调器。他们不希望去更改许许多多的调制解调器客户应用程序,所以完全可以让这些调制解调
器客户程序去拨一些假(dummy)电话号码。

如果能选择的话,我们会把系统的设计更改为下图所示的那样。


3.png

      我们把拨号和通信功能分离为两个不同的接口。原来的调制解调器实现这两个接口,而调制解调器客户程序使用
这两个接口。DedUser 只使用 Modem 接口,而 DedicateModem 只实现 Modem 接口。但这样做会要求我
们更改所有的调制解调器客户程序--这是客户不允许的。

一个可能的解决方案是让 DedicatedModem 从 Modem 派生并且把 dial 方法和 hangup 方法实现为空,就像
下面这样:


4.png

      几个月后,已经有了大量的 DedUser,此时客户提出了一个新的更改。为了能拨国际电话号码、信用卡电话、
PIN 标识电话等等,必修对现有 dial 中使用 char[10]存储号码改为能够拨打任意长度的电话号码。

      显然,所有的调制解调器客户程序都必须更改。客户同意了对调制解调器客户程序的更改,因为他们别无选择。
糟糕的是,现在必须要去告诉 DedUser 的编写者,他们必须要更改他们的代码!你可以想象他们听到这个会有
多高兴。本来他们是不用调用 dial 的。

      这就是许多项目都会具有的那种有害的混乱依赖关系。系统某一部分中的一个杂凑体(kludge)创建了一个有害
的依赖关系,最终导致系统中完全无关的部分出现问题。

如果使用 ADAPTER 模式解决最初的问题的话,就可以避免这个严重问题。如图:

5.png
请注意,杂凑体仍然存在。适配器仍然要模拟连接状态。然而,所有的依赖关系都是从适配器发起的。杂凑体和
系统隔离,藏身于几乎无人知晓的适配器中。

BRIDGE  模式

看待这个问题,还有另外一个方式。现在,出现了另外一种切分 Modem 层次结构的方式。如下图:


6.png

      这不是一个理想的结构。每当增加一款新硬件时,就必须创建两个新类--一个针对专用的情况,一个针对拨号的
情况。每当增加一种新连接类型时,就必须创建 3 个新类,分别对应 3 款不同的硬件。如果这两个自由度根本
就是不稳定的,那么不用多久,就会出现大量的派生类。

      在类型层次结构具有多个自由度的情况中,BRIDGE 模式通常是有用的。我们可以把这些层次结构分开并通过
桥把它们结合到一起,而不是把它们合并起来。如图:

7.png

      我们把调制解调器类层次结构分成两个层次结构。一个表示连接方法,另一个表示硬件。

      这个结构虽然复杂,但是很有趣。它的创建不会影响到调制解调器的使用者,并且还完全分离了连接策略和硬件
实现。ModemConnectController 的每个派生类代表了一个新的连接策略。在这个策略的实现中可以使用
sendlmp、receivelmp、diallmp 和 hanglmp。新 imp 方法的增加不会影响到使用者。可以使用 ISP 来给连
接控制类增加新的接口。这种做法可以创建出一条迁移路径,调制解调器的客户程序可以沿着这条路径慢慢地得
到一个比 dial 和 hangup 层次更高的 API。






发表于 2019-1-9 14:59:16 | 显示全部楼层
发表于 2019-1-9 15:52:43 | 显示全部楼层
发表于 2019-1-9 16:12:12 | 显示全部楼层
发表于 2019-1-9 16:24:31 | 显示全部楼层
发表于 2019-1-9 17:00:15 | 显示全部楼层
您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

QQ|手机版|小黑屋|手机版|联系我们|关于我们|广告合作|苏飞论坛 ( 豫ICP备17001017号-1)

GMT+8, 2019-12-7 23:04

© 2017-2018

快速回复 返回顶部 返回列表