
一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网
本文介绍了桥接模式的设计思想和实现方法。桥接模式通过将抽象部分与实现部分分离,使它们可以独立变化,解决了多层继承带来的复杂性和耦合性问题。文章详细讲解了桥接模式的由来、定义、应用场景和实现步骤,并通过具体实例演示了如何在支付场景中使用桥接模式。此外,还讨论了桥接模式的优缺点及其适用环境,提供了丰富的代码示例和进一步学习的资源链接。
设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:
对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便。设计方案二即是桥接模式的应用。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
在实际应用中有许多场景,以下是一些常见的应用场景:更多内容
当我们思考桥接模式时,以下几个方面值得考虑:
桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展,有效的控制了系统中类的个数 (避免了继承层次的指数级爆炸)。更多内容
假设有一个几何形状Shape类,从它能扩展出两个子类:圆形Circle和方形Square。
你希望对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色Red和蓝色Blue的形状子类。
但是,由于你已有两个子类,所以总共需要创建四个类才能覆盖所有组合,例如蓝色圆形Blue-Circle和红色方形Red-Square。在层次结构中新增形状和颜色将导致代码复杂程度指数增长。在这种情况下,桥接模式就能起到作用,它将形状和颜色解耦,使得两者可以相对独立地变化。
桥接模式包含如下角色:
首先,我们创建一个抽象类 Shape,它有一个抽象方法 draw():更多内容
public abstract class Shape {
public abstract void draw();
}然后,我们创建两个实现了 Shape 接口的具体类:Circle 和 Square:
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("画一个圆形");
}
}
public class Square extends Shape {
@Override
public void draw() {
System.out.println("画一个正方形");
}
}接下来,我们创建一个桥接类 Color,它也实现了 Shape 接口,并持有一个 Shape 类型的引用:
public class Color extends Shape {
private Shape shape;
public Color(Shape shape) {
this.shape = shape;
}
@Override
public void draw() {
setColor();
shape.draw();
resetColor();
}
private void setColor() {
System.out.println("设置颜色");
}
private void resetColor() {
System.out.println("重置颜色");
}
}最后,我们在主函数中测试这个桥接模式:
private void test() {
Shape circle = new Circle();
Shape square = new Square();
Shape coloredCircle = new Color(circle);
Shape coloredSquare = new Color(square);
coloredCircle.draw();
coloredSquare.draw();
}使用桥接模式时需要注意以下几点:更多内容
以当下支付场景为例,看下不同支付模式中桥接模式的应用。
如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度,包括:支付渠道和支付方式。
不使用设计模式来模拟实现不同模式的支付场景。不使用设计模式缺点:维护和扩展都会变得非常复杂,需要修改原来代码,风险较大。更多内容
public class PayController {
/**
* @param uId 用户id
* @param tradeId 交易流水号
* @param amount 交易金额
* @param channelType 渠道类型 1 微信, 2 支付宝
* @param modeType 支付模式 1 密码,2 人脸,3 指纹
* @return: boolean
*/
public void doPay(String uId, String tradeId, BigDecimal amount, int channelType, int modeType) {
//微信支付
if (1 == channelType) {
System.out.println("微信渠道支付划账开始......");
if (1 == modeType) {
System.out.println("密码支付");
} else if (2 == modeType) {
System.out.println("人脸支付");
} else if (3 == modeType) {
System.out.println("指纹支付");
}
}
//支付宝支付
if (2 == channelType) {
System.out.println("支付宝渠道支付划账开始......");
if (1 == modeType) {
System.out.println("密码支付");
} else if (2 == modeType) {
System.out.println("人脸支付");
} else if (3 == modeType) {
System.out.println("指纹支付");
}
}
}
}使用一下,如下所示:
private void test1() {
PayController payController = new PayController();
System.out.println("测试: 微信支付、人脸支付方式");
payController.doPay("weixin", "1000112333333", new BigDecimal(100), 1, 2);
System.out.println("\n测试: 支付宝支付、指纹支付方式");
payController.doPay("zhifubao", "1000112334567", new BigDecimal(100), 2, 3);
}虽然不使用设计模式也能实现该支付场景需求,但以后若增加支付渠道或修改支付方式,比如增加了京东支付,抖音支付,或者增加一个微信刷掌支付,则成本比较高,不利于后边的扩展和维护。
使用桥接模式重构支付场景代码。
桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。更多内容
针对该支付场景上边已经抽出了2个维度,即:支付渠道 和 支付方式;这里我们可以把支付渠道作为抽象化角色,支付方式作为实现化角色,支付渠道*支付模式 = 相对应的支付组合;
1)Pay抽象类(支付渠道)
2)IPayMode接口(支付方式)
Implementor:实现类接口。定义实现化角色的接口,包含角色必须的行为和属性,并供扩展抽象化角色调用。更多内容,这个定义支付方式接口!
/**
* 支付模式接口
*/
public interface IPayMode {
//安全校验功能: 对各种支付模式进行风控校验
boolean security(String uId);
}ConcreteImplementor:具体实现类。给出实现化角色接口的具体实现。这里有密码支付,刷脸支付,指纹支付等。
/**
* 密码支付及风控校验
* 具体实现化(Concrete Implementor)角色
*/
public class PayCypher implements IPayMode {
@Override
public boolean security(String uId) {
return false;
}
}
/**
* 刷脸支付及风控校验
* 具体实现化(Concrete Implementor)角色
*/
public class PayFaceMode implements IPayMode {
@Override
public boolean security(String uId) {
return true;
}
}
/**
* 指纹支付及风控校验
* 具体实现化(Concrete Implementor)角色
*/
public class PayFingerprintMode implements IPayMode {
@Override
public boolean security(String uId) {
return false;
}
}Abstraction:抽象类。主要负责定义出该角色的行为,并包含一个对实现化对象的引用。这里针对渠道抽象出角色。更多内容
/**
* 支付抽象化类
* 抽象化(Abstraction)角色
*/
public abstract class Pay {
protected IPayMode payMode;
public Pay(IPayMode payMode){
this.payMode = payMode;
}
//划账功能
public abstract String transfer(String uId, String tradeId, BigDecimal amount);
}RefinedAbstraction:扩充抽象类。是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
/**
* 支付渠道-微信
* 扩展抽象化(RefinedAbstraction)角色
*/
public class WxPay extends Pay{
public WxPay(IPayMode payMode) {
super(payMode);
}
@Override
public String transfer(String uId, String tradeId, BigDecimal amount) {
System.out.println("微信渠道支付划账开始......");
//支付方式校验
boolean security = payMode.security(uId);
System.out.println("微信渠道支付风险校验: " + uId + " , " + tradeId +" , " + security);
if(!security){
System.out.println("微信渠道支付划账失败!");
return "500";
}
System.out.println("微信渠道划账成功! 金额: "+ amount);
return "200";
}
}
/**
* 支付渠道--支付宝
* 扩展抽象化(RefinedAbstraction)角色
*/
public class ZfbPay extends Pay{
public ZfbPay(IPayMode payMode) {
super(payMode);
}
@Override
public String transfer(String uId, String tradeId, BigDecimal amount) {
System.out.println("支付宝渠道支付划账开始......");
//支付方式校验
boolean security = payMode.security(uId);
System.out.println("支付宝渠道支付风险校验: " + uId + " , " + tradeId +" , " + security);
if(!security){
System.out.println("支付宝渠道支付划账失败!");
return "500";
}
System.out.println("支付宝渠道划账成功! 金额: "+ amount);
return "200";
}
}最后测试一下桥接模式,如下
private void test1() {
System.out.println("测试场景1: 微信支付、人脸方式.");
Pay wxpay = new WxPay(new PayFaceMode());
wxpay.transfer("weixin","10001900",new BigDecimal(100));
System.out.println();
System.out.println("测试场景2: 支付宝支付、指纹方式");
Pay zfbPay = new ZfbPay(new PayFingerprintMode());
zfbPay.transfer("zhifubao","567689999999",new BigDecimal(200));
}使用继承和组合的方式实现桥接模式。更多内容
这种方式需要创建两个类,一个作为抽象类,另一个作为具体类。抽象类中定义了对抽象部分和实现部分的引用,具体类中实现了抽象部分的具体逻辑。
// 抽象部分
abstract class Abstraction {
protected Implementation implementation;
public void setImplementation(Implementation implementation) {
this.implementation = implementation;
}
public abstract void operation();
}
// 具体部分
class ConcreteAbstraction extends Abstraction {
@Override
public void operation() {
System.out.println("具体操作");
}
}
// 实现部分
interface Implementation {
void operationImpl();
}
class ConcreteImplementationA implements Implementation {
@Override
public void operationImpl() {
System.out.println("实现A的操作");
}
}
class ConcreteImplementationB implements Implementation {
@Override
public void operationImpl() {
System.out.println("实现B的操作");
}
}
// 客户端代码
private void test() {
Abstraction abstraction = new ConcreteAbstraction();
Implementation implementationA = new ConcreteImplementationA();
Implementation implementationB = new ConcreteImplementationB();
abstraction.setImplementation(implementationA);
abstraction.operation(); // 输出:具体操作
abstraction.setImplementation(implementationB);
abstraction.operation(); // 输出:具体操作
}使用接口和内部类的方式实现桥接模式
这种方式需要创建一个接口,一个抽象类和一个内部类。抽象类中定义了对接口的引用,内部类中实现了抽象类的具体逻辑。
// 接口
interface Shape {
void draw();
}
// 抽象部分
abstract class AbstractShape {
protected Shape shape;
public void setShape(Shape shape) {
this.shape = shape;
}
public abstract void draw();
}
// 具体部分
class Rectangle extends AbstractShape {
@Override
public void draw() {
shape.draw();
}
}
class Circle extends AbstractShape {
@Override
public void draw() {
shape.draw();
}
}
// 内部类实现接口
class ShapeImpl implements Shape {
@Override
public void draw() {
System.out.println("绘制形状");
}
}
// 客户端代码
private void test() {
AbstractShape abstractShape = new Rectangle();
Shape shapeA = new ShapeImpl();
Shape shapeB = new ShapeImpl();
abstractShape.setShape(shapeA);
abstractShape.draw(); // 输出:绘制形状
abstractShape.setShape(shapeB);
abstractShape.draw(); // 输出:绘制形状
}桥接模式的优点:
桥接模式的缺点:
在以下情况下可以使用桥接模式:
适配器模式与桥接模式的联用:更多内容
桥接模式和适配器模式用于设计的不同阶段,桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,特别是那些涉及到大量第三方应用接口的情况。
01.桥接模式基础
桥接模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,桥接模式被引入。
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
主要解决的问题:桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转变为动态的组合关系,使得系统更加灵活,并易于扩展。
02.桥接模式实现
假设有一个几何形状Shape类,从它能扩展出两个子类:圆形Circle和方形Square。对这样的类层次结构进行扩展以使其包含颜色,所以你打算创建名为红色Red和蓝色Blue的形状子类。
桥接模式实现如下所示:
03.桥接实例演示
如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度,包括:支付渠道和支付方式。
不使用设计模式来模拟实现不同模式的支付场景。不使用设计模式缺点:维护和扩展都会变得非常复杂,需要修改原来代码,风险较大。
以后若增加支付渠道或修改支付方式,比如增加了京东支付,抖音支付,或者增加一个微信刷掌支付,则成本比较高,不利于后边的扩展和维护。
桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。
模块 | 描述 | 备注 |
|---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 |
23种设计模式
23种设计模式 & 描述 & 核心作用 | 包括 |
|---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是“类或对象之间的交互”问题 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。