浅谈设计模式-桥接模式

书接上回,本篇讲一下结构型模式-桥接设计模式

桥接设计模式

定义:将抽象部分与它的具体实现部分分离,使他们都可以独立的变化
实现:通过组合的方式建立两个类之间联系,而不是继承

这里得解释什么事抽象部分,什么事具体实现部分,什么是独立变化,还是什么事桥接

抽象部分

在Java中一般说到抽象,首先想到的是接口,抽象类,然后对应的抽象方法。这些都算抽象,但这理解停留在编码层面上,有点狭义。如果把抽象立意提升到架构设计上,抽象更多表示规则(规范)、约定(约束)。举个例子:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

//-------------------------------------
public class Thread implements Runnable {

    public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }

    public synchronized void start() {
        strart0();
    }

    private native void start0();

    public void run() {
        if (target != null) {
            target.run();
        }
    }

}

上面的JDK中线程Runnable接口,定义了一个run方法,约定线程执行要调用run方法,居于这个约定,Thread线程操作设计出以下规则:

1>实现Runable接口,重写run方法  2> 创建Thread实例,传入runable接口对象   3>start启动

有了这个基础之后,桥接模式中抽象部分指的是:业务逻辑(或说执行流程)的抽象,抽象部分定义操作逻辑规范,业务正式执行,依赖实现部分对象来实现。

实现部分

这个跟抽象部分是一对的,抽象是在定义规则,实现部分就是实现规则,还是以线程为例子:

抽象部分定义线程执行规则:

1>实现Runable接口,重写run方法  2> 创建Thread实例,传入runable接口对象   3>start启动

实现部分依照规则实现逻辑

//匿名内部类实现Runnable接口,重写run方法
Runable run = new Runnable() {
	@Override
	public void run() {
		System.out.println(".....");
	}
};
//创建线程对象,传入runable实例
Thread th = new Thread(run);
//启动线程,调用run对象中run方法
th.start();

独立变化

独立变化指的是抽象部分跟实现部分的变化,这里的变化个人认为是扩展,

抽象部分扩展:
根据业务/需求不同,再原有的基础上做扩展(比如扩展新的业务方法)
落地实现方法:接口继承,子类继承等

实现部扩展:跟上面一样,可以多接口实现,多子类实现

桥接

桥接模式还有一个核心点:桥接。何为桥接?就是建立抽象部分与实现部分连接通道,让他们可以进行通讯,实现实现方式:组合,更装逼的讲法:面向接口/抽象编程。比如:

public class EmployeeController {
    private IEmployeeService employeeService = new EmployeeServiceImpl();
}

最后来看下,UML的定义。

UML 

Abstratction:抽象部分的接口(或抽象类),定制具体实现规则,同时里面要持有实现部分对象(implementor)引用。

RefinedAbstraction:扩展抽象部分的实现,实现Abstratction接口,可以根据业务变化扩展出RefinedAbstractionA/B/C来。但是,不管怎么变,真实业务操作还是要靠实现部分对象(implementor)去实现。

implementor:定义实现部分的接口,定义的业务真实实现方法。

ConCreteImplementor:实现implementor,业务方法真实实现者,可以根据业务变化,扩展出ConCreteImplementorA/B/C来。

实现部分-Implementor 

/**
 * 具体部分
 */
public interface Implementor {
    //真实要执行的逻辑
    void operation();
}

 实现部分-ConCreteImplementorA 

//真实执行方式A
public class ConCreteImplementorA implements Implementor{
    @Override
    public void operation() {
        System.out.println("具体部分A实现....");
    }
}

 实现部分-ConCreteImplementorB

//真实执行方式B
public class ConCreteImplementorB implements Implementor{
    @Override
    public void operation() {
        System.out.println("具体部分B实现....");
    }
}

抽象部分-Abstraaction 

/**
 * 抽象部分
 * 定义操作规则,具体实现由Implementor实现
 */
public abstract class Abstraction {
    private Implementor implementor;
    public Abstraaction(Implementor implementor) {
        this.implementor = implementor;
    }
    //业务操作规则
    protected void operation(){
        implementor.operation();
    }
}

抽象部分-RefindedAbastractionA  

//根据业务不一样,可以扩展出更多的具体实现调用规则
//这里是规则A
public class RefindedAbastractionA  extends Abstraction{

    public RefindedAbastractionA(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        System.out.println("调用xxx方法执行规则A")
        super.operation();
    }
}

 抽象部分-RefindedAbastractionB

//根据业务不一样,可以扩展出更多的具体实现调用规则
//这里是规则B
public class RefindedAbastractionB  extends Abstraction{

    public RefindedAbastractionB(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        System.out.println("调用xxx方法执行规则B")
        super.operation();
    }
}

左边是抽象部分Abstraction,只有操作规则的定制(operation),没有具体实现,具体的实现依赖右边的Implementor 接口。

抽象部分跟具体部分使用组合模式建立操作通道,也就是桥接

    private Implementor implementor;
    public Abstraaction(Implementor implementor) {
        this.implementor = implementor;
    }

后续如果需求发送变化,抽象部分可以扩展出:RefindedAbastractionB  具体部分可以拓展出:ConCreteImplementorC。

案例分析

需求:支付宝支付与微信支付
支付宝支付:余额,信用卡,银行卡 
微信支付:余额,信用卡,银行卡

分析

支付方式有3种:余额,信用卡,银行卡 
支付平台有2种:支付宝,微信
能组合出来的支付逻辑:3 * 2 = 6种
支付宝余额支付,支付宝信用卡支付,支付宝银行支付
微信余额支付,微信信用卡支付,微信银行支付

这种场景就非常符合桥接模式啦:
1>不同维度(平台/支付方式)组合 


2>抽象与实现分离--抽象:平台,平台设计支付逻辑。实现:支付方式,执行具体支付操作。

直接上代码:

UML 

 实现部分-IPay

//具体部分-支付逻辑
public interface IPay {
    //支付
    void pay(int money);
}

 实现部分-BalancePay 

//实现部分-余额支付
public class BalancePay implements IPay{
    @Override
    public void pay(int money) {
        System.out.println("支付方式:余额,支付金额:" + money);
    }
}

   实现部分-BankCardPay 

//实现部分-银行卡支付
public class BankCardPay implements IPay{
    @Override
    public void pay(int money) {
        System.out.println("支付方式:银行卡,支付金额:" + money);
    }
}

   实现部分-CreditCardPay 

//实现部分-信用卡支付
public class CreditCardPay implements IPay{
    @Override
    public void pay(int money) {
        System.out.println("支付方式:信用卡,支付金额:" + money);
    }
}

抽象部分-PayPlatform  

//抽象部分-支付平台
public abstract class PayPlatform {
    private IPay pay;
    public PayPlaform(IPay pay) {
        this.pay = pay;
    }
    public void pay(int money){
        pay.pay(money);
    }
}

 抽象部分-AlipayPlatform  

//抽象部分-支付宝平台
public class AlipayPlatform  extends PayPlatform{
    public AlipayPlatform(IPay pay) {
        super(pay);
    }

    @Override
    public void pay(int money) {
        super.pay(money);
        System.out.println("支付宝支付完成,赠送花呗1000额度...");
    }
}

 抽象部分-WeChatPlatform 

//抽象部分-微信平台
public class WeChatPlatform extends PayPlatform{
    public WeChatPlatform(IPay pay) {
        super(pay);
    }

    @Override
    public void pay(int money) {
        super.pay(money);
        System.out.println("微信支付完成,赠送微粒贷1000额度...");
    }
}

测试

public class App {

    public static void main(String[] args) {
        //支付宝平台
        new AlipayPlatform(new BalancePay()).pay(1000);    //余额
        new AlipayPlatform(new BankCardPay()).pay(1000);   //银行卡
        new AlipayPlatform(new CreditCardPay()).pay(1000); //信用卡

        System.out.println("-------------------------------------------");
        //微信平台
        new WeChatPlatform(new BalancePay()).pay(1000);    //余额
        new WeChatPlatform(new BankCardPay()).pay(1000);   //银行卡
        new WeChatPlatform(new CreditCardPay()).pay(1000); //信用卡
    }
}

结果

支付方式:余额,支付金额:1000
支付宝支付完成,赠送花呗1000额度...
支付方式:银行卡,支付金额:1000
支付宝支付完成,赠送花呗1000额度...
支付方式:信用卡,支付金额:1000
支付宝支付完成,赠送花呗1000额度...
-------------------------------------------
支付方式:余额,支付金额:1000
微信支付完成,赠送微粒贷1000额度...
支付方式:银行卡,支付金额:1000
微信支付完成,赠送微粒贷1000额度...
支付方式:信用卡,支付金额:1000
微信支付完成,赠送微粒贷1000额度...

解析

分析代码结构:Platform 负责制定平台整套支付流程,具体的实现交给IPay实现,标准的桥接模式。这么好处在于从容扩展,

比如:
加入银行的云支付,只需要再弄一个云支付Platform即可
加入积分支付,只需要再实现IPay接口即可。

完全符合开闭原则:扩展开放,修改闭合。

适用场景

一套逻辑存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展时,可用
不希望使用继承,或因为多层继承导致系统类个数剧增时,可用。

优缺点

优点

分离抽象部分及其具体实现部分,提高系统的可扩展性
符合开闭
符合合成复用

缺点

增加系统理解与设计难度
需要正确的识别系统2个独立变化的维度

广义桥接模式

来做一道题:用一句大白话讲清楚Java 面向接口编程。

大白话:接口把具体的实现和使用接口的客户端分离开来,从而使得具体的实现和使用接口的客户端可以分别扩展,而不会相互影响。

public class EmployeeServiceImpl  implements IEmployeeService{
    //private IEmployeeDAO  dao= new EmployeeMysqlDAO();
    private IEmployeeDAO  dao= new EmployeeOracelDAO();

    @Override
    public void save() {
        dao.save(....);
    }
}

EmployeeServiceImpl  客户端依赖IEmployeeDAO接口,具体是mysql 的DAO实现还是Oracle的DAO实现,都无所谓,能执行即可。

仔细想想,是不是跟桥接模式操作优点类似呢?答案是yes的,从某种角度来说,桥接模式可以认为是面向接口/抽象编程的扩展。这种简化版的桥接模式,就是广义的桥接模式。

开发案例

桥接模式在JDK中经典案例-JDBC

回顾一下JDBC操作步骤:贾琏欲执事

//贾:加载驱动
Class.forName("com.mysql.jdbc.Driver");

//琏:获取数据库连接
Connection connection = DriverManager.
                    getConnection("jdbc:mysql://localhost:3306/数据名", "数据库账号", "数据库密码");

//欲:获取预编译对象
PreparedStatement ps = connection.prepareStatement(sql);

//执:执行SQL语句
ps.executeUpdate();

//事:释放资源
ps.close();
connection.close();

从JDBC API上看,大家有没有发现,都是JDK的api,操作的都是接口,具体实现没有,到这反应过来了没,上面代码都是桥接模式中的抽象部分。那实现部分呢?就是各种数据库驱动程序啦。关系图大概是这样:

 其中的DriverManager就是连接的桥梁

//com.mysql.jdbc.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {

    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

}

 DriverManager 类中registeredDrivers 属性存放所有加载的jdbc驱动,算是建立桥接啦

public class DriverManager {


    // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

    public static void registerDriver(java.sql.Driver driver)
        throws SQLException {

        registerDriver(driver, null);
    }

    public static void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
     
    }
    
}

总结

桥接模式的本质:分离抽象和实现,适合多维度扩展

广义桥接模式:面向接口/抽象编程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值