连续上一篇装璜器模式的话题,咱们持续对需要进行降级。
示例
需要
还是以奶茶店为例,然而咱们不再仅仅思考奶茶的成分了,要想奶茶卖的好,还得须要一个嘹亮的品牌,奶茶有很多品牌,如一点点,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;
}
}
问题
高级计划和遇到的问题也跟装璜器模式简直是截然不同的:
- 类爆炸;
- 大量代码反复;
- 如果减少品牌,或者调整规格价格,代码保护艰难,重大违反开闭准则。
思考
到了这里,咱们无妨思考一下如下两个问题:
- 思考是否应用装璜器模式实现?
- 这里的品牌、规格跟装璜器模式中用到的配料有什么区别?
很显著,通过装璜器模式必定是能够达成指标的,毕竟他们看起来差不多,然而这样好不好呢?这就须要答复第二问题,品牌、规格跟配料有区别吗?
- 首先,一杯奶茶能够同时加多种配料,然而不能同时属于多种品牌或者多种规格,例如能够有红豆布丁奶茶,却不能够有一点点 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
就是桥,桥接的是 Drink
,BrandBase
,SkuBase
三个维度,你没看错,这里的 Drink
起到了两个作用,一个作用是饮品基类,另一个作用就是桥。其实,桥的目标就是为了将多个维度分割起来,因而,也能够单纯的通过多继承实现,或者单纯的通过组合实现。然而,高级语言个别都不反对多继承,并且,咱们也晓得,继承并不是一个好的设计形式,因而不抉择多继承;另外,如果纯正的通过组合实现,就须要额定定义一个无意义的桥类,这个类同时将 Drink
,BrandBase
,SkuBase
组合进来,尽管可行,但这显著是不够优雅的,因而,桥接模式仍然采纳的是继承 + 组合的模式。
优缺点
长处
- 拆散形象局部与它的实现局部
- 绝对于继承有更少的子类,应用更灵便,可在 多个维度 上自在扩大
毛病
- 减少零碎的了解与设计难度;
- 独立变动的维度的辨认比拟艰难;
- 客户端应用老本较高,须要对各个维度进行结构。
跟装璜器模式的区别
- 装璜器模式是为了动静地给一个对象减少性能,而桥接模式是为了让类在多个维度上自在扩大;
- 装璜器模式的装璜者和被装璜者须要继承自同一父类,而桥接模式通常不须要;
- 装璜器模式通常能够嵌套应用,而桥接模式不能。
总结
有看过前一篇装璜器模式相干介绍的敌人可能会有所纳闷,咱们这里同样改变了 Drink
基类啊,后面不是说不应该批改原有的类吗?其实起因很简略,因为目标不一样了,装璜器模式是为了动静附加职能,而桥接模式是为了能够在 多个维度 上自在扩大。说到底,桥接模式实用在设计阶段,也就是在设计 Drink
类的时候,目标是为了把 Drink
设计的更好用,而不是动静的在原有的 Drink
类上额定减少内容,正因如此,在设计之初,维度的辨认也就显得至关重要了。
源码链接
更多内容,欢送关注公众号: