由JDBC数据库连接所联想到的5种设计模式(转)

最近在看设计模式方面的资料.突发灵感,从数据库的连接中联想到了5种设计模式.然后编写了下,都能实现,可能有些实现方式在实际生产环境中并没有意义.就当是对设计模式的学习吧.
     首先上演的就是策略模式.我们在连接数据库时,并非一种数据库,比如,有时是MySql,有时是Oracle,有时又换到SQL Server,都要涉及数据库的切换.此时.我们就可以将数据库连接的算法封装起来,使它们可以相互替换.
     首先我们定义一个策略接口,用来表示数据库的连接.

Java代码 复制代码
  1. package strategy;   
  2.   
  3. public interface Strategy {   
  4.   
  5.     public void getConnDB();   
  6.   
  7. }  
package strategy;

public interface Strategy {

	public void getConnDB();

}


     然后我们实现了对具体的策略类:三大数据库的连接.我们在这里只是强调模式的实现,简单起见,不实现JDBC的连接的具体操作.下面的也是一样.
Mysql:

Java代码 复制代码
  1. public class MysqlStrategy implements Strategy {   
  2.     public void getConnDB() {   
  3.         /*try {  
  4.             Class.forName("com.mysql.jdbc.Driver").newInstance();  
  5.             String url = "jdbc:mysql://localhost/myDB?user=root&password=123456&useUnicode=true&characterEncoding=utf-8";  
  6.             Connection connection = DriverManager.getConnection(url);  
  7.         } catch (SQLException e) {  
  8.             e.printStackTrace();  
  9.         } catch (InstantiationException e) {  
  10.             e.printStackTrace();  
  11.         } catch (IllegalAccessException e) {  
  12.             e.printStackTrace();  
  13.         } catch (ClassNotFoundException e) {  
  14.             e.printStackTrace();  
  15.         }*/  
  16.         System.out.println("connect MySQL");   
  17.     }   
  18. }  
public class MysqlStrategy implements Strategy {
	public void getConnDB() {
		/*try {
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			String url = "jdbc:mysql://localhost/myDB?user=root&password=123456&useUnicode=true&characterEncoding=utf-8";
			Connection connection = DriverManager.getConnection(url);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}*/
		System.out.println("connect MySQL");
	}
}


Oracle:

Java代码 复制代码
  1. public class OracleStrategy implements Strategy {   
  2.        
  3.     public void getConnDB(){   
  4.            
  5.         System.out.println("connect  oracle");   
  6.            
  7.     }   
  8.   
  9. }  
public class OracleStrategy implements Strategy {
	
	public void getConnDB(){
		
		System.out.println("connect  oracle");
		
	}

}


SQL Server:

Java代码 复制代码
  1. public class SQLStrategy  implements Strategy{   
  2.     public void getConnDB(){   
  3.             System.out.println("connect  SQL SERVER");   
  4.         }   
  5.     }  
public class SQLStrategy  implements Strategy{
	public void getConnDB(){
			System.out.println("connect  SQL SERVER");
		}
	}


策略应用场景,方便在运行时动态选择具体要执行的行为.

Java代码 复制代码
  1. public class ClientContext {   
  2.     Strategy strategy;   
  3.        
  4.     public ClientContext(Strategy strategy){   
  5.            
  6.         this.strategy=strategy;   
  7.     }   
  8.        
  9.     public void getConnDB(){   
  10.         strategy.getConnDB();   
  11.     }   
  12.   
  13. }  
public class ClientContext {
	Strategy strategy;
	
	public ClientContext(Strategy strategy){
		
		this.strategy=strategy;
	}
	
	public void getConnDB(){
		strategy.getConnDB();
	}

}


下面就开始测试了:

Java代码 复制代码
  1. public class StrategyTest {   
  2.     public static void main(String[] args) {   
  3.         /**  
  4.          * 策略模式实现对Oracle的连接操作.  
  5.          */  
  6.         ClientContext occ = new ClientContext(new OracleStrategy());   
  7.         occ.getConnDB();   
  8.         /**  
  9.          * 策略模式实现对Mysql的连接操作.  
  10.          */  
  11.         ClientContext mcc = new ClientContext(new MysqlStrategy());   
  12.         mcc.getConnDB();   
  13.         /**  
  14.          * 策略模式实现对SQL Server的连接操作.  
  15.          */  
  16.         ClientContext scc = new ClientContext(new SQLStrategy());   
  17.         scc.getConnDB();   
  18.   
  19.     }   
  20.   
  21. }  
public class StrategyTest {
	public static void main(String[] args) {
		/**
		 * 策略模式实现对Oracle的连接操作.
		 */
		ClientContext occ = new ClientContext(new OracleStrategy());
		occ.getConnDB();
		/**
		 * 策略模式实现对Mysql的连接操作.
		 */
		ClientContext mcc = new ClientContext(new MysqlStrategy());
		mcc.getConnDB();
		/**
		 * 策略模式实现对SQL Server的连接操作.
		 */
		ClientContext scc = new ClientContext(new SQLStrategy());
		scc.getConnDB();

	}

}


    这样,就基本完成通过策略模式动态切换数据库连接的算法.如果想实现对DB2,Sybase,PostgreSQL数据库的操作.只需实现策略接口即可.这样就可以任意扩展.同时对客户(StrategyTest 类)隐藏具体策略(算法)的实现细节,彼此完全独立。完全做到高内聚,低耦合.

     到这里,突然改变需求,需要在数据库连接之前增加一些日志的打印输出.按照传统的做法,我们修改每一个具体的实现类,增加这些功能,如果先前我们实现的具体类非常多.这无异是一笔不小的负担.而且也违反了开闭原则(OCP).
    这里大家可能想到在学习Spring AOP常提到的用AOP打印日志的情景.AOP主要用到了代理模式.这里我们也通过代理模式来实现.由于抽象策略类是接口,所以我们采用JAVA 反射中提供的代理.
   代理模式具体实现:

Java代码 复制代码
  1. public class ProxyDB implements InvocationHandler {   
  2.   
  3.     private Object target;   
  4.   
  5.     public ProxyDB(Object target) {   
  6.         this.target = target;   
  7.     }   
  8.   
  9.     /**  
  10.      * 此处为我们要额外添加的方法 进行日志的打印输出  
  11.      */  
  12.     public void printLog() {   
  13.         System.out.println("---------------打印输出点日志----------");   
  14.     }   
  15.   
  16.     /**  
  17.      * 代理业务处理器  
  18.      */  
  19.     public Object invoke(Object proxy, Method method, Object[] args)   
  20.             throws Exception {   
  21.         printLog();   
  22.         return method.invoke(this.target, args);   
  23.     }   
  24.   
  25.     public static void main(String[] args) {   
  26.         /**  
  27.          *   
  28.          * 通过代理模式在连接Mysql前增加日志的打印输出;  
  29.          *   
  30.          */  
  31.         MysqlStrategy ms = new MysqlStrategy();   
  32.         ProxyDB proxyCorps = new ProxyDB(ms);   
  33.         Strategy realObject = (Strategy) Proxy.newProxyInstance(ms.getClass()   
  34.                 .getClassLoader(), ms.getClass().getInterfaces(), proxyCorps);   
  35.         realObject.getConnDB();   
  36.   
  37.         /**  
  38.          * 通过代理模式在连接 Oracle前增加日志的打印输出;  
  39.          *   
  40.          */  
  41.         OracleStrategy os = new OracleStrategy();   
  42.         ProxyDB proxyCorps1 = new ProxyDB(os);   
  43.         Strategy realObject1 = (Strategy) Proxy.newProxyInstance(os.getClass()   
  44.                 .getClassLoader(), os.getClass().getInterfaces(), proxyCorps1);   
  45.         realObject1.getConnDB();   
  46.   
  47.         /**  
  48.          * 通过代理模式在连接 SQL Server前增加日志的打印输出;  
  49.          *   
  50.          */  
  51.         SQLStrategy ss = new SQLStrategy();   
  52.         ProxyDB proxyCorps2 = new ProxyDB(ss);   
  53.         Strategy realObject2 = (Strategy) Proxy.newProxyInstance(ss.getClass()   
  54.                 .getClassLoader(), ss.getClass().getInterfaces(), proxyCorps2);   
  55.         realObject2.getConnDB();   
  56.   
  57.     }   
  58.   
  59. }  
public class ProxyDB implements InvocationHandler {

	private Object target;

	public ProxyDB(Object target) {
		this.target = target;
	}

	/**
	 * 此处为我们要额外添加的方法 进行日志的打印输出
	 */
	public void printLog() {
		System.out.println("---------------打印输出点日志----------");
	}

	/**
	 * 代理业务处理器
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Exception {
		printLog();
		return method.invoke(this.target, args);
	}

	public static void main(String[] args) {
		/**
		 * 
		 * 通过代理模式在连接Mysql前增加日志的打印输出;
		 * 
		 */
		MysqlStrategy ms = new MysqlStrategy();
		ProxyDB proxyCorps = new ProxyDB(ms);
		Strategy realObject = (Strategy) Proxy.newProxyInstance(ms.getClass()
				.getClassLoader(), ms.getClass().getInterfaces(), proxyCorps);
		realObject.getConnDB();

		/**
		 * 通过代理模式在连接 Oracle前增加日志的打印输出;
		 * 
		 */
		OracleStrategy os = new OracleStrategy();
		ProxyDB proxyCorps1 = new ProxyDB(os);
		Strategy realObject1 = (Strategy) Proxy.newProxyInstance(os.getClass()
				.getClassLoader(), os.getClass().getInterfaces(), proxyCorps1);
		realObject1.getConnDB();

		/**
		 * 通过代理模式在连接 SQL Server前增加日志的打印输出;
		 * 
		 */
		SQLStrategy ss = new SQLStrategy();
		ProxyDB proxyCorps2 = new ProxyDB(ss);
		Strategy realObject2 = (Strategy) Proxy.newProxyInstance(ss.getClass()
				.getClassLoader(), ss.getClass().getInterfaces(), proxyCorps2);
		realObject2.getConnDB();

	}

}


    

    难道只能通过代理来增加日志功能?这时又突然想到装饰者(Decorator),它本来的目的就是对类的实例追加或扩展附加功能.它可以动态的改变一个对象方法的行为.同时也满足设计原则中的开闭原则.
     装饰者模式
(Decorator):

Java代码 复制代码
  1. public class Decorator implements Strategy {   
  2.   
  3.     private Strategy strategy;   
  4.   
  5.     public Decorator(Strategy strategy) {   
  6.         this.strategy = strategy;   
  7.     }   
  8.   
  9.     /**  
  10.      * 额外增加的功能,  
  11.      * 通过装饰者模式动态改变原来对象方法的行为.  
  12.      */  
  13.     public void printLog() {   
  14.         System.out.println("---------------先打印输出点日志----------");   
  15.     }   
  16.   
  17.     public void getConnDB() {   
  18.         printLog();   
  19.         strategy.getConnDB();   
  20.     }   
  21.   
  22.     public static void main(String[] args) {   
  23.            
  24.         /**  
  25.          * 在Oracle连接前增加日志的输出  
  26.          */  
  27.         Decorator decorator = new Decorator(new OracleStrategy());   
  28.         decorator.getConnDB();   
  29.         /**  
  30.          * 在Mysql连接前增加日志的输出  
  31.          */  
  32.         Decorator decorator1 = new Decorator(new MysqlStrategy());   
  33.         decorator1.getConnDB();   
  34.         /**  
  35.          * 在Mysql连接前增加日志的输出  
  36.          */  
  37.         Decorator decorator2 = new Decorator(new SQLStrategy());   
  38.         decorator2.getConnDB();   
  39.     }   
  40. }  
public class Decorator implements Strategy {

	private Strategy strategy;

	public Decorator(Strategy strategy) {
		this.strategy = strategy;
	}

	/**
	 * 额外增加的功能,
	 * 通过装饰者模式动态改变原来对象方法的行为.
	 */
	public void printLog() {
		System.out.println("---------------先打印输出点日志----------");
	}

	public void getConnDB() {
		printLog();
		strategy.getConnDB();
	}

	public static void main(String[] args) {
		
		/**
		 * 在Oracle连接前增加日志的输出
		 */
		Decorator decorator = new Decorator(new OracleStrategy());
		decorator.getConnDB();
		/**
		 * 在Mysql连接前增加日志的输出
		 */
		Decorator decorator1 = new Decorator(new MysqlStrategy());
		decorator1.getConnDB();
		/**
		 * 在Mysql连接前增加日志的输出
		 */
		Decorator decorator2 = new Decorator(new SQLStrategy());
		decorator2.getConnDB();
	}
}


       呵呵, 这里大家可以比较一下代理模式和装饰者模式的区别.

    有时候,为了安全起见,我们并不像暴露每个具体的数据库连接类给客户.减少客户与后台具体类的依赖关系.提高子系统的独立性和可移植性. 这是需要我们提供一个简单接口,统一跟外界打交道.这时,就该我们的门面模式(Facade)上场了.

Facade外观模式
    为子系统中的一组接口提供一个统一接口。Facade模式定义了一个更高层的接口,使子系统更加容易使用。在构建一个层次化的系统的时候,可以使用Facade模式定义系统的每一层的入口,如果层与层之间是相互依赖的,则可以限定他们仅通过Facade进行通信,从而简化了层与层之间的依赖关系。
    同时也实现了软件设计原则中的迪米特法则(LCP).

Java代码 复制代码
  1. public class Facade {   
  2.   
  3.     /**  
  4.      *通过连接操作,提供一个统一的接口,统一跟外界打交道  
  5.      *减少对具体实现类的依赖.  
  6.      *  
  7.      */  
  8.     public void getConn() {   
  9.         OracleStrategy os = new OracleStrategy();   
  10.         MysqlStrategy ms =  new MysqlStrategy();   
  11.         SQLStrategy ss =    new SQLStrategy();   
  12.   
  13.         os.getConnDB();   
  14.         ms.getConnDB();   
  15.         ss.getConnDB();   
  16.     }   
  17.   
  18.     public static void main(String[] args) {   
  19.            
  20.         new Facade().getConn();   
  21.     }   
  22. }   
  23.    
public class Facade {

	/**
	 *通过连接操作,提供一个统一的接口,统一跟外界打交道
	 *减少对具体实现类的依赖.
	 *
	 */
	public void getConn() {
		OracleStrategy os = new OracleStrategy();
		MysqlStrategy ms =  new MysqlStrategy();
		SQLStrategy ss =    new SQLStrategy();

		os.getConnDB();
		ms.getConnDB();
		ss.getConnDB();
	}

	public static void main(String[] args) {
		
		new Facade().getConn();
	}
}
 



      其实JAVA 中的JDBC数据库连接本身也采用了门面模式.
     最后一个想到的模式那就是模板模式.这个就没有代码了,大家可以参考Spring所提供的jdbc抽象框架.其核心就是JDBC模板.可能与最开始的想法偏离,就当是一种发散性思维吧.
      最后来个简单总结吧,主要实现了五种模式:策略模式,代理模式,装饰者模式,门面模式和模板模式.两大原则:开闭原则和迪米特法则.至于两大原则
    策略模式  :主要强调算法的封装和自由切换.
    代理模式  :为其他对象提供一种代理以控制对这个对象的访问.
    装饰者模式:动态添加一个类的功能,实现比继承更灵活的扩展方式.
    门面模式  :系统对外提供一个统一简单的接口,减少系统间的耦合性.
    模板模式  :将代码的公共行为提取出来,达到复用的目的.
讲得比较简单,就当是抛砖引玉.望大家使劲拍砖.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值