乐趣区

关于设计模式:11-设计模式桥接模式

连续上一篇装璜器模式的话题,咱们持续对需要进行降级。

示例

需要

还是以奶茶店为例,然而咱们不再仅仅思考奶茶的成分了,要想奶茶卖的好,还得须要一个嘹亮的品牌,奶茶有很多品牌,如一点点,COCO,喜茶等,除此之外,咱们还要对奶茶的规格进行辨别,如大杯、中杯、小杯等,不同品牌价格不同,不同规格价格也不同(不思考太简单的状况,就假如每种品牌和规格都有一个价格基数,总价间接累加)。

高级计划

乍一看,跟上一篇装璜器模式中的需要没什么区别,不就是把配料换成了品牌和规格吗?咱们还是先看看继承的计划:

再看看局部实现代码:

public abstract class Drink
{public string Name { get; set;}

    public int Price {get; set;}

    public abstract string Desc {get;}

    public abstract int Cost {get;}
}

public class Naicha : Drink
{public Naicha()
    {
        Name = "奶茶";
        Price = 8;
    }
    public override string Desc => this.Name;
    public override int Cost => this.Price;
}

public class CoCoNaicha:Naicha
{public CoCoNaicha()
    {Name += "[CoCo]";
        Price += 2;
    }
}

public class DaCoCoNaicha : CoCoNaicha
{public DaCoCoNaicha()
    {
        Name += "+ 大杯";
        Price += 3;
    }
}

问题

高级计划和遇到的问题也跟装璜器模式简直是截然不同的:

  • 类爆炸;
  • 大量代码反复;
  • 如果减少品牌,或者调整规格价格,代码保护艰难,重大违反开闭准则。

思考

到了这里,咱们无妨思考一下如下两个问题:

  1. 思考是否应用装璜器模式实现?
  2. 这里的品牌、规格跟装璜器模式中用到的配料有什么区别?

很显著,通过装璜器模式必定是能够达成指标的,毕竟他们看起来差不多,然而这样好不好呢?这就须要答复第二问题,品牌、规格跟配料有区别吗?

  • 首先,一杯奶茶能够同时加多种配料,然而不能同时属于多种品牌或者多种规格,例如能够有红豆布丁奶茶,却不能够有一点点 COCO 奶茶;
  • 其次,奶茶配料能够加多份,例如多冰,多糖,双份布丁等,然而奶茶只能属于一种品牌、一种规格;
  • 最初,一杯奶茶能够不加配料,然而肯定会属于某一个品牌或者规格。

改良

发现了吗?区别还是很大的。因为有了这些区别,实现形式天然也应该是有所不同的。其实,这里没有那么简单,不须要一步就跳到装璜者模式,咱们仍旧用最老套的改良形式 — 继承转组合 — 就能够了。
改良后的类图如下所示:

再看看局部实现代码,这里间接将品牌和规格聚合到了饮品基类中,其中,品牌和规格面向形象编程,我想就不必细说了:

public abstract class Drink
{
    private readonly BrandBase _brand;

    private readonly SkuBase _sku;
    public Drink(BrandBase brand, SkuBase sku)
    {
        this._brand = brand;
        this._sku = sku;
    }

    public string Name {get; set;}

    public int Price {get; set;}


    public string Desc
    {
        get
        {return this.Name + this._brand.BrandName + this._sku.SkuType;}
    }

    public int Cost
    {
        get
        {return this.Price + this._brand.Price + this._sku.Price;}
    }
}

public class Naicha : Drink
{public Naicha(BrandBase brand, SkuBase sku):base(brand,sku)
    {
        Name = "奶茶";
        Price = 8;
    }
}

品牌和规格的局部代码如下:

public abstract class SkuBase
{public abstract string SkuType { get;}

    public abstract int Price {get;}
}

public class Dabei : SkuBase
{
    public override string SkuType => "大杯";

    public override int Price => 3;
}

public abstract class BrandBase
{public abstract string BrandName { get;}

    public abstract int Price {get;}
}

public class CoCo : BrandBase
{public override string BrandName => "[CoCo]";

    public override int Price => 2;
}

比设想中的要简略,一步就到位了。咱们再来看看,如果要扩大减少瑞辛咖啡呢?

public class Kafei : Drink
{public Kafei(BrandBase brand, SkuBase sku) : base(brand, sku)
    {
        Name = "咖啡";
        Price = 12;
    }
}

public class Ruixin : BrandBase
{public override string BrandName => "[瑞辛]";

    public override int Price => 2;
}

没错,就是这么简略,间接在饮品和品牌两个维度上减少咖啡和瑞辛就能够了,原有的代码不必做任何批改,一步革新间接就满足了开闭准则。没错,这就是桥接模式。

简化 UML

将上述案例中的类图简化并形象就能够失去桥接模式的 UML 类图了:

  • Abstraction:抽象化角色,并保留一个对实现化对象的援用。
  • RefinedAbstraction:修改抽象化角色,扭转和修改父类对抽象化的定义。
  • Implementor:实现化角色,这个角色给出实现化角色的接口,但不给出具体的实现。
  • ConcreteImplementor:具体实现化角色,这个角色给出实现化角色接口的具体实现。

定义

桥接模式是将形象局部与它的实现局部拆散,使它们都能够独立地变动。

有的人可能会说,既然是桥接模式,那么桥在哪里呢?其实,Abstraction就是桥,从奶茶店的例子来看,Drink就是桥,桥接的是 DrinkBrandBaseSkuBase 三个维度,你没看错,这里的 Drink 起到了两个作用,一个作用是饮品基类,另一个作用就是桥。其实,桥的目标就是为了将多个维度分割起来,因而,也能够单纯的通过多继承实现,或者单纯的通过组合实现。然而,高级语言个别都不反对多继承,并且,咱们也晓得,继承并不是一个好的设计形式,因而不抉择多继承;另外,如果纯正的通过组合实现,就须要额定定义一个无意义的桥类,这个类同时将 DrinkBrandBaseSkuBase 组合进来,尽管可行,但这显著是不够优雅的,因而,桥接模式仍然采纳的是继承 + 组合的模式。

优缺点

长处

  • 拆散形象局部与它的实现局部
  • 绝对于继承有更少的子类,应用更灵便,可在 多个维度 上自在扩大

毛病

  • 减少零碎的了解与设计难度;
  • 独立变动的维度的辨认比拟艰难;
  • 客户端应用老本较高,须要对各个维度进行结构。

跟装璜器模式的区别

  • 装璜器模式是为了动静地给一个对象减少性能,而桥接模式是为了让类在多个维度上自在扩大;
  • 装璜器模式的装璜者和被装璜者须要继承自同一父类,而桥接模式通常不须要;
  • 装璜器模式通常能够嵌套应用,而桥接模式不能。

总结

有看过前一篇装璜器模式相干介绍的敌人可能会有所纳闷,咱们这里同样改变了 Drink 基类啊,后面不是说不应该批改原有的类吗?其实起因很简略,因为目标不一样了,装璜器模式是为了动静附加职能,而桥接模式是为了能够在 多个维度 上自在扩大。说到底,桥接模式实用在设计阶段,也就是在设计 Drink 类的时候,目标是为了把 Drink 设计的更好用,而不是动静的在原有的 Drink 类上额定减少内容,正因如此,在设计之初,维度的辨认也就显得至关重要了。

源码链接
更多内容,欢送关注公众号:

退出移动版