C#设计模式-桥接模式(Bridge Pattern)

引言

例如我有好几个项目,需要外包出去做各种类型的测试,不同的公司基础费用不同,不同的测试类型价格也是不同的。此时不同的项目选择不同的公司和不同类型的测试进行测试价格都是不同的。于是我们可以创建一个项目抽象类,一个公司抽象类,一个测试类型抽象类,然后实现各自的具体类,不同的项目使用不同的公司和测试类型,进行测试。抽象化与实现化解耦,二者可以独立变化。我们将这种通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦的类型称为桥接模式。

概念

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

结构图

桥接(Bridge)模式中的主要角色:

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

具体实现

以引言中的示例进行实现。一个产品需要外包出去进行测试。

涉及三个角色:产品、外包公司、测试类型

using System;


namespace Bridge
{
    class Program
    {
        static void Main(string[] args)
        {
            Company beijingCompany = new BeijingCompany();
            TestType manualTest = new ManualTest();
            Prodeuct managementProdeuct = new ManagementProdeuct(beijingCompany, manualTest);
            managementProdeuct.Run();

            Company shanghaiCompany = new ShanghaiCompany();
            TestType autoTest = new AutoTest();
            Prodeuct operationProdeuct = new ManagementProdeuct(shanghaiCompany, autoTest);
            operationProdeuct.Run();

            Console.Read();
        }
    }

    /// <summary>
    /// 软件产品,抽象类
    /// 充当抽象化角色
    /// 将抽象部分与实现部分分离,使他们都可以独立的变化
    /// </summary>
    public abstract class Prodeuct
    {
        private readonly Company _company;
        private readonly TestType _testType;
        
        // 使用组合,一个产品外包出去,需要一个公司进行不同测试类型的测试
        public Prodeuct(Company company, TestType testType)
        {
            this._company = company;
            this._testType = testType;
        }

        public string Name { get; set; }

        // 执行实现部分
        public void Run()
        {
            Console.WriteLine($"{this._company.Name}对产品{Name}进行{this._testType.Name},总共花费时间{this._company.BaseTime + this._testType.RunTime}。");
            Console.WriteLine("======================================================");
        }

    }


    /// <summary>
    /// 公司,对实现部分进行抽象
    /// </summary>
    public abstract class Company
    {
        public string Name { get; set; }

        public Double BaseTime { get; set; }

    }

    /// <summary>
    /// 测试类型,对实现部分进行抽象
    /// </summary>
    public abstract class TestType
    {
        public string Name { get; set; }

        public Double RunTime { get; set; }

    }

    /// <summary>
    /// 管理系统,扩展抽象化角色
    /// </summary>
    public class ManagementProdeuct : Prodeuct
    {

        public ManagementProdeuct(Company company, TestType testType) : base(company, testType)
        {
            Name = "管理系统";
        }
    }

    /// <summary>
    /// 运营系统,扩展抽象化角色
    /// </summary>
    public class OperationProdeuct : Prodeuct
    {

        public OperationProdeuct(Company company, TestType testType) : base(company, testType)
        {
            Name = "运营系统";
        }
    }

    /// <summary>
    /// 公司抽象类具体实现,具体实现化角色
    /// </summary>
    public class BeijingCompany : Company
    {
        public BeijingCompany()
        {
            Name = "北京公司";
            BaseTime = 200;
        }
    }

    /// <summary>
    /// 公司抽象类具体实现,具体实现化角色
    /// </summary>
    public class ShanghaiCompany : Company
    {
        public ShanghaiCompany()
        {
            Name = "上海公司";
            BaseTime = 100;
        }
    }

    /// <summary>
    ///测试类型抽象类具体实现,具体实现化角色
    /// </summary>
    public class ManualTest : TestType
    {
        public ManualTest()
        {
            Name = "手工测试";
            RunTime = 30;
        }
    }

    /// <summary>
    ///测试类型抽象类具体实现,具体实现化角色
    /// </summary>
    public class AutoTest : TestType
    {
        public AutoTest()
        {
            Name = "自动测试";
            RunTime = 10;
        }
    }
}

运行后结果:

北京公司对产品管理系统进行手工测试,总共花费时间230。
======================================================
上海公司对产品管理系统进行自动测试,总共花费时间110。
======================================================

适用场景

  • “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
  • 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
  • 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

优缺点

优点

  • 抽象与实现分离,扩展能力强。
  • 符合开闭原则。
  • 符合合成复用原则。
  • 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。

缺点

  • 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

与装饰器模式区别

  • 装饰器模式是为了动态地给一个对象增加功能,而桥接模式时为了让类在多个维度上自由扩展。
  • 装饰器模式的装饰者和被装饰者需要继承自同一父类,而桥接模式通常不需要;
  • 装饰器模式通常可以嵌套使用,而桥接模式不能。