设计模式在ssh中的典型应用

单例模式:数据库连接池的设计一般采用单例模式,数据库连接是一种数据库资源。软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的。当然,使用数据库连接池还有很多其它的好处,可以屏蔽不同数据数据库之间的差异,实现系统对数据库的低度耦合,也可以被多个系统同时使用,具有高可复用性,还能方便对数据库连接的管理等等。数据库连接池属于重量级资源,一个应用中只需要保留一份即可,既节省了资源又方便管理。所以数据库连接池采用单例模式进行设计会是一个非常好的选择。


 在Spring 中大量使用的以下两种设计模式:工厂模式单态模式

工厂模式可将Java 对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则(接口) ,而不必关心实例的具体实现过程。这是面向接口编程的优势,能提高程序的解耦,避免所有的类以硬编码方式耦合在一起。

        如果所有的类直接耦合,极易形成"骨牌效应",假如B 类调用了A 类,一旦A 类需要修改,则B 类也需要修改;假如C 类调用了B 类,则C 类也需要修改......依次类推,从而导致整个系统都需要改写。造成"牵一发而动全身",而系统重构的代价是相当高的。

        Spring 倡导”面向接口编程“,可以避免上述的问题,使设计良好的架构可保证系统重构的工作被封闭在重构的层内,绝不会影响其他层。

        Spring 容器是实例化和管理全部bean 的工厂,Spring 默认将所有的bean 设置成单态模式,无须自己完成单态模式,即对所有相同id 的bean 请求都将返回同一个共享实例。因此,单态模式可大大降低Java 对象在创建和销毁时的系统开销。

 

一. 单态模式的回顾


        单态模式限制了类实例的创建,但采用这种模式设计的类,可以保证仅有一个实例,并可提供访问该实例的全局访问点。J2EE应用的大量组件,都需要保证一个类只有一个实例。比如数据库引擎访问点只能有一个。

        更多的时候,为了提高性能,程序应尽量减少Java 对象的创建和销毁时的开销。使用单态模式可避免Java 类被多次实例化,让相同类的全部实例共享同一内存区。

        为了防止单态模式的类被多次实例化,应将类的构造器设成私有,这样就保证了只能通过静态方法获得类实例。而该静态方法则保证每次返回的实例都是同一个,这就需将该类的实例设置成类属性,由于该属性需要被静态方法访问,因此该属性应设成静态属性。

下面给出单态模式的示例代码:

[java]  view plain copy
  1. package ppp;  
  2. //单态模式测试类  
  3. public class SingletonTest {  
  4.     //该类的一个普通属性  
  5.     int value;  
  6.     //使用静态属性保存该类的一个实例  
  7.     private static SingletonTest instance;  
  8.     //构造器私有化,避免该类被多次实例化  
  9.     private SingletonTest(){  
  10.         System.out.println("正在执行构造器...");  
  11.     }  
  12.     //提供静态方法返回该类实例   
  13.     public static SingletonTest getInstance(){  
  14.         //实例化类实例前,先检查该实例是否存在  
  15.         if(instance == null){  
  16.             //如果不存在,则新建一个实例  
  17.             instance = new SingletonTest();  
  18.         }  
  19.         //返回该类的成员变量:该类的实例   
  20.         return instance;      
  21.     }  
  22.     //以下提供对普通属性value的getter和setter方法  
  23.     public int getValue(){  
  24.         return value;  
  25.     }  
  26.   
  27.     public void setValue(int values){  
  28.         this.value = values;      
  29.     }  
  30.     public static void main(String args[]){  
  31.         SingletonTest t1 = SingletonTest.getInstance();  
  32.         SingletonTest t2 = SingletonTest.getInstance();  
  33.         t2.setValue(9);  
  34.         System.out.println(t1 == t2);  
  35.     }  
  36. }  

        从程序最后的打印结果可以看出,该类的两个实例完全相同。这证明单态模式类的全部实例是同一共享实例。程序里虽然获得了类的两个实例,但实际上只执行一次构造器,因为对于单态模式的类,无论有多少次的创建实例请求,都只执行一次构造器。

 

二. 工厂模式的回顾


        工厂模式是根据调用数据返回某个类的一个实例,此类可以是多个类的某一个类。通常,这些类满足共同的规则(接口)或父类。调用者只关心工厂生产的实例是否满足某种规范,即实现的某个接口是否可供自己正常调用(调用者仅仅使用)。该模式给对象之间作出了清晰的角色划分,降低程序的耦合。

        接口产生的全部实例通常用于实现相同接口,接口里定义了全部实例共同拥有的方法,这些方法在不同的实现类中实现的方式不同。从而使程序调用者无须关心方法的具体实现,降低了系统异构的代价。

下面是工厂模式的示例代码:

[java]  view plain copy
  1. package ppp;  
  2.   
  3. //Person接口定义  
  4. public interface Person {  
  5.     public String sayHello(String name);  
  6.     public String sayGoodbye(String name);  
  7. }  

该接口定义了Person规范,规范要求实现该接口的类必须具有以下两个的方法:能打招呼,能告别。

[java]  view plain copy
  1. package ppp;  
  2. //American类实现Person接口  
  3. public class American implements Person {  
  4.     public String sayHello(String name){  
  5.         return name+",hello";  
  6.     }  
  7.     public String sayGoodbye(String name)  
  8.     {  
  9.         return name+",goodbye";  
  10.     }  
  11. }  

下面是Person类的另一个实现类:Chinese

[java]  view plain copy
  1. package ppp;    
  2. //Chinese类实现Person接口  
  3. public class Chinese implements Person {  
  4.     public String sayHello(String name){  
  5.         return name+",您好";  
  6.     }  
  7.     public String sayGoodbye(String name)  
  8.     {  
  9.         return name+",下次再见";  
  10.     }  
  11. }  

然后再看Person工厂的代码:

[java]  view plain copy
  1. package ppp;  
  2.   
  3. public class PersonFactory {  
  4.     public Person getPerson(String ethnic)  
  5.     {  
  6.         if(ethnic.equalsIgnoreCase("chin"))  
  7.         {  
  8.             return new Chinese();  
  9.         }else{  
  10.             return new American();        
  11.         }  
  12.     }  
  13. }  

以上是最简单的工厂模式框架,其主程序如下:

[java]  view plain copy
  1. package ppp;  
  2.   
  3. public class FactoryTest {  
  4.     public static void main(String[] args){  
  5.         //创建PersonFactory实例 ,获得工厂实例   
  6.         PersonFactory pf = new PersonFactory();  
  7.         //定义接口Person实例,面向接口编程   
  8.         Person p = null;  
  9.         //使用工厂获得person实例  
  10.         p = pf.getPerson("chin");  
  11.         //下面调用Person接口方法  
  12.         System.out.println(p.sayHello("wawa"));  
  13.         System.out.println(p.sayGoodbye("wawa"));  
  14.         //使用工厂获得Person的另一个实例  
  15.         p = pf.getPerson("ame");  
  16.         //再次调用Person接口的方法  
  17.         System.out.println(p.sayHello("wawa"));  
  18.         System.out.println(p.sayGoodbye("wawa"));  
  19.     }  
  20. }  

由此可看出,主程序从Person 接口的具体类中解耦出来,而且程序调用者无须关心Person 的实例化过程,主程序仅仅与工厂服务定位结合在一起,可获得所有工厂能产生的实例。具体类的变化,接口无须发生任何改变,调用者程序代码部分也无须发生任何改动。
下面是Spring 对这两种模式的实现。

 

三. Spring 对单态与工厂模式的实现


        随着Spring 提供工厂模式的实现,在使用Spring 时,无须自己提供工厂类。因为Spring容器是最大的工厂,而且是个功能超强的工厂。Spring 使用配置文件管理所有的bean ,其配置文件中bean 由Spring 工厂负责生成和管理。

下面是关于两个实例的配置文件:

[html]  view plain copy
  1. <!--下面是xml文件的文件头-->  
  2. <?xml version = "1.0" encoding = "gb2312"?>  
  3. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
  4.    "http://www.springsource.org/dtd/spring-beans.dtd">  
  5. <!--beans是Spring配置文件的根元素-->  
  6. <beans>  
  7. <!--定义第一个bean,该bean的id是chinese-->  
  8. <bean id = "chinese" class = "ppp.Chinese"/>  
  9. <!--定义第二个bean,该bean的id是American-->  
  10. <bean id = "american" class = "ppp.American"/>  
  11. </beans>  

主程序部分如下:

[java]  view plain copy
  1. package ppp;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.FileSystemXmlApplicationContext;  
  5. public class SpringTest {  
  6.     public static void main(String[] args) {  
  7.         //实例化Spring容器  
  8.         ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");  
  9.         //定义Person接口实例  
  10.         Person p = null;  
  11.         //通过Spring上下文获得Chinese实例  
  12.         p = (Person)ctx.getBean("chinese");  
  13.         //执行chinese实例的方法  
  14.         System.out.println(p.sayHello("wawa"));  
  15.         System.out.println(p.sayGoodbye("wawa"));  
  16.           
  17.         p = (Person)ctx.getBean("american");  
  18.         System.out.println(p.sayHello("wawa"));  
  19.         System.out.println(p.sayGoodbye("wawa"));  
  20.     }  
  21. }  

       使用Spring 时,即使没有工厂类PersonFactory ,程序一样可以使用工厂模式, Spring完全可以提供所有工厂模式的功能。

       下面对主程序部分进行简单的修改:   

[java]  view plain copy
  1. package ppp;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.support.FileSystemXmlApplicationContext;  
  5. public class SpringTest{  
  6.      public static void main(String[] args){  
  7.          //实例化Spring容器  
  8.          ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");  
  9.          //定义p1接口的实例p1  
  10.          Person p1 = null;  
  11.          //通过Spring上下文获得Chinese实例  
  12.          p1 = (Person)ctx.getBean("Chinese");  
  13.          //定义p2接口的实例p2  
  14.          Person p2 = null;  
  15.          p2 = (Person)ctx.getBean("Chinese");  
  16.          System.out.println(p1 == p2);  
  17.      }  
  18. }  

          程序的执行结果是:true

        表明Spring对接受容器管理的全部的bean,默认采用单态模式管理,建议不要随意更改bean的行为方式。因为从性能上讲,单态的bean比非单态的bean性能更为优秀。

        仔细检查上面的代码就会发现如下的特点;

        (1)除测试部分的主程序外,代码并未出现Spring的特定类和接口。

        (2)调用者的代码,也就是测试用的主程序部分,仅仅面向Person的接口编程,而无需知道实现类的具体名称。同时,通过修改配置文件来彻底切换底层的具体实现类。

        (3)由于厂无需多个实例,因此工厂应该采用单态模式设计,其中Spring上下文也就是Spring工厂,已被设计成单态。

          Spring工厂模式,不仅提供了创建bean的功能,还提供了对bean的生命周期的管理。最重要的是还以管理bean和bean之间的依赖关系。

 

hibernate 使用的设计模式(一) 观察者

 以hibernate.get(String, Serializable)方法为例:

  get()的方法实现为:

   图1:

Java代码   收藏代码
  1. public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {  
  2.         LoadEvent event = new LoadEvent(id, entityName, lockMode, this);  
  3.         fireLoad(event, LoadEventListener.GET);  
  4.         return event.getResult();  
  5.     }  
  6.   
  7.     private void fireLoad(LoadEvent event, LoadType loadType) {  
  8.         errorIfClosed();  
  9.         checkTransactionSynchStatus();  
  10.         LoadEventListener[] loadEventListener = listeners.getLoadEventListeners();  
  11.         for ( int i = 0; i < loadEventListener.length; i++ ) {  
  12.             loadEventListener[i].onLoad(event, loadType);  
  13.         }  
  14.     }  

   图2:

Java代码   收藏代码
  1. public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException {  
  2.   
  3.     final SessionImplementor source = event.getSession();  
  4.   
  5.     EntityPersister persister;  
  6.     if ( event.getInstanceToLoad() != null ) {  
  7.         persister = source.getEntityPersister( null, event.getInstanceToLoad() ); //the load() which takes an entity does not pass an entityName  
  8.         event.setEntityClassName( event.getInstanceToLoad().getClass().getName() );  
  9.     }  
  10.     else {  
  11.         persister = source.getFactory().getEntityPersister( event.getEntityClassName() );  
  12.     }  
  13.   
  14.     if ( persister == null ) {  
  15.         throw new HibernateException(  
  16.                 "Unable to locate persister: " +  
  17.                 event.getEntityClassName()  
  18.             );  
  19.     }  
  20.   
  21.     if ( persister.getIdentifierType().isComponentType() && EntityMode.DOM4J == event.getSession().getEntityMode() ) {  
  22.         // skip this check for composite-ids relating to dom4j entity-mode;  
  23.         // alternatively, we could add a check to make sure the incoming id value is  
  24.         // an instance of Element...  
  25.     }  
  26.     else {  
  27.         Class idClass = persister.getIdentifierType().getReturnedClass();  
  28.         if ( idClass != null && ! idClass.isInstance( event.getEntityId() ) ) {  
  29.             throw new TypeMismatchException(  
  30.                     "Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass + ", got " + event.getEntityId().getClass()  
  31.             );  
  32.         }  
  33.     }  
  34.   
  35.     EntityKey keyToLoad = new EntityKey( event.getEntityId(), persister, source.getEntityMode()  );  
  36.   
  37.     try {  
  38.         if ( loadType.isNakedEntityReturned() ) {  
  39.             //do not return a proxy!  
  40.             //(this option indicates we are initializing a proxy)  
  41.             event.setResult( load(event, persister, keyToLoad, loadType) );  
  42.         }  
  43.         else {  
  44.             //return a proxy if appropriate  
  45.             if ( event.getLockMode() == LockMode.NONE ) {  
  46.                 event.setResult( proxyOrLoad(event, persister, keyToLoad, loadType) );  
  47.             }  
  48.             else {  
  49.                 event.setResult( lockAndLoad(event, persister, keyToLoad, loadType, source) );  
  50.             }  
  51.         }  
  52.     }  
  53.     catch(HibernateException e) {  
  54.         log.info("Error performing load command", e);  
  55.         throw e;  
  56.     }  
  57. }  

 

   如代码所示:当执行get()方法时,首先创建一个loadEvent,然后触发加载操作,获取对应listeners做出对应的响应;

   SessionImpl为被观察者, listers为观察者, event为事件, event中包含了事件相关的属性。

ps:图1中为SessionImpl中的代码(SessionImpl为session的实现类), 图2为DefaultLoadEventListener的代码(DefaultLoadEventListener为LoadEventListener的实现类)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值