什么是桥接模式?一文详解
目录一.意图二.结构三.举例产品上色1.不用桥接模式的场景2.使用桥接模式的场景3.代码实现①创建“抽象产品”本质是一个抽象类②创建“抽象颜色”本质是一个接口③创建两个具体的产品比如产品A、产品B④创建三个具体的颜色比如红、蓝、绿最终进行测试4.总结四.软考真题1.2009年上半年题目分析过程正确答案2.2013年下半年题目分析过程正确答案3.2017年下半年题目分析过程正确答案一.意图将抽象部分与实现部分分离使它们都可以独立地变化。二.结构下图只是一个大致的、通用的一个示意结构图看起来懵是正常的因此我们下面会举具体的例子。三.举例产品上色我们就拿产品比如产品需要根据具体情况上色来举例。说白了就是给产品上色时受两种因素影响产品的类型即产品A、产品B、产品C之间它们由于物理结构不同上色工艺就不同产品的颜色即红色的产品A就上红色蓝色的产品A就上蓝色1.不用桥接模式的场景如下图可见此时类的数量就会爆炸即需要创建很多的类。一共需要创建类的数量 1 m m*n1就是“抽象产品”第一层m就是产品A、产品B.....这些第二层m*n就是带颜色的产品的数量即有m种产品每种产品又有n种颜色思考为什么会类的数量爆炸答案因为颜色是在产品类中的应该单独提取成“抽象颜色”这个模块然后再与“抽象产品”进行一个关联即桥接模式这个关联就相当于一个桥。2.使用桥接模式的场景如下图此时类的数量就会大大减少一共需要创建类的数量 1 m 1 n1就是“抽象产品”m就是产品A、产品B....这些1就是“抽象颜色”n就是红、蓝、绿.....这些注意菱形表示“抽象颜色”属于“抽象产品”的一部分说白了就是“抽象产品”这个抽象类中要定义一个属性即“抽象颜色”。注意“抽象颜色”这个抽象用抽象类、接口来实现都可以因为这俩都是抽象的东西具体采用哪个就需要具体情况具体分析。3.代码实现①创建“抽象产品”本质是一个抽象类//抽象产品 abstract class Product { //属性 private String type;//产品类型比如产品A、产品B、产品C protected Color color;//颜色比如红、蓝、绿 //方法 public abstract void Operation();//上色方法 //get、set public String getType() { return type; } public void setType(String type) { this.type type; } public Color getColor() { return color; } public void setColor(Color color) { this.color color; } }②创建“抽象颜色”本质是一个接口//抽象颜色 public interface Color { //根据“产品的类型”上色具体上什么颜色由Color接口的具体实现类决定 public void OperationImp(String productType); }③创建两个具体的产品比如产品A、产品Bpublic class ProductA extends Product{ Override //实现父类Product定义的抽象方法在Product中抽象方法没有方法体所以在这里给出 public void Operation() { //调用上色方法 color.OperationImp(this.getType());//color继承自父类的属性color this代表ProductA的对象本身 } }public class ProductB extends Product{ Override //实现父类Product定义的抽象方法在Product中抽象方法没有方法体所以在这里给出 public void Operation() { //调用上色方法 color.OperationImp(this.getType());//color继承自父类的属性color this代表ProductB的对象本身 } }④创建三个具体的颜色比如红、蓝、绿public class Red implements Color{ Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println(正在给productType涂上红色); } }public class Blue implements Color{ Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println(正在给productType涂上蓝色); } }public class Green implements Color{ Override //实现接口Color中定义的上色方法 public void OperationImp(String productType) { System.out.println(正在给productType涂上绿色); } }最终进行测试测试1先来个红色的产品Apublic class Demo01Application { public static void main(String[] args) { //目标此时我想要一个红色的产品A //1、创建一个具体的产品比如产品A即类型为A的产品 ProductA productA new ProductA(); //2、初始化产品的两个属性产品类型、颜色这是影响产品上色的两个因素 productA.setType(产品A); Color color new Red();//这就是泛型说白了就是左边是通用的接口右边是具体的实现类 productA.setColor(color); //3、执行上色方法 productA.Operation(); } }运行效果如下测试2再来一个蓝色的产品Bpublic class Demo01Application { public static void main(String[] args) { //目标此时我想要一个蓝色的产品B //1、创建一个具体的产品比如产品B即类型为B的产品 ProductB productB new ProductB(); //2、初始化产品的两个属性产品类型、颜色这是影响产品上色的两个因素 productB.setType(产品B); Color color new Blue(); productB.setColor(color);//这就是泛型说白了就是左边是通用的接口右边是具体的实现类 //3、执行上色方法 productB.Operation(); } }运行效果如下4.总结颜色本身就是属于产品的一个属性但是由于我们产品有很多类型每个类型又有很多颜色。当我们的上色工艺是由产品类型 颜色这两个因素决定的此时我们就需要创建很多类因为红色的产品A、红色的产品B、蓝色的产品C、绿色的产品D等等它们的上色工艺都不同所以只要产品类型 or 颜色 有一个不同就需要定义一个专门的上色方法因此此时就需要创建很多个类因为是相乘关系即产品类型数量 * 颜色数量此时为了解决这个问题我们可以将颜色单独抽出为“抽象颜色”color是一个接口接口中定义一个上色方法参数为“产品类型”然后将这个color接口当做“抽象产品”的一个属性。此时我们可以定义很多颜色类去实现这个color接口每个颜色类根据接口的产品类型再结合上自己的颜色最终写出来独特的上色工艺由产品类型颜色决定这样一看核心思路就是将颜色单独抽象为“抽象颜色”color接口然后又把这个color接口当做“抽象产品”的一个属性。这就相当于一个桥梁也是桥接模式的灵魂可见十分灵活。而且要体会泛型的思想代码如下左面的Color是接口右边是Color接口的具体实现类。说白了就是父 子 这种就是泛型的思想。也只有这样我们才能把这个color塞到“抽象产品”中当做属性。你要是不用泛型而是Blue blue new Blue(); 将blue塞到“抽象产品”中当做属性会报错因为“抽象产品”类中属性定义的是Color接口。四.软考真题1.2009年上半年题目分析过程(1)this.imp但注意要用this关键字this表示当前类的本身区分开imp是属性还是形参。因此第一空答案为this.imp(2)ImageImp故第二空答案为ImageImp(3)imp.doPaint(m)此时我们可见显示像素矩阵m的方法在抽象类ImageImp中此时BMP对象如何获取别忘了我们BMP继承了Image类Image类中就有即属性imp。因此此时这个BMP类可以直接使用父类对象imp去调用显示像素矩阵的方法。因此第三空的答案为imp.doPaint(m)(4)new BMP()根据泛型的思想再结合桥接模式图可轻松得到答案因为人家说要查看bmp类型的图像文件所以此时要创建BMP对象故第四空答案为new BMP()(5)new WinImp()思考过程同上人家要在Windows操作系统上查看因此要创建WinImp类的对象(6)image1.setImp(imageImp1)由于Image类需要ImageImp当做属性因此很清晰就能得到这一空的答案。(7)17Image占一个ImageImp占一个BMP、GIF等等共有10个题目给的WinImp、LinuxImp共有5个综上一共11105 17个正确答案可见我们的上述答案和下面的正确答案一样。2.2013年下半年题目分析过程(1)interface我们往后看发现都有的类实现了Drawing了那Drawing肯定就是接口了。故第一空的答案为interface(2)(3)具体定义了哪两个方法可以去看一下它的实现类如下因此可见第二空的答案为public void drawLine(double x1, double y1,double x2, double y2)第三空的答案为public void drawCircle(double x, double y, double r)(4)(5)然后再结合这个规则我们还需要去找一下具体的方法在哪里第四空的答案为DP1.draw_a_circle(x, y, r)第五空的答案为DP2.drawcircle(x, y, r)(6)并且这个draw方法被子类又实现了一遍并且Shape本身就是抽象类所以draw方法肯定是抽象的需要用abstract修饰且需要子类能访问故还要用public修饰。通过子类能发现draw方法的返回值是void因此第六空的答案为abstract public void draw()正确答案经过对比发现我们上述的答案和正确答案一致。下面的答案没写public关键字最好带上3.2017年下半年题目分析过程(1)abstract void doPaint(Matrix m)由于Implementor是抽象类、且该方法没有方法体故这个方法肯定是abstract修饰的。再根据该抽象类的两个子类可得该方法的方法名为doPaint且返回值为void形参为Matrix m因此第一空的答案为abstract void doPaint(Matrix m)(2)imp.doPaint(m)由于GIFImage继承了Image类所以就有imp这个属性了是Implementor抽象类的对象因此就可以调用其中的doPaint方法来显示像素矩阵了。故第二空的答案为imp.doPaint(m)(3)(4)(5)由于人家要看gif类型的图像所以我们要创建GIFImage类的对象。而且人家要在linux操作系统上查看因此我们必须创建LinuxImp类的对象。最后再将imageImp放入image中充当属性。故第三空的答案new GIFImage()第四空的答案new LinuxImp()第五空的答案image.setImp(imageImp)正确答案将上述我们的答案和下面的正确答案对比发现全对。