【设计模式】十一.结构型模式之桥接模式

桥接模式

一. 说明

桥接模式将抽象部分与实现部分分离,使它们都可以独立地变化,他也是结构型模式的一种。

桥接模式让实体类接入桥接的接口,使得实体类的功能独立于桥接接口实现类,这两种类型的类可被结构化改变而互不影响。像手机品牌与手机软件的关系,软件独立运行在操作系统上,而跟手机品牌是没有直接关系的,操作系统就像桥梁一样,只要手机品牌用的是同一个操作系统,即使新出一个手机品牌,手机软件依然能够在该品牌上运行。

二.应用场景

  1. 微信和支付宝都有二维码支付,刷脸支付等其他支付方式。支付渠道和支付方式之间可以随意组合,就会产生n*m个支付场景,而支付渠道和支付方式其实是可以通过桥梁接口进行解耦。
  2. Spring框架中的JdbcTemplate和Datasource之间就是桥接模式的实现。
  3. 在DDD中,领域服务和仓储层之间的关系可以认为是桥接模式,领域服务只依赖于仓储接口,仓储接口的实现在仓储层,仓储层可以是mysql,可以是Oracle,也可以是ElasticSearch等其他DB实现。

三.代码示例

我们以支付场景为案例,来说明桥接模式的使用。我们支付时通常需要判断支付渠道是什么,支付方式是什么,如果按一般思路编写代码,那就是这个样子的:

public void doPay(String uid, String orderId, String channel, String payType) {
   if (channel.equals("wxpay")) {
        if (payType.equals("qr")) {
            log.info("处理微信二维码支付");
        }
        if (payType.equals("face")) {
            log.info("处理微信刷脸支付");
        }
    } else if (channel.equals("alipay")) {
        if (payType.equals("qr")) {
            log.info("处理支付宝二维码支付");
        }
        if (payType.equals("face")) {
            log.info("处理支付宝刷脸支付");
        }
    }
}

可以看到这种代码,非常不利于维护,如果新增渠道或者方式,更改起来就是灾难。

其实,支付渠道在实际开发中,我们一般使用策略模式去实现的,这个我们后面的篇章会讲到,这里我们只关心支付方式的代码优化。

每一个支付渠道会存在不一样的支付方式,每一种支付方式对参数的校验或对用户的风控又是不一样的,某种支付方式在不同渠道下的逻辑却是一致的。所以这里,我们可以将支付方式抽象化成桥接接口,在支付渠道中进行桥接,支付时传入对应的方式即可。

先将支付渠道抽象化,建立支付渠道接口和支付宝+微信实现类

public interface IPay {
    boolean pay(String uid, String orderId);
}

@Slf4j
public class WxPay implements IPay {
    @Override
    public boolean pay(String uid, String orderId) {
        log.info("微信支付");
        return true;
    }
}

@Slf4j
public class AliPay implements IPay{
    @Override
    public boolean pay(String uid, String orderId) {
        log.info("支付宝支付");
        return true;
    }
}

再建立支付方式的桥接接口,有二维码支付和刷脸支付两种支付方式的实现

public interface IPayType {
    /**
     * 用户风控
     */
    boolean security(String uid);
}

@Slf4j
public class QRPayType implements IPayType{
    @Override
    public boolean security(String uid) {
        log.info("用户:{}执行二维码风控逻辑,风控结果放行。", uid);
        return true;
    }
}

@Slf4j
public class FacePayType implements IPayType{
    @Override
    public boolean security(String uid) {
        log.info("用户:{}执行刷脸支付风控逻辑,风控结果放行。", uid);
        return true;
    }
}

目前可以看到,我们的支付渠道和支付方式是各自隔离的并互不影响的,我们需要在支付渠道上新增抽象类,集成支付方式作为桥接,微信和支付宝实现类需要变更

public abstract class AbstractPay implements IPay {
    protected IPayType payType;
    public AbstractPay(IPayType payType) {
        this.payType = payType;
    }
    @Override
    public boolean pay(String uid, String orderId) {
        if (payType.security(uid)) {
            return realPay();
        }
        return false;
    }
    public abstract boolean realPay();
}

@Slf4j
public class WxPay extends AbstractPay {
    public WxPay(IPayType payType) {
        super(payType);
    }
    @Override
    public boolean realPay() {
        log.info("微信支付");
        return true;
    }
}

@Slf4j
public class AliPay extends AbstractPay {
    public AliPay(IPayType payType) {
        super(payType);
    }
    @Override
    public boolean realPay() {
        log.info("支付宝支付");
        return true;
    }
}

桥接完成,编写测试代码进行测试

 public static void main(String[] args) {
     IPay wxPay = new WxPay(new QRPayType());
      wxPay.pay("10001", "abcd");

      IPay aliPay = new AliPay(new FacePayType());
      aliPay.pay("10001", "abcd");
  }

【设计模式】十一.结构型模式之桥接模式_第1张图片
测试结果正确,此时,我们无论新增多少种支付渠道,或者新增多少种支付方式,我们的渠道和方式是可以随意组合的。

在Spring的JdbcTemplate中,桥接模式也是这么使用的,通过构造器注入DataSource,DataSource依赖的是jdbc的驱动接口,它的实现可以是mysql,也可以是oracle,JdbcTemplate不用关心Datasource是如何实现,它是拿来主义,拿来就能用。

public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
	 public JdbcTemplate(DataSource dataSource) {
        this.setDataSource(dataSource);
        this.afterPropertiesSet();
    }
    
	@Nullable
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
        //这里就是直接拿构造器注入的DataSource对象
        Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
        Statement stmt = null;

        Object var11;
        try {
            stmt = con.createStatement();
            this.applyStatementSettings(stmt);
            T result = action.doInStatement(stmt);
            this.handleWarnings(stmt);
            var11 = result;
        } catch (SQLException var9) {
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, this.getDataSource());
            con = null;
            throw this.translateException("StatementCallback", sql, var9);
        } finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, this.getDataSource());
        }

        return var11;
    }
}

四. 总结

桥接模式就跟桥梁一样,将两个职责不一样的类连接在一起,他们具有相关性, 又可以各自工作互不影响,即使需要扩展一个类,也不会影响到另一个类所承载的职责,所以它满足了单一职责原则和开闭原则。

你可能感兴趣的:(java设计模式,设计模式,桥接模式,java)