每日一个设计模式之【桥接模式】
☁️前言🎉🎉🎉
大家好✋,我是知识汲取者😄,今天给大家带来一篇有关桥接模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
桥接模式是一个很神器的模式,它能够大幅度降低系统类的数量,系统越复杂、效果越明显,它基于类的最小设计原则,通过使用封装、聚合、继承等行为让不同的类承担不同的职责,现在就让我们一起学习吧(●’◡’●)
推荐阅读:
🌻桥接模式概述
-
什么是桥接模式?
桥接模式(Bridge Pattern)又称为柄体(Handle and Body)模式或接口(Interface)模式,它是一种结构型模式,将实现与抽象放在两个不同的层次中,使两个层次可以独立改变
-
桥接模式的作用:将抽象部分与它的实现部分分离开来,使他们都可以独立变化
-
桥接模式的优缺点:
-
优点:
- 实现了抽象层和实现层的解耦。解耦能够提高系统的维护性、扩展性、符合开闭原则
- 提高系统的透明度。桥接模式是面对抽象层开发的,用户只需要关注接口,不需要关注接口的具体实现
- 降低了系统的复杂度。使用桥接模式可以取代多层继承方案,极大地减少了子类的个数
……
-
缺点:
- 桥接模式要求正确识别出系统中两个独立变化的维度,较为困难
……
-
-
桥接模式的适用场景:
- 需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系
- 不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展
……
生活中的应用:笔和笔芯,一只一样的笔只要搭配不同的笔芯就能得到不一样的笔;开关,它将抽象和实现进行了分离,用户只需要关注开关这个抽象的东西,就能实现某种功能;
Java中的应用:在JDBC的驱动管理就使用了桥接模式,不同的数据库驱动名称放到Class.forName()中就能获取到对应的数据库连接
-
桥接模式的角色划分:
- 抽象化角色(Abstraction):定义了类的行为,内部还保存了一个实现化角色的引用,是桥接模式的核心,一般是一个抽象类
- 扩展抽象化角色(RefinedAbstraction):是抽象化角色的子类,用于确定类的行为(行为的具体实现细节需要通过调用实现化角色的引用),是与客户进行交互的类
- 实现化角色(Implementor):定义类的行为,但不实现
- 具体实现化角色(ConcreteImplementor):是实现化角色的具体实现,包含了行为实现的细节
🌱桥接模式的实现
示例:
问题描述:一家小卖部它卖雪碧、可口可乐、橙汁、柠檬汁四款饮料,每种饮料都由大、中、小三中型号,请使用Java语言模拟实现饮料的售卖。
思考:如果不使用桥接模式,则需要创建
4*3
个类,类数量的增长级别是n*m
;如果使用桥接模式,只需要4+3
个类,类数量的增长级别是n+m
。显然这里十分适合使用桥接模式,我们从两个维度对客户买饮料这个行为进行分析,买什么种类的饮料?买什么型号的饮料?从而创建一个抽象化角色和实现化角色,由面对对象的思维:对象的行为由对象自己触发(例如:关门这个方法在门类中,通过门对象调用),所以买饮料的这个行为的具体实现可以放在Drink类中(确定了实现化角色),那么抽象化角色就应该是Glass类,分析完毕🤗……
-
Step1:创建实现化角色
Drink:
package com.hhxy.drink; /** * @author ghp * @date 2022/10/8 * @title * @description */ public interface Drink { /** * 喝饮料的具体实现方法(确定了喝那种种饮料?喝什么型号的饮料) * @param type */ void drink(String type); }
-
Step2:创建具体实现化角色
1)CocaCola:
package com.hhxy.drink.imp; import com.hhxy.drink.Drink; /** * @author ghp * @date 2022/10/5 * @title 可口可乐 * @description */ public class CocaCola implements Drink { @Override public void drink(String type) { System.out.println("我想喝"+type+"可口可乐"); } }
2)LemonJuice:
具体请参考Github或Gitee仓库,略…
3)OrangeJuice:
具体请参考Github或Gitee仓库,略……
4)Sprite:
具体请参考Github或Gitee仓库,略……
-
Step3:创建抽象化角色
package com.hhxy.glass; import com.hhxy.drink.Drink; /** * @author ghp * @date 2022/10/8 * @title * @description */ public abstract class Glass { //使用protected让子类能够访问,但是不让外部其它包访问,进一步确保封装性 protected Drink drink; //将子类的共同属性抽象出来,减少重复定义 protected String type; public Glass(Drink drink){ this.drink = drink; } /** * 喝饮料的方法 */ public abstract void drink(); }
-
Step4:创建扩展抽象化角色
1)SmallGlass:
package com.hhxy.glass.ext; import com.hhxy.drink.Drink; import com.hhxy.glass.Glass; /** * @author ghp * @date 2022/10/5 * @title 小杯 * @description */ public class SmallGlass extends Glass { public SmallGlass(Drink drink) { super(drink); } /** * 喝饮料的方法 */ @Override public void drink() { type = "小杯"; drink.drink(type); } }
2)MediumGlass:
具体请参考Github或Gitee仓库,略……
3)BigGlass:
具体请参考Github或Gitee仓库,略……
-
Step5:编写配置文件
glass-config.xml:
<?xml version="1.0" encoding="UTF-8" ?> <config> <!--用于反射获取Drink对象--> <drinkType>com.hhxy.drink.imp.CocaCola</drinkType> <drinkType>com.hhxy.drink.imp.LemonJuice</drinkType> <drinkType>com.hhxy.drink.imp.OrangeJuice</drinkType> <drinkType>com.hhxy.drink.imp.Sprite</drinkType> <drinkType>test</drinkType> <!--用于反射获取Glass对象--> <glassType>com.hhxy.glass.ext.SmallGlass</glassType> <glassType>com.hhxy.glass.ext.MediumGlass</glassType> <glassType>com.hhxy.glass.ext.BigGlass</glassType> <glassType>test</glassType> </config>
-
Step6:编写配置文件读取类
ReadGlassConfig:
package com.hhxy.read; import com.hhxy.drink.Drink; import com.hhxy.glass.Glass; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.lang.reflect.Constructor; /** * @author ghp * @date 2022/10/5 * @title 读取杯子的配置文件 * @description */ public class ReadGlassConfig { public static Glass getGlass(){ try{ //1、将配置文件加载到内存中,获取DOM对象 //1.1 获取DOM解析器工厂对象DocumentBuilderFactory,用于创建DOM解析器 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //1.2 获取DOM解析器DocumentBuilder DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //1.3 加载配置文件 // Document document = documentBuilder.parse(new FileInputStream("day06_Bridge/src/glass-config.xml")); //让代码和模块名进行解耦,比上面那种方法更优 Document document = documentBuilder.parse(ReadGlassConfig.class.getResourceAsStream("/glass-config.xml")); //2、获取配置文件中的数据 //2.1 从DOM中获取指定的结点的结点列表 NodeList nodeListDrink = document.getElementsByTagName("drinkType"); NodeList nodeListGlass = document.getElementsByTagName("glassType"); //2.2 获取指定位置的结点 Node classNodeDrink = nodeListDrink.item(0).getFirstChild(); Node classNodeGlass = nodeListGlass.item(0).getFirstChild(); //2.3 获取指定结点中的数据(排除空格) String drinkType = classNodeDrink.getNodeValue().trim(); String glassType = classNodeGlass.getNodeValue().trim(); //3、使用反射获取获取Glass对象 //3.1 获取类对象 Class clsDrink = Class.forName(drinkType); Class clsGlass = Class.forName(glassType); //3.2 获取该类对象的构造器对象 Constructor constructorDrink = clsDrink.getDeclaredConstructor(); Constructor constructorGlass = clsGlass.getDeclaredConstructor(Drink.class); //3.3 暴力反射,防止构造器私有化导致无法创建对象 constructorDrink.setAccessible(true); constructorGlass.setAccessible(true); //3.4 获取Drink、Glass对象 Drink drink = (Drink) constructorDrink.newInstance(); Glass glass = (Glass) constructorGlass.newInstance(drink); //4、返回通过配置文件获取的Glass对象 return glass; } catch (Exception e) { //如果异常就打印异常信息,同时返回一个空 e.printStackTrace(); throw new RuntimeException("未找到该Glass类或Drink类,请检查配置文件或者添加一个Glass类或Drink类!"); } } }
-
Step7:测试
package com.hhxy.test; import com.hhxy.glass.Glass; import com.hhxy.read.ReadGlassConfig; /** * @author ghp * @date 2022/10/5 * @title 测试类 * @description 用于测试桥接模式 */ public class Test { public static void main(String[] args) { /* 方式一:通过new获取Drink对象喝Glass对象 Drink drink = new CocaCola(); Glass glass = new BigGlass(drink); glass.drink(); */ //方式二:通过读取配置文件获取Drink对象喝Glass对象,实现了解耦,同时也很方便测试 Glass glass = ReadGlassConfig.getGlass(); glass.drink(); } }
测试结果:
🌲总结
又到了我最喜欢的总结时间了(●ˇ∀ˇ●),总的来讲:
- 桥接模式是一种结构型模式,它将抽象和实现进行解耦,能很大程度降低系统中类的数量,提高系统的灵活性、可维护性、可扩展性
- 桥接模式由于需要从类的两个维度,在实际应用中是有一定难度的
- 其次就是要注意桥接模式的主要应用场景,当类中存在大量继承关系式,可以选择使用桥接模式;或者让系统的抽象与实现充分解耦,降低系统的透明度,也可以选择桥接模式
自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O
上一篇:每日一个设计模式之【适配器模式】
下一篇:每日一个设计模式之【组合模式】
参考文章:
在次致谢