GOF23设计模式之桥接模式(bridge)
一、桥接模式概述
桥接模式核心要点:
处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
二、桥接模式场景提出与存在问题
商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?
这个场景中有两个变化的维度:品牌、电脑类型。
(1)不使用桥接模式时:
1 /** 2 * 不适用桥接模式时的电脑产品按照类型和品牌分类 3 * @author CL 4 * 5 */ 6 public interface Computer { 7 void sale(); 8 } 9 10 /** 11 * 类型:台式机 12 * @author CL 13 * 14 */ 15 class Desktop implements Computer { 16 17 @Override 18 public void sale() { 19 System.out.println("销售台式机"); 20 } 21 22 } 23 24 /** 25 * 类型:笔记本 26 * @author CL 27 * 28 */ 29 class Laptop implements Computer { 30 31 @Override 32 public void sale() { 33 System.out.println("销售笔记本"); 34 } 35 36 } 37 38 /** 39 * 类型:平板 40 * @author CL 41 * 42 */ 43 class Pad implements Computer { 44 45 @Override 46 public void sale() { 47 System.out.println("销售平板电脑"); 48 } 49 50 } 51 52 /** 53 * 品牌:联想台式机 54 * @author CL 55 * 56 */ 57 class LenovoDesktop extends Desktop { 58 @Override 59 public void sale() { 60 System.out.println("销售联想台式机"); 61 } 62 } 63 64 /** 65 * 品牌:联想笔记本 66 * @author CL 67 * 68 */ 69 class LenovoLaptop extends Desktop { 70 @Override 71 public void sale() { 72 System.out.println("销售联想笔记本"); 73 } 74 } 75 76 /** 77 * 品牌:联想平板电脑 78 * @author CL 79 * 80 */ 81 class LenovoPad extends Desktop { 82 @Override 83 public void sale() { 84 System.out.println("销售联想平板电脑"); 85 } 86 } 87 88 /** 89 * 品牌:戴尔台式机 90 * @author CL 91 * 92 */ 93 class DellDesktop extends Desktop { 94 @Override 95 public void sale() { 96 System.out.println("销售戴尔台式机"); 97 } 98 } 99 100 /** 101 * 品牌:戴尔笔记本 102 * @author CL 103 * 104 */ 105 class DellLaptop extends Desktop { 106 @Override 107 public void sale() { 108 System.out.println("销售戴尔笔记本"); 109 } 110 } 111 112 /** 113 * 品牌:戴尔平板电脑 114 * @author CL 115 * 116 */ 117 class DellPad extends Desktop { 118 @Override 119 public void sale() { 120 System.out.println("销售戴尔平板电脑"); 121 } 122 } 123 124 /** 125 * 品牌:华硕台式机 126 * @author CL 127 * 128 */ 129 class ASUSDesktop extends Desktop { 130 @Override 131 public void sale() { 132 System.out.println("销售华硕台式机"); 133 } 134 } 135 136 /** 137 * 品牌:华硕笔记本 138 * @author CL 139 * 140 */ 141 class ASUSLaptop extends Desktop { 142 @Override 143 public void sale() { 144 System.out.println("销售华硕笔记本"); 145 } 146 } 147 148 /** 149 * 品牌:华硕平板电脑 150 * @author CL 151 * 152 */ 153 class ASUSPad extends Desktop { 154 @Override 155 public void sale() { 156 System.out.println("销售华硕平板电脑"); 157 } 158 }
测试:
1 /** 2 * 客户端 3 * @author CL 4 * 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 Computer c1 = new LenovoDesktop(); 10 c1.sale(); 11 } 12 13 }
控制台输出:
销售联想台式机
(2)存在问题
① 扩展性问题(类的个数膨胀问题)
如果要增加一个新的电脑类型,如智能手机,则要增加各个品牌下面的类;
如果要增加一个新的品牌,也要增加各种电脑类型的类。
② 违反了单一职责原则
一个类有两个职责:品牌+电脑类型。每个类都有两个原因引起变化。
三、桥接模式应用
(1)电脑类型维度
1 /** 2 * 维度:电脑类型 3 * @author CL 4 * 5 */ 6 public class Computer { 7 protected Brand brand; 8 9 public Computer(Brand brand) { 10 this.brand = brand; 11 } 12 13 public void sale() { 14 brand.sale(); 15 } 16 } 17 18 /** 19 * 台式机 20 * @author CL 21 * 22 */ 23 class Desktop extends Computer { 24 25 public Desktop(Brand brand) { 26 super(brand); 27 } 28 29 @Override 30 public void sale() { 31 super.sale(); 32 System.out.println("销售台式机"); 33 } 34 } 35 36 /** 37 * 笔记本 38 * @author CL 39 * 40 */ 41 class Laptop extends Computer { 42 43 public Laptop(Brand brand) { 44 super(brand); 45 } 46 47 @Override 48 public void sale() { 49 super.sale(); 50 System.out.println("销售笔记本"); 51 } 52 } 53 54 /** 55 * 平板电脑 56 * @author CL 57 * 58 */ 59 class Pad extends Computer { 60 61 public Pad(Brand brand) { 62 super(brand); 63 } 64 65 @Override 66 public void sale() { 67 super.sale(); 68 System.out.println("销售平板电脑"); 69 } 70 }
(2)品牌维度
1 /** 2 * 一种维度:品牌 3 * @author CL 4 * 5 */ 6 public interface Brand { 7 void sale(); 8 } 9 10 /** 11 * 联想电脑 12 * @author CL 13 * 14 */ 15 class Lenovo implements Brand { 16 17 @Override 18 public void sale() { 19 System.out.println("销售联想电脑"); 20 } 21 22 } 23 24 /** 25 * 戴尔电脑 26 * @author CL 27 * 28 */ 29 class Dell implements Brand { 30 31 @Override 32 public void sale() { 33 System.out.println("销售戴尔电脑"); 34 } 35 36 } 37 38 /** 39 * 华硕电脑 40 * @author CL 41 * 42 */ 43 class ASUS implements Brand { 44 45 @Override 46 public void sale() { 47 System.out.println("销售华硕电脑"); 48 } 49 50 }
(3)测试
1 /** 2 * 客户端 3 * @author CL 4 * 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 //销售联想笔记本电脑 10 Computer c1 = new Laptop(new Lenovo()); 11 c1.sale(); 12 13 //销售戴尔台式机 14 Computer c2 = new Desktop(new Dell()); 15 c2.sale(); 16 } 17 18 }
控制台输出:
销售联想电脑
销售笔记本
销售戴尔电脑
销售台式机
(4)假如现在要在品牌维度上增加一种品牌:宏碁电脑,只需要在品牌维度中增加如下代码:
1 /** 2 * 宏碁电脑 3 * @author CL 4 * 5 */ 6 class Acer implements Brand { 7 8 @Override 9 public void sale() { 10 System.out.println("销售宏碁电脑"); 11 } 12 13 }
(5)测试
1 /** 2 * 客户端 3 * @author CL 4 * 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 //销售联想笔记本电脑 10 Computer c1 = new Laptop(new Lenovo()); 11 c1.sale(); 12 13 //销售戴尔台式机 14 Computer c2 = new Desktop(new Dell()); 15 c2.sale(); 16 17 //销售宏碁的平板电脑 18 Computer c3 = new Pad(new Acer()); 19 c3.sale(); 20 } 21 22 }
控制台输出:
销售联想电脑
销售笔记本
销售戴尔电脑
销售台式机
销售宏碁电脑
销售平板电脑
四、桥接模式总结
(1)桥接模式可以取代多层继承的方案。
多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
(2)桥接模式极大的提高了系统的可扩展性。
在两个变化的维度中任意扩展一个维度。都不需要修改原有的代码,符合开闭原则。
五、桥接模式实际开发中的应用场景
(1)JDBC驱动程序;
(2)AWT中的Peer框架;
(3)人力资源系统中的奖金计算模块:
奖金分类:个人奖金、团队奖金、激励奖金
部门分类:开发部门、运维部门、人事部门
(4)OA系统中的消息处理:
业务类型:普通消息、加密消息、紧急消息
发送方式:系统内部、手机短信、邮件、飞秋
(5)…………