桥接模式

桥接模式UML类图

桥接模式UML类图

桥接模式java实现

/**
* Enchantment.
*/
public interface Enchantment {

void onActivate();

void apply();

void onDeactivate();
}

/**
* Weapon.
*/
public interface Weapon {

void wield();

void swing();

void unwield();

Enchantment getEnchantment();
}

/**
* Sword.
*/
public class Sword implements Weapon {

private static final Logger LOGGER = LoggerFactory.getLogger(Sword.class);

private final Enchantment enchantment;

public Sword(Enchantment enchantment) {
this.enchantment = enchantment;
}

@Override
public void wield() {
//持剑。
LOGGER.info("The sword is wielded.");
enchantment.onActivate();
}

@Override
public void swing() {
//剑摆动。
LOGGER.info("The sword is swinged.");
enchantment.apply();
}

@Override
public void unwield() {
//剑未使用。
LOGGER.info("The sword is unwielded.");
enchantment.onDeactivate();
}

@Override
public Enchantment getEnchantment() {
return enchantment;
}
}

/**
* Hammer.
*/
public class Hammer implements Weapon {

private static final Logger LOGGER = LoggerFactory.getLogger(Hammer.class);

private final Enchantment enchantment;

public Hammer(Enchantment enchantment) {
this.enchantment = enchantment;
}

@Override
public void wield() {
//锤子被挥舞了。
LOGGER.info("The hammer is wielded.");
enchantment.onActivate();
}

@Override
public void swing() {
//锤子摆动。
LOGGER.info("The hammer is swinged.");
enchantment.apply();
}

@Override
public void unwield() {
//锤子没有挥舞。
LOGGER.info("The hammer is unwielded.");
enchantment.onDeactivate();
}

@Override
public Enchantment getEnchantment() {
return enchantment;
}
}

/**
* SoulEatingEnchantment.
*/
public class SoulEatingEnchantment implements Enchantment {

private static final Logger LOGGER = LoggerFactory.getLogger(SoulEatingEnchantment.class);

@Override
public void onActivate() {
//该物品散布嗜血的味道。
LOGGER.info("The item spreads bloodlust.");
}

@Override
public void apply() {
//该物品吞噬了敌人的灵魂。
LOGGER.info("The item eats the soul of enemies.");
}

@Override
public void onDeactivate() {
//嗜血慢慢消失。
LOGGER.info("Bloodlust slowly disappears.");
}
}

/**
* FlyingEnchantment.
*/
public class FlyingEnchantment implements Enchantment {

private static final Logger LOGGER = LoggerFactory.getLogger(FlyingEnchantment.class);

@Override
public void onActivate() {
//该项目开始微弱地发光。
LOGGER.info("The item begins to glow faintly.");
}

@Override
public void apply() {
//该物品飞走并击中了敌人,最后又回到了主人的手上。
LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
}

@Override
public void onDeactivate() {
//该项目的辉光消失。
LOGGER.info("The item's glow fades.");
}
}

/**
* Composition over inheritance. The Bridge pattern can also be thought of as two layers of
* abstraction. With Bridge, you can decouple an abstraction from its implementation so that the two
* can vary independently.
*
* <p>In Bridge pattern both abstraction ({@link Weapon}) and implementation ( {@link Enchantment})
* have their own class hierarchies. The interface of the implementations can be changed without
* affecting the clients.
*
* <p>In this example we have two class hierarchies. One of weapons and another one of
* enchantments. We can easily combine any weapon with any enchantment using composition instead of
* creating deep class hierarchy.
组成超过继承。 桥接模式也可以被认为是两层抽象。 使用Bridge,您可以将抽象与其实现分离,以便两者
可以独立变化。
<p>在Bridge模式中,抽象({@link Weapon})和实现({@link Enchantment})都有自己的类层次结构。 实现的界面可以更改,而不会影响客户端。
<p>在此示例中,我们有两个类层次结构。 一种武器,另一种是结界。 我们可以使用合成轻松地将任何武器与任何结界结合起来,而无需创建深层次的等级体系。
*/
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
//骑士收到一把附魔剑。
LOGGER.info("The knight receives an enchanted sword.");
var enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
//女武神得到了一个附魔的锤子。
LOGGER.info("The valkyrie receives an enchanted hammer.");
var hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();
}
}

应用场景

JDBC是桥接模式的典型实现。

先看下类图:
jdbc-bridge
通常使用JDBC连接数据库时,会使用如下代码:

Class.forName("数据库类驱动器");
Connection conn = DriverManager.getConnection("数据库url", "用户名", "密码");
//.................

针对不同的数据库,JDBC都可以通过java.sql.DriverManager类的静态方法getConnection(数据库url, 用户名, 密码)来获取数据库的连接。JDBC通过DriverManager对外提供了操作数据库的统一接口getConnection,通过该方法可以获取不同数据库的连接,并且通过Connection类提供的接口来进行数据的查询操作。

JDBC为不同的数据库操作提供了相同的接口,但是JDBC本身并没有针对每种数据库提供一套具体实现代码,而是通过接口java.sql.Driver的connect方法连接到了不同的数据库实现。

public interface Driver
{

public abstract Connection connect(String s, Properties properties) throws SQLException;
//其他方法

}

在JDBC中,针对不同数据库提供的统一的操作接口通过java.sql.Driver(桥)连接到了不同的数据库实现。如连接mysql数据库。

package com.mysql.jdbc;

public class NonRegisteringDriver implements java.sql.Driver //对java.sql.Driver接口提供了实现
{

public Connection connect(String url, Properties info)
throws SQLException
{
//实现
}

//其他方法
}

Java在连接MySQL时需要使用mysql-connector-java.jar,mysql-connector-java.jar包提供了对MySQL数据库操作的具体实现,并通过接口Driver连接到了JDBC统一的api。
JDBC中桥接模式具体如何实现?
既然,针对不同的数据库,通过DriverManger.getConnection()可以获得相同的Connection接口,那先看DriverManager的源码:

public class DriverManager
{
private static final CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList(); //存放DriverInfo的链表

public static synchronized void registerDriver(Driver driver)
throws SQLException
{
if(driver != null)
registeredDrivers.addIfAbsent(new DriverInfo(driver)); //向链表中添加DriverInfo实例,DriverInfo封装了Driver
else
throw new NullPointerException();
println((new StringBuilder()).append("registerDriver: ").append(driver).toString());
}

private static Connection getConnection(String s, Properties properties, Class class1)
throws SQLException
{
//.....
Iterator iterator = registeredDrivers.iterator(); //遍历registeredDrivers表
do
{
if(!iterator.hasNext())
break;
DriverInfo driverinfo = (DriverInfo)iterator.next();
if(isDriverAllowed(driverinfo.driver, classloader))
try
{
Connection connection = driverinfo.driver.connect(s, properties); //调用Driver接口提供的connect方法来获取Connection对象
if(connection != null)
{
return connection;
}
}
catch(SQLException sqlexception1)
{
if(sqlexception == null)
sqlexception = sqlexception1;
}
} while(true);
}

//其他方法

}

从DriverManager.getConnection()源码可见,方法中遍历了包含DriverInfo实例的表registeredDrivers,通过表中实例driverinfo来获取封装的java.sql.Driver类型的实例,并调用java.sql.Driver接口的connect方法获取到Connection。注:DriverInfo是Driver的封装类。由DriverInfo源码可见。

class DriverInfo
{

DriverInfo(Driver driver1)
{
driver = driver1;
}

public boolean equals(Object obj)
{
return (obj instanceof DriverInfo) && driver == ((DriverInfo)obj).driver;
}

public int hashCode()
{
return driver.hashCode();
}

public String toString()
{
return (new StringBuilder()).append("driver[className=").append(driver).append("]").toString();
}

final Driver driver;
}

那么,Driver实例是何时注入到DriverManager类的registeredDrivers中的呢?以mysql为例,在每次使用JDBC连接mysql时,都会有下面的调用:
Class.forName(“com.mysql.jdbc.Driver”);
该行代码通过反射加载了com.mysql.jdbc.Driver类(com.mysql.jdbc.Driver类在mysql-connector-java.jar中,而mysql-connector-java.jar是JDBC连接MySQL的jar包),查看com.mysql.jdbc.Driver类:

package com.mysql.jdbc;

public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{

public Driver()
throws SQLException
{
}

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

在com.mysql.jdbc.Driver的源码中可以看到在加载com.mysql.jdbc.Driver类时,通过类中的静态域中的红色代码,会调用DriverManager的registerDriver方法将当前MySQL的驱动类实例注入到DriverManager的registeredDrivers中。

通过整个代码调用,展示了桥接模式在JDBC中是如何运用的。