大家好✋,我是知识汲取者😄,今天给大家带来一篇有关桥接模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
桥接模式是一个很神器的模式,它能够大幅度降低系统类的数量,系统越复杂、效果越明显,它基于类的最小设计原则,通过使用封装、聚合、继承等行为让不同的类承担不同的职责,现在就让我们一起学习吧(●’◡’●)
推荐阅读:
什么是桥接模式?
桥接模式(Bridge Pattern)又称为柄体(Handle and Body)模式或接口(Interface)模式,它是一种结构型模式,将实现与抽象放在两个不同的层次中,使两个层次可以独立改变
桥接模式的作用:将抽象部分与它的实现部分分离开来,使他们都可以独立变化
桥接模式的优缺点:
优点:
……
缺点:
……
桥接模式的适用场景:
……
生活中的应用:笔和笔芯,一只一样的笔只要搭配不同的笔芯就能得到不一样的笔;开关,它将抽象和实现进行了分离,用户只需要关注开关这个抽象的东西,就能实现某种功能;
Java中的应用:在JDBC的驱动管理就使用了桥接模式,不同的数据库驱动名称放到Class.forName()中就能获取到对应的数据库连接
桥接模式的角色划分:
示例:
问题描述:一家小卖部它卖雪碧、可口可乐、橙汁、柠檬汁四款饮料,每种饮料都由大、中、小三中型号,请使用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:
<config>
<drinkType>com.hhxy.drink.imp.CocaColadrinkType>
<drinkType>com.hhxy.drink.imp.LemonJuicedrinkType>
<drinkType>com.hhxy.drink.imp.OrangeJuicedrinkType>
<drinkType>com.hhxy.drink.imp.SpritedrinkType>
<drinkType>testdrinkType>
<glassType>com.hhxy.glass.ext.SmallGlassglassType>
<glassType>com.hhxy.glass.ext.MediumGlassglassType>
<glassType>com.hhxy.glass.ext.BigGlassglassType>
<glassType>testglassType>
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


上一篇:每日一个设计模式之【适配器模式】
下一篇:每日一个设计模式之【组合模式】
参考文章:
在次致谢