设计模式-结构-桥接模式

5.2 桥接模式

5.2.1 桥接模式介绍

桥接模式(bridge pattern) 的定义是:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式用一种巧妙的方式处理多层继承存在的问题。用抽象关联来取代传统的多层继承。将类之间的静态继承关系转变为动态的组合关系。使得系统更加灵活,并易于扩展,有效的控制了系统中类的个数 (避免了继承层次的指数级爆炸)。

5.2.2 桥接模式原理


桥接(Bridge)模式包含以下主要角色:

  • 抽象化(Abstraction)角色 :主要负责定义出该角色的行为 ,并包含一个对实现化对象的引用。
  • 扩展抽象化(RefinedAbstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色 :定义实现化角色的接口,包含角色必须的行为和属性,并供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。

桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合.总结一句话就是: 抽象角色引用实现角色

5.2.3 桥接模式的应用实例

模拟不同的支付工具对应不同的支付模式,比如微信和支付宝都可以完成支付操作,而支付操作又可以有扫码支付、密码支付、人脸支付等,那么关于支付操作其实就有两个维度, 包括:支付渠道和支付方式

在这里插入图片描述
1) 不使用设计模式

public class PayController {


    /**
     * @param uId   用户id
     * @param tradeId 交易流水号
     * @param amount    交易金额
     * @param channelType 渠道类型 1 微信, 2 支付宝
     * @param modeType    支付模式 1 密码,2 人脸,3 指纹
     * @return: boolean
     */
    public boolean doPay(String uId, String tradeId, BigDecimal amount,int channelType,int modeType){
        //微信支付
        if(1 == channelType){
            System.out.println("微信渠道支付划账开始......");
            if(1 == modeType){
                System.out.println("密码支付");
            }if(2 == modeType){
                System.out.println("人脸支付");
            }if(3 == modeType){
                System.out.println("指纹支付");
            }
        }

        //支付宝支付
        if(2 == channelType){
            System.out.println("支付宝渠道支付划账开始......");
            if(1 == modeType){
                System.out.println("密码支付");
            }if(2 == modeType){
                System.out.println("人脸支付");
            }if(3 == modeType){
                System.out.println("指纹支付");
            }
        }
        return true;
    }
}

//测试
public class Test_Pay {

    public static void main(String[] args) {
        PayController payController = new PayController();
        System.out.println("测试: 微信支付、人脸支付方式");
        payController.doPay("weixin_001","1000112333333",new BigDecimal(100),1,2);

        System.out.println("\n测试: 支付宝支付、指纹支付方式");
        payController.doPay("hifubao_002","1000112334567",new BigDecimal(100),2,3);
    }
}

从测试结果看,是满足了需求,但是这样的代码设计,后面的维护和扩展都会变得非常复杂.

1) 桥接模式重构代码

桥接模式原理的核心是: 首先有要识别出一个类所具有的的两个独立变化维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合.

  • Pay抽象类
    • 支付渠道子类: 微信支付
    • 支付渠道子类: 支付宝支付
  • IPayMode接口
    • 支付模式实现: 刷脸支付
    • 支付模式实现: 指纹支付
  • 支付渠道*支付模式 = 相对应的组合.
  • 重构类图
    在这里插入图片描述
    1) 支付模式接口 (实现化角色)
/**
 * 支付模式接口
 * @author spikeCong
 * @date 2022/9/26
 **/
public interface IPayMode {

    //安全校验功能: 对各种支付模式进行风控校验
    boolean security(String uId);
}

2) 三种具体支付模式 (具体实现化角色)

//指纹支付及风控校验
public class PayFingerprintMode implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("指纹支付,风控校验-指纹信息");
        return true;
    }
}

//刷脸支付及风控校验
public class PayFaceMode implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("人脸支付,风控校验-脸部识别");
        return true;
    }
}
//密码支付及风控校验
public class PayCypher implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("密码支付,风控校验-环境安全");
        return false;
    }
}

3) 支付抽象类 (抽象化角色)

public abstract class Pay {
    //桥接对象
    protected IPayMode payMode;

    public Pay(IPayMode payMode) {
        this.payMode = payMode;
    }

    //划账功能
    public abstract String transfer(String uId, String tradeId, BigDecimal amount);

}

4) 支付渠道实现 (扩展抽象化角色)

/**
 * 支付渠道-微信划账
 * @author spikeCong
 * @date 2022/9/26
 **/
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";
    }
}

/**
 * 支付渠道-支付宝划账
 * @author spikeCong
 * @date 2022/9/26
 **/
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";
    }
}

5) 测试

public class Client {
    public static void main(String[] args) {
        System.out.println("测试场景1: 微信支付、人脸方式.");
        Pay wxpay = new WxPay(new PayFaceMode());
        wxpay.transfer("wx_00100100", "10001900", new BigDecimal(100));

        System.out.println("测试场景2: 支付宝支付、指纹方式");
        Pay zfbPay = new ZfbPay(new PayFingerprintMode());
        zfbPay.transfer("jlu1234567", "567689999999", new BigDecimal(200));
    }
}

代码重构完成后,结构更加清晰整洁, 可读性和易用性更高,外部的使用接口的用户不需要关心具体实现.桥接模式满足了单一职责原则和开闭原则,让每一部分都更加清晰并且易扩展.

5.2.4 桥接模式总结

桥接模式的优点:

  1. 分离抽象接口及其实现部分.桥接模式使用"对象间的关联关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化.
  2. 在很多情况下,桥接模式可以取代多层继承方案.多层继承方案违背了单一职责原则,复用性差,类的个数多.桥接模式很好的解决了这些问题.
  3. 桥接模式提高了系统的扩展性,在两个变化维度中任意扩展一个维度都不需要修改原有系统,符合开闭原则.

桥接模式的缺点:

  1. 桥接模式的使用会增加系统的理解和设计难度,由于关联关系建立在抽象层,要求开发者一开始就要对抽象层进行设计和编程
  2. 桥接模式要求正确识别出系统中的两个独立变化的维度,因此具有一定的局限性,并且如果正确的进行维度的划分,也需要相当丰富的经验.

桥接模式使用场景

  1. 需要提供平台独立性的应用程序时。 比如,不同数据库的 JDBC 驱动程序、硬盘驱动程序等。

  2. 需要在某种统一协议下增加更多组件时。 比如,在支付场景中,我们期望支持微信、支付宝、各大银行的支付组件等。这里的统一协议是收款、支付、扣款,而组件就是微信、支付宝等。

  3. 基于消息驱动的场景。 虽然消息的行为比较统一,主要包括发送、接收、处理和回执,但其实具体客户端的实现通常却各不相同,比如,手机短信、邮件消息、QQ 消息、微信消息等。

  4. 拆分复杂的类对象时。 当一个类中包含大量对象和方法时,既不方便阅读,也不方便修改。

  5. 希望从多个独立维度上扩展时。 比如,系统功能性和非功能性角度,业务或技术角度等。

5.2.5 实际案例

在Spring框架中,桥接模式通常通过JdbcTemplate类来实现。JdbcTemplate是Spring JDBC框架的核心类,它提供了一系列方法来执行SQL查询和更新操作。

JdbcTemplate内部使用了桥接模式,将不同类型的连接API转换成统一的API,从而使得应用程序无需关心底层数据库的细节。

总之,Spring框架中的桥接模式是一种用于解决不同API之间不兼容性问题的设计模式,通过将不同类型的API转换成统一的API,使得应用程序能够无缝地集成各种类型的数据库。

以下是Spring框架中使用桥接模式的示例代码:

// JDBC模板类


public class JdbcTemplate {
    private DataSource dataSource;

    // 构造函数,传入数据源
    public JdbcTemplate(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // 查询方法
    public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        List<T> results = new ArrayList<T>();

        try {
            // 通过数据源获取数据库连接
            conn = this.dataSource.getConnection();
            // 创建预编译语句

对象
            stmt = conn.prepareStatement(sql);
            // 设置参数
            for (int i = 0; i < args.length; i++) {
                stmt.setObject

(i + 1, args[i]);
            }
            // 执行查询
            rs = stmt.executeQuery();
            // 处理结果集
            while (rs.next()) {
                T result = rowMapper.mapRow(rs, rs.getRow());
                results.add(result);
            }
        } catch (SQLException e) {
            // 处理异常
        } finally {
            // 关闭连接、语句和结果集
            closeResultSet(rs);
            closeStatement(stmt);
            closeConnection(conn);
        }

        return results;
    }

    // 关闭连接方法
    private void closeConnection(Connection conn) {
        // ...
    }

    // 关闭语句方法
    private void closeStatement(Statement stmt) {
        // ...
    }

    // 关闭结果集方法
    private void closeResultSet(ResultSet rs) {
        // ...
    }
}

// 数据源接口
public interface DataSource {
    Connection getConnection() throws SQLException;
}

// MySQL数据源实现类
public class MySQLDataSource implements DataSource {
    // 实现获取连接方法
    public Connection getConnection() throws SQLException {
        // ...
    }
}

// Oracle数据源实现类
public class OracleDataSource implements DataSource {
    // 实现获取连接方法
    public Connection getConnection() throws SQLException {
        // ...
    }
}

在这个示例中,JdbcTemplate是桥接模式的核心类,它通过数据源接口DataSource来桥接不同类型的数据库连接API。MySQLDataSourceOracleDataSource是实现DataSource接口的具体实现类,它们分别提供了不同类型的数据库连接实现。

通过这种方式,JdbcTemplate类就可以使用不同类型的数据库连接API,而不必担心底层的细节。这样,应用程序就可以方便地连接和操作各种类型的数据库了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值