单位怎样做网站,网站seo评测,自己做网站卖东西怎么样,西安网站制作公司排1、传统方式解决手机操作问题
现在对不同手机类型的不同品牌实现操作编程#xff08;比如#xff1a;开机、关机、上网#xff0c;打电话等#xff09;#xff0c;如图#xff1a; UML 类图 问题分析
扩展性问题#xff08;类爆炸#xff09;#xff1a;如果我们再…1、传统方式解决手机操作问题
现在对不同手机类型的不同品牌实现操作编程比如开机、关机、上网打电话等如图 UML 类图 问题分析
扩展性问题类爆炸如果我们再增加手机的样式旋转式就需要增加各个品牌手机的类同样如果我们增加一个手机品牌也要在各个手机样式类下增加违反了单一职责原则当我们增加手机样式时要同时增加所有品牌的手机这样增加了代码维护成本解决方案——使用桥接模式
2、桥接模式基本介绍
桥接模式Bridge模式一种结构型设计模式将实现与抽象放在两个不同的类层次中使两个层次可以独立改变Bridge模式基于类的最小设计原则通过使用封装、聚合及继承等行为让不同的类承担不同的职责它的主要特点是把抽象Abstraction与行为实现Implementation分离开来从而可以保持各部分的独立性以及应对他们的功能扩展
举个例子说明下 对于手机来说我们可以根据手机品牌分类也可以根据手机类型来分类。所以手机这个系统可以在这两个角度独立的变化手机品牌的变化不影响手机类型的变化。桥接模式其实就是通过合成/聚合代替继承实现了松耦合的、在各个不同角度的”独立地变化“。 原理类图 原理类图说明
Client桥接模式的调用者AbstractionAbstraction 充当桥接类维护了 Implementor即 ConcreteImplementorA / ConcreteImplementorBRefinedAbstractionAbstraction 抽象类的子类Implementor行为实现类的接口ConcreteImplementorA / ConcreteImplementorB行为的具体实现类这里的抽象类和接口是聚合的关系也是调用者和被调用者的关系
3、桥接模式解决手机操作问题
UML 类图 核心代码
// 行为接口——品牌接口
public interface Branch {void open();void call();void close();
}
// 行为实现类——华为品牌
public class Huawei implements Branch {Overridepublic void open() {System.out.println(华为手机开机);}Overridepublic void call() {System.out.println(华为手机打电话);}Overridepublic void close() {System.out.println(华为手机关机);}
}
// 行为实现类——小米品牌
public class Xiaomi implements Branch {Overridepublic void open() {System.out.println(小米手机开机);}Overridepublic void call() {System.out.println(小米手机打电话);}Overridepublic void close() {System.out.println(小米手机关机);}
}
// 行为实现类——苹果品牌
public class iPhone implements Branch {Overridepublic void open() {System.out.println(苹果手机开机);}Overridepublic void call() {System.out.println(苹果手机打电话);}Overridepublic void close() {System.out.println(苹果手机关机);}
}// 桥接类——手机抽象类
public abstract class Phone {private Branch branch;public Phone(Branch branch) {this.branch branch;}public void open() {branch.open();}public void call() {branch.call();}public void close() {branch.close();}
}
// 桥接子类——翻盖式手机
public class FlipPhone extends Phone {public FlipPhone(Branch branch) {super(branch);System.out.println(翻盖式手机);}Overridepublic void open() {super.open();}Overridepublic void call() {super.call();}Overridepublic void close() {super.close();}
}
// 桥接子类——滑盖式手机
public class SlidePhone extends Phone {public SlidePhone(Branch branch) {super(branch);System.out.println(滑盖式手机);}Overridepublic void open() {super.open();}Overridepublic void call() {super.call();}Overridepublic void close() {super.close();}
}
// 桥接子类——直立式手机
public class UprightPhone extends Phone {public UprightPhone(Branch branch) {super(branch);System.out.println(直立式手机);}Overridepublic void open() {super.open();}Overridepublic void call() {super.call();}Overridepublic void close() {super.close();}
}public class Client {public static void main(String[] args) {Phone phone new FlipPhone(new Huawei());phone.open();phone.call();phone.close();System.out.println();phone new FlipPhone(new Xiaomi());phone.open();phone.call();phone.close();System.out.println();phone new UprightPhone(new iPhone());phone.open();phone.call();phone.close();}
}4、JDBC 源码分析
说起jdbc,我相信很多人都不陌生,在最开始的web项目中,我们常常用它来连接数据库执行sql语句,下面是一个连接mysql的例子: /*** 定义数据库连接辅助类*/
public class DBhelper {private static final String DRIVERNAME com.mysql.cj.jdbc.Driver;private static final String URL jdbc:mysql://127.0.0.1:3306/test;private static final String USER root;private static final String PASSWORD 123456;private Connection conn null;private Statement st null;private PreparedStatement ppst null;private ResultSet rs null; /*加载驱动*/static {try {Class.forName(DRIVERNAME).newInstance();} catch (Exception e) {e.printStackTrace();System.out.println(加载驱动失败);}} /*获取数据库连接*/public Connection getConn() {try {conn DriverManager.getConnection(URL, USER, PASSWORD);} catch (SQLException throwables) {throwables.printStackTrace();System.out.println(获取数据库连接失败);}return conn;} /*释放数据库连接*/public void releaseConn() {if (rs ! null) {try {rs.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if (st ! null) {try {st.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if (ppst ! null) {try {ppst.close();} catch (SQLException e) {System.out.println(e.getMessage());}}if (conn ! null) {try {conn.close();} catch (SQLException e) {System.out.println(e.getMessage());}}}测试一下:
public class TestJdbc {private Connection conn null;private Statement st null;private PreparedStatement ppst null;private ResultSet rs null;private ListObject selectUser(User user) {ListObject list new ArrayList();DBhelper dBhelper new DBhelper();conn dBhelper.getConn();String sql select * from blog_user where login_num user.getLoginNum() and password user.getPassword();try {st conn.createStatement();rs st.executeQuery(sql);ResultSetMetaData rsmd rs.getMetaData();int columnCount rsmd.getColumnCount();while (rs.next()) {Map map new HashMap();for (int i 1; i columnCount; i) {map.put(rsmd.getColumnLabel(i), rs.getObject(i));}list.add(map);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {dBhelper.releaseConn();}return list;}public static void main(String[] args) {TestJdbc testJdbc new TestJdbc();User user new User();user.setLoginNum(123456);user.setPassword(123456);List list testJdbc.selectUser(user);}
}debug一下: 然而在实际开发中,我们常常用到的框架是mybatis,其实mybatis就是对jdbc的封装,在我们以后的开发中我们可能会遇到关于持久层的各种问题,我们理解了jdbc的原理,那么mybatis又有何难?
依然以mysql为例,首先,我们来看数据库驱动Driver的加载过程:
在上面的贴出的代码中,我们可以看到一个静态代码块,利用class.forName()加载这个驱动,如下图: private static final String DRIVERNAME com.mysql.cj.jdbc.Driver;/*加载驱动*/static {try {Class.forName(DRIVERNAME).newInstance();} catch (Exception e) {e.printStackTrace();System.out.println(加载驱动失败);}}我们点进这个驱动,发现它继承了一个父类,实现了一个接口,里面有一个方法----注册驱动, 如下图:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException(Cant register driver!);}}
}我们先来看父接口 Driver,如下图: 这是一个顶层接口(由于太长,注释删掉一部分),简单理解一下注释,我们就会明白,这是一个所有的驱动必须实现的方法,也就是说,这是一个java连接数据库的一个接口,一个规范,数据库有很多种,因此数据库驱动也分很多种,但是不管你是那种数据库驱动,必须都要实现这个接口,遵循这个规范,才能连接数据库,无疑给我们带来了很大的方便,更换数据库就代表着更换驱动,而所有驱动都实现了这个接口,那我们只需要在利用反射加载驱动的class.forName()方法中注明需要加载的驱动就ok了,这样就可以适配所有的数据库. /*** 每个驱动程序类必须实现的接口。* PJava SQL 框架允许多个数据库驱动程序。 P* 每个驱动程序都应提供一个实现 * 驱动程序接口的类。* PDriverManager 将尝试加载尽可能多的驱动程序然后对于任何给定的连接请求* 它将依次要求每个 * 驱动程序尝试连接到目标 URL。 P* 强烈建议每个 Driver 类都应该是 * 小且独立的以便 Driver 类可以加载和 * 查询而无需引入大量支持代码。* P加载 Driver 类时它应该创建 * 本身的实例并将其注册到 DriverManager。* 这意味着 * 用户可以通过调用以下命令来加载和注册驱动程序* p * {code Class.forName“foo.bah.Driver”}*/
public interface Driver {/*** 尝试与给定 URL 建立数据库连接。 ** 如果驱动程序意识到连接到给定 URL 的驱动程序类型错误则应返回“null”。* 这很常见因为当 * 要求 JDBC 驱动程序管理器连接到给定的 URL 时* 它会依次将 * URL 传递给每个加载的驱动程序。 **/Connection connect(String url, java.util.Properties info) throws SQLException;/*** 检索驱动程序是否认为它可以打开与给定 URL 的连接 *。 * 通常如果驱动程序 * 理解 URL 中指定的子协议则返回 codetrue/code* 如果 * 不理解则返回 codefalse/code。*/boolean acceptsURL(String url) throws SQLException;DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;int getMajorVersion();int getMinorVersion();boolean jdbcCompliant();public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}再看NonRegisteringDriver类
它实现了Driver,实际上,它是mysql驱动的一部分,里面的一些方法是关于连接mysql数据库的一些配置细节,根据一些连接属性创建一个真正连接数据库的网络通道
public class NonRegisteringDriver implements Driver {public static String getOSName() {return Constants.OS_NAME;}public static String getPlatform() {return Constants.OS_ARCH;}static int getMajorVersionInternal() {return StringUtils.safeIntParse(8);}static int getMinorVersionInternal() {return StringUtils.safeIntParse(0);}public NonRegisteringDriver() throws SQLException {} //接收url,验证url的合法性public boolean acceptsURL(String url) throws SQLException {try {return ConnectionUrl.acceptsUrl(url);} catch (CJException var3) {throw SQLExceptionsMapping.translateException(var3);}}/*** 根据给定的URL和属性信息建立数据库连接。** param url 数据库连接URL用于指定连接的数据库类型和位置。* param info 属性信息包含登录数据库所需的用户名和密码等信息。* return 返回与数据库建立的连接对象如果无法建立连接则返回null。* throws SQLException 如果建立连接过程中发生错误则抛出SQLException。*/public Connection connect(String url, Properties info) throws SQLException {try {try {// 检查URL是否被当前驱动接受如果不接受则直接返回null。if (!ConnectionUrl.acceptsUrl(url)) {return null;} else {// 根据URL和属性信息获取ConnectionUrl实例用于解析连接URL并确定连接类型。//负载均衡式访问ConnectionUrl conStr ConnectionUrl.getConnectionUrlInstance(url, info);// 根据连接类型创建并返回相应的连接对象。switch (conStr.getType()) {case SINGLE_CONNECTION:return ConnectionImpl.getInstance(conStr.getMainHost());case FAILOVER_CONNECTION:case FAILOVER_DNS_SRV_CONNECTION:return FailoverConnectionProxy.createProxyInstance(conStr);case LOADBALANCE_CONNECTION:case LOADBALANCE_DNS_SRV_CONNECTION:return LoadBalancedConnectionProxy.createProxyInstance(conStr);case REPLICATION_CONNECTION:case REPLICATION_DNS_SRV_CONNECTION:return ReplicationConnectionProxy.createProxyInstance(conStr);default:return null;}}} catch (UnsupportedConnectionStringException var5) {// 如果连接字符串不被支持则返回null。return null;} catch (CJException var6) {// 如果在连接过程中发生CJException则将其转换为SQLException并抛出。throw (UnableToConnectException) ExceptionFactory.createException(UnableToConnectException.class, Messages.getString(NonRegisteringDriver.17, new Object[]{var6.toString()}), var6);}} catch (CJException var7) {// 如果发生CJException则将其转换为SQLException并抛出。throw SQLExceptionsMapping.translateException(var7);}}public int getMajorVersion() {return getMajorVersionInternal();}public int getMinorVersion() {return getMinorVersionInternal();}/*** 获取驱动程序属性信息。* 该方法通过分析给定的URL和属性构造驱动程序需要的属性信息。它主要用于配置连接到数据库所需的属性。** param url 数据库连接URL用于解析数据库类型、主机、端口、数据库名称等信息。* param info 已经存在的属性信息可能包含主机、端口、数据库名称、用户和密码等信息。* return DriverPropertyInfo数组包含所有必要的驱动程序属性信息。* throws SQLException 如果解析URL或获取属性信息过程中出现错误则抛出SQLException。*/public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {try {// 初始化数据库连接所需的各个属性String host ;String port ;String database ;String user ;String password ;// 如果URL不为空则尝试解析URL以获取更多配置信息if (!StringUtils.isNullOrEmpty(url)) {ConnectionUrl connStr ConnectionUrl.getConnectionUrlInstance(url, info);// 如果是单个连接配置提取主机信息if (connStr.getType() Type.SINGLE_CONNECTION) {HostInfo hostInfo connStr.getMainHost();// 将主机信息转换为Properties对象info hostInfo.exposeAsProperties();}}// 从info对象中提取出各个属性值if (info ! null) {host info.getProperty(PropertyKey.HOST.getKeyName());port info.getProperty(PropertyKey.PORT.getKeyName());database info.getProperty(PropertyKey.DBNAME.getKeyName());user info.getProperty(PropertyKey.USER.getKeyName());password info.getProperty(PropertyKey.PASSWORD.getKeyName());}// 创建驱动程序属性信息对象并设置相应的属性和描述DriverPropertyInfo hostProp new DriverPropertyInfo(PropertyKey.HOST.getKeyName(), host);hostProp.required true;hostProp.description Messages.getString(NonRegisteringDriver.3);DriverPropertyInfo portProp new DriverPropertyInfo(PropertyKey.PORT.getKeyName(), port);portProp.required false;portProp.description Messages.getString(NonRegisteringDriver.7);DriverPropertyInfo dbProp new DriverPropertyInfo(PropertyKey.DBNAME.getKeyName(), database);dbProp.required false;dbProp.description Messages.getString(NonRegisteringDriver.10);DriverPropertyInfo userProp new DriverPropertyInfo(PropertyKey.USER.getKeyName(), user);userProp.required true;userProp.description Messages.getString(NonRegisteringDriver.13);DriverPropertyInfo passwordProp new DriverPropertyInfo(PropertyKey.PASSWORD.getKeyName(), password);passwordProp.required true;passwordProp.description Messages.getString(NonRegisteringDriver.16);// 初始化JDBC属性集并根据当前配置初始化属性JdbcPropertySet propSet new JdbcPropertySetImpl();propSet.initializeProperties(info);// 将JDBC属性集暴露为驱动程序属性信息ListDriverPropertyInfo driverPropInfo propSet.exposeAsDriverPropertyInfo();// 创建一个包含所有属性信息的数组DriverPropertyInfo[] dpi new DriverPropertyInfo[5 driverPropInfo.size()];// 将基本属性和额外的驱动程序属性信息添加到数组中dpi[0] hostProp;dpi[1] portProp;dpi[2] dbProp;dpi[3] userProp;dpi[4] passwordProp;System.arraycopy(driverPropInfo.toArray(new DriverPropertyInfo[0]), 0, dpi, 5, driverPropInfo.size());// 返回包含所有属性信息的数组return dpi;} catch (CJException var17) {// 将内部CJException转换为SQLException并抛出throw SQLExceptionsMapping.translateException(var17);}}public boolean jdbcCompliant() {return false;}public Logger getParentLogger() throws SQLFeatureNotSupportedException {throw new SQLFeatureNotSupportedException();}static {try {Class.forName(AbandonedConnectionCleanupThread.class.getName());} catch (ClassNotFoundException var1) {}}
}然后就是 DriverManager.registerDriver( new Driver() ) 这个注册驱动的方法,它是将自己传给DriverManager,我们点开这个方法: 这里将Driver封装进DriverInfo类中,添加在DriverManager的静态List中,便于DriverManager管理驱动 /*** class DriverManager* 这段代码定义了一个私有的、静态的、常量registeredDrivers其类型为CopyOnWriteArrayListDriverInfo。这个列表用于存储DriverInfo类型的元素。* CopyOnWriteArrayList是Java并发编程中的一种线程安全的列表实现。它通过使用“写时复制”Copy-on-Write的策略来实现并发访问和修改。当有线程尝试修改列表时会创建该列表的一个副本并在副本上进行修改操作而原列表则保持不变。这样可以确保在并发环境下读操作的高效性和线程安全性。* 在该代码中registeredDrivers列表用于存储DriverInfo类型的元素可以进行元素的添加、删除和查询等操作。由于使用了CopyOnWriteArrayList因此在并发环境下对列表的操作是线程安全的。*/private final static CopyOnWriteArrayListDriverInfo registeredDrivers new CopyOnWriteArrayList();public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {registerDriver(driver, null);}/*** 注册驱动程序* 该函数用于向DriverManager注册给定的 JDBC 驱动程序。* 如果驱动程序已经注册则不会采取任何行动。该方法接受两个参数* driver是要注册的 JDBC 驱动程序* da是当调用DriverManager#deregisterDriver时使用的DriverAction实现。* 如果driver为null则会抛出NullPointerException。* 如果注册成功则会在控制台打印registerDriver:加上驱动程序的信息。* param driver* param da* throws SQLException*/public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {/* 如果驱动程序尚未添加到我们的列表中请注册它 */if (driver ! null) {registeredDrivers.addIfAbsent(new DriverInfo(driver, da));} else {//这是为了与原始 DriverManager 兼容throw new NullPointerException();}println(registerDriver: driver);}再点开: registeredDrivers.addIfAbsent(new DriverInfo(driver, da)) 方法名大概是说 如果缺席(没有)就添加类里面有一个array,看注释,这是一个放置驱动类的临时数组,只能通过getArray和setArray获取和设置.
而 indexOf(e, snapshot, 0, snapshot.length) 0 ? false :addIfAbsent(e, snapshot)是返回参数e 在数组snapshot 的下标,这里的 e 就是 上面的new DriverInfo(driver, da), snapshot 为上面提到的 array 当e为null,返回snapshot中null的下标.如果snapshot中没有e,则返回 -1,也就是说其实是判断snapshot中有没有e,没有的话,就调用方法添加.
再看下面的添加方法,就是把 Driver 放进 array 中,相当于把驱动注册进DriverManager中,至于这里为什么是一个数组?假如我们一个系统同时连接两种或者多种数据库,那我们就需要多个驱动,因此这里是一个数组,当我们需要连接哪种数据库的时候,就可以从这里取出对应的驱动去获取连接
private transient volatile Object[] array;/*** 查找元素在数组中的索引。** param o 要查找的元素可以为null。* param elements 目标数组可能包含null元素。* param index 搜索的起始索引。* param fence 搜索的结束界限但不包括该索引本身。* return 如果找到元素返回其索引如果未找到返回-1。** 方法首先检查要查找的元素是否为null然后遍历数组从指定索引开始直到指定的界限。* 如果元素为null则查找数组中的null元素如果元素不为null则使用equals方法进行匹配查找。* 这种方法允许在数组中高效地查找特定元素无论元素是否为null。*/private static int indexOf(Object o, Object[] elements, int index, int fence) {if (o null) {// 如果要查找的元素为null则在数组中查找null元素。for (int i index; i fence; i) if (elements[i] null) return i;} else {// 如果要查找的元素不为null则使用equals方法进行匹配查找。for (int i index; i fence; i) if (o.equals(elements[i])) return i;}// 如果未找到匹配的元素返回-1。return -1;}/*** 在集合中添加元素e仅当e不存在于集合中时添加。** param e 要添加到集合中的元素。* return 如果元素已存在则返回false如果元素成功添加则返回true。*/public boolean addIfAbsent(E e) {// 获取当前集合的元素数组快照用于后续判断元素是否已存在Object[] snapshot getArray();// 判断元素e是否已存在于集合中if (indexOf(e, snapshot, 0, snapshot.length) 0) {return false; // 元素已存在不添加返回false} else {// 元素不存在调用addIfAbsent方法实际添加元素return addIfAbsent(e, snapshot);}}/*** 该函数在同步锁定的情况下检查给定的元素e是否存在于数组中。* 如果不存在则将e添加到数组中并返回true* 如果存在则返回false。函数首先比较给定的快照数组snapshot和当前数组是否相等* 如果不相等则遍历两个数组中长度较小的部分* 检查e是否已经存在。如果e不存在则继续查找剩余部分。* 如果e仍然不存在则创建一个新数组将e添加到新数组末尾* 然后将新数组设置为当前数组。最后返回true表示成功添加元素。** param e 要添加到集合的元素。* param snapshot 快照数组用于比较以确定元素是否已经存在。* return 如果元素成功添加到集合中则返回true如果元素已经存在则返回false。*/private boolean addIfAbsent(E e, Object[] snapshot) {final ReentrantLock lock this.lock;lock.lock(); // 获取锁以确保线程安全try {Object[] current getArray(); // 获取当前的元素数组int len current.length; // 获取当前数组的长度if (snapshot ! current) { // 检查快照数组是否是当前的元素数组// 针对另一个 addXXX 操作的失败竞争进行了优化 int common Math.min(snapshot.length, len); // 计算快照数组和当前数组的长度的较小值for (int i 0; i common; i) {if (current[i] ! snapshot[i] eq(e, current[i])) {return false; // 如果在相同位置上元素不相同且新元素已存在则返回false}}if (indexOf(e, current, common, len) 0) {return false; // 如果新元素在当前数组的剩余部分中存在则返回false}}Object[] newElements Arrays.copyOf(current, len 1); // 创建一个新数组长度为当前数组长度加一newElements[len] e; // 将新元素添加到新数组的末尾setArray(newElements); // 设置新数组为当前的元素数组return true; // 返回true表示新元素已成功添加} finally {lock.unlock(); // 释放锁}}
我们再看如何获取连接Connection, DriverManager遍历其中的所有的驱动,然后获取该驱动的连接,这种方法或许有点笨,但是可以兼容所有的数据库驱动,而这里真正连接数据库的操作Connection con aDriver.driver.connect(url, info);调用的是NonRegisteringDriver中的connect()方法,返回一个Connection实例 /*** 根据给定的URL、属性和调用者类加载器获取一个数据库连接。* 此方法是getConnection方法的实现它尝试通过已注册的驱动程序来建立连接。* * param url 数据库连接URL不能为null。* param info 连接属性可以为null。* param caller 调用者的类用于获取类加载器可以为null。* return 数据库连接对象。* throws SQLException 如果无法建立连接或URL为null。*/// Worker method called by the public getConnection() methods. private static Connection getConnection(String url, java.util.Properties info, Class? caller) throws SQLException {// 根据调用者类加载器获取合适的类加载器ClassLoader callerCL caller ! null ? caller.getClassLoader() : null;// 确保线程安全地处理类加载器synchronized (DriverManager.class) { // 同步加载正确的类加载器。 if (callerCL null) {callerCL Thread.currentThread().getContextClassLoader();}}// 检查URL是否为nullif (url null) {throw new SQLException(The url cannot be null, 08001);}// 打印获取连接的日志信息println(DriverManager.getConnection(\ url \));SQLException reason null;// 尝试通过每个已注册的驱动程序建立连接for (DriverInfo aDriver : registeredDrivers) {// 检查驱动程序是否允许被调用者使用if (isDriverAllowed(aDriver.driver, callerCL)) {try {// 尝试连接到数据库println( trying aDriver.driver.getClass().getName());Connection con aDriver.driver.connect(url, info);if (con ! null) {// 连接成功返回连接对象println(getConnection returning aDriver.driver.getClass().getName());return (con);}} catch (SQLException ex) {// 记录第一个发生的SQLExceptionif (reason null) {reason ex;}}} else {// 跳过不被允许的驱动程序println( skipping: aDriver.getClass().getName());}}// 如果有SQLException发生抛出该异常if (reason ! null) {println(getConnection failed: reason);throw reason;}// 如果没有找到合适的驱动程序抛出SQLExceptionprintln(getConnection: no suitable driver found for url);throw new SQLException(No suitable driver found for url, 08001);}再来看返回的连接 Connection,这也是一个接口,定义了一些数据库连接都要有的方法会用到的方法,我们看一下它的继承类图,如下:
package java.sql;import java.util.Properties;
import java.util.concurrent.Executor;/*** P与特定 * 数据库的连接会话。在连接上下文中执行 SQL 语句并返回结果 *。* * pre * java.util.Map map con.getTypeMap; * * map.put“mySchemaName.ATHLETES” Class.forName“运动员”; * * con.setTypeMap地图; * /pre * * * see DriverManager#getConnection * * see语句 * * see ResultSet * see DatabaseMetaData*/
public interface Connection extends Wrapper, AutoCloseable {//所有的数据库连接都要有的方法 Statement createStatement() throws SQLException;PreparedStatement prepareStatement(String sql) throws SQLException;CallableStatement prepareCall(String sql) throws SQLException;String nativeSQL(String sql) throws SQLException;void setAutoCommit(boolean autoCommit) throws SQLException;boolean getAutoCommit() throws SQLException;void commit() throws SQLException;void rollback() throws SQLException;void close() throws SQLException;boolean isClosed() throws SQLException;
}public interface MysqlConnection { //略 // 关于mysql连接的一些方法
}public interface JdbcConnection extends Connection, MysqlConnection, TransactionEventHandler {//略
}public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable {//略
}根据上面的分析如图所示
JDBC 的 Driver 接口如果从桥接模式来看Driver 就是一个接口下面可以有 MySQL 的 Driver、Oracle 的 Driver这些就可以当做实现接口类 Connection 继承体系 Driver源码
public class Driver extends NonRegisteringDriver implements java.sql.Driver {static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException(Cant register driver!);}}public Driver() throws SQLException {// Required for Class.forName().newInstance()}
}DriverManager 结构 说明
MySQL 有自己的 Connectionlmpl 类同样 Oracle 也有对应的实现类Driver 和 Connection 之间是通过 DriverManager 类进行桥连接的
5、注意事项和细节
实现了抽象和实现部分的分离从而极大的提供了系统的灵活性让抽象部分和实现部分独立开来。这有助于系统进行分层设计从而产生更好的结构化系统对于系统的高层部分只需要知道抽象部分和实现部分的接口就可以了其它的部分由具体业务来完成桥接模式替代多层继承方案可以减少子类的个数降低系统的管理和维护成本桥接模式的引入增加了系统的理解和设计难度由于聚合关联关系建立在抽象层要求开发者针对抽象进行设计和编程桥接模式要求正确识别出系统中两个独立变化的维度因此其使用范围有一定的后限性即需要有这样的应用场景
6、桥接模式其他应用场景
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统桥接模式尤为适用
常见的应用场景
JDBC 驱动程序银行转账系统 转账分类网上转账、柜台转账、AMT 转账转账用户类型普通用户、银卡用户、金卡用户 消息管理 消息类型即时消息、延时消息消息分类手机短信、邮件消息、QQ消息…