桥接模式又称桥梁模式,属于结构型模式,是指将抽象化 与 实现化 脱耦,使得二者可以独立的变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
存在于多个实体中的共同的概念性联系,就是抽象化,作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当作同样的实体对待。通常情况下,一组对象如果具有相同的概念性联系,那么他们就可以通过一个共同的类来描述,如果一些类具有相同的概念性联系,往往可以通过一个共同的抽象类来描述,在更加复杂的情况下,可以使用一个继承关系的包括抽象类和具体子类的等级结构来描述。
抽象化给出的具体实现,就是实现化。一个类的实例就是这个类的实现化,一个具体子类是它的抽象超类的实现化。在更加复杂的情况下,实现化也可以是与抽象化等级结构相平行的等级结构,同样可以由抽象类和具体类组成。
所谓耦合,就是两个实体的行为的某种强关联,而将他们的强关联去掉,就是脱耦。在这里脱耦是指将抽象化和实现化之间的耦合解脱开,或者是将他们之间的强关联改换成弱关联。
手机已经是我们日常生活不可缺少的一环了,国内的手机有华为,小米,VIVO等几个厂商,每个厂商旗下又有好多款不同配置的手机,接下来就以手机打电话的功能作为例子讲解。
传统方式实现打电话功能:
传统模式如果要再加一个手机样式(旋转式),就需要对各个品牌添加call()的功能,这样增加了代码的维护成本。
对于传统模式出现的问题,使用桥接模式就可以很好的解决。
桥接模式的UML类图如下:
从UML类图可以看出,这个系统含有两个等级结构:
桥接模式所涉及到抽象化角色,扩展抽象化角色,实现化角色,具体实现化角色等几种角色:
例子的UML类图:
抽象化角色:
package com.charon.bridge; /** * @className: Brand * @description: 抽象化产品角色 * @author: charon * @create: 2022-03-18 22:51 */ public interface Brand { void open(); void close(); void call(); }
扩展抽象化角色:
package com.charon.bridge; /** * @className: Vivo * @description: * @author: charon * @create: 2022-03-18 22:55 */ public class Vivo implements Brand { @Override public void open() { System.out.println("vivo手机开机了。。。。"); } @Override public void close() { System.out.println("vivo手机关机了。。。。"); } @Override public void call() { System.out.println("vivo手机打电话。。。。"); } } package com.charon.bridge; /** * @className: XiaoMi * @description: * @author: charon * @create: 2022-03-18 22:56 */ public class XiaoMi implements Brand{ @Override public void open() { System.out.println("小米手机开机了。。。。"); } @Override public void close() { System.out.println("小米手机关机了。。。。"); } @Override public void call() { System.out.println("小米手机打电话。。。。"); } }
实现化角色:
package com.charon.bridge; /** * @className: Phone * @description: 实现化角色 * @author: charon * @create: 2022-03-18 22:52 */ public abstract class Phone { private Brand brand; public Phone(Brand brand) { this.brand = brand; } protected void open(){ this.brand.open(); } protected void call(){ this.brand.call(); } protected void close(){ this.brand.close(); } }
具体实现化角色:
package com.charon.bridge; /** * @className: FoldedPhone * @description: * @author: charon * @create: 2022-03-18 22:57 */ public class FoldedPhone extends Phone{ public FoldedPhone(Brand brand) { super(brand); } @Override protected void open(){ super.open(); System.out.println("折叠样式手机开机。。。。"); } @Override protected void call(){ super.call(); System.out.println("折叠样式手机打电话。。。。"); } @Override protected void close(){ super.close(); System.out.println("折叠样式手机关机。。。。"); } } package com.charon.bridge; /** * @className: UpRightPhone * @description: * @author: charon * @create: 2022-03-18 22:59 */ public class UpRightPhone extends Phone{ public UpRightPhone(Brand brand) { super(brand); } @Override protected void open(){ super.open(); System.out.println("直立样式手机开机。。。。"); } @Override protected void call(){ super.call(); System.out.println("直立样式手机打电话。。。。"); } @Override protected void close(){ super.close(); System.out.println("直立样式手机关机。。。。"); } }
测试:
package com.charon.bridge; /** * @className: Client * @description: * @author: charon * @create: 2022-03-18 22:51 */ public class Client { public static void main(String[] args) { // 获取折叠样式手机(品牌+样式) FoldedPhone xiaomiFoldedPhone = new FoldedPhone(new XiaoMi()); xiaomiFoldedPhone.open(); xiaomiFoldedPhone.call(); xiaomiFoldedPhone.close(); UpRightPhone xiaomiUpRightPhone = new UpRightPhone(new XiaoMi()); xiaomiUpRightPhone.open(); xiaomiUpRightPhone.call(); xiaomiUpRightPhone.close(); } }
如此这般,不管是新增一个产品还是新增一个样式,代码的改动都非常小。
桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则。
桥接模式的优点:
桥接模式的缺点:
当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
桥接模式通常适用于以下场景。
桥接模式的一个常见使用场景就是替换继承。我们知道,继承拥有很多优点,比如,抽象、封装、多态等,父类封装共性,子类实现特性。继承可以很好的实现代码复用(封装)的功能,但这也是继承的一大缺点。
因为父类拥有的方法,子类也会继承得到,无论子类需不需要,这说明继承具备强侵入性(父类代码侵入子类),同时会导致子类臃肿。因此,在设计模式中,有一个原则为优先使用组合/聚合,而不是继承。