Spring 實現兩種設計模式:工廠模式和單態模式(單例模式)


本文摘自:李剛 著 《輕量級 Java EE企業應用實戰 Struts2+Spring+hibernate整合開發》

        在Spring 中大量使用的以下兩種設計模式:工廠模式單態模式

工廠模式可將Java 對象的調用者從被調用者的實現邏輯中分離出來,調用者只需關心被調用者必須滿足的規則(接口) ,而不必關心實例的具體實現過程。這是面向接口編程的優勢,能提高程序的解耦,避免所有的類以硬編碼方式耦合在一起。

        如果所有的類直接耦合,極易形成"骨牌效應",假如B 類調用了A 類,一旦A 類需要修改,則B 類也需要修改;假如C 類調用了B 類,則C 類也需要修改......依次類推,從而導致整個系統都需要改寫。造成"牽一發而動全身",而系統重構的代價是相當高的。

        Spring 倡導”面向接口編程“,可以避免上述的問題,使設計良好的架構可保證系統重構的工作被封閉在重構的層內,絕不會影響其他層。

        Spring 容器是實例化和管理全部bean 的工廠,Spring 默認將所有的bean 設置成單態模式,無須自己完成單態模式,即對所有相同id 的bean 請求都將返回同一個共享實例。因此,單態模式可大大降低Java 對象在創建和銷毀時的系統開銷。

一. 單態模式的回顧

        單態模式限制了類實例的創建,但采用這種模式設計的類,可以保證僅有一個實例,並可提供訪問該實例的全局訪問點。J2EE應用的大量組件,都需要保證一個類只有一個實例。比如數據庫引擎訪問點只能有一個。

        更多的時候,為了提高性能,程序應盡量減少Java 對象的創建和銷毀時的開銷。使用單態模式可避免Java 類被多次實例化,讓相同類的全部實例共享同一內存區。

        為了防止單態模式的類被多次實例化,應將類的構造器設成私有,這樣就保證了只能通過靜態方法獲得類實例。而該靜態方法則保證每次返回的實例都是同一個,這就需將該類的實例設置成類屬性,由於該屬性需要被靜態方法訪問,因此該屬性應設成靜態屬性。

下面給出單態模式的示例代碼:

  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. }  

        從程序最后的打印結果可以看出,該類的兩個實例完全相同。這證明單態模式類的全部實例是同一共享實例。程序里雖然獲得了類的兩個實例,但實際上只執行一次構造器,因為對於單態模式的類,無論有多少次的創建實例請求,都只執行一次構造器。

 

二. 工廠模式的回顧

 

        工廠模式是根據調用數據返回某個類的一個實例,此類可以是多個類的某一個類。通常,這些類滿足共同的規則(接口)或父類。調用者只關心工廠生產的實例是否滿足某種規范,即實現的某個接口是否可供自己正常調用(調用者僅僅使用)。該模式給對象之間作出了清晰的角色划分,降低程序的耦合。

        接口產生的全部實例通常用於實現相同接口,接口里定義了全部實例共同擁有的方法,這些方法在不同的實現類中實現的方式不同。從而使程序調用者無須關心方法的具體實現,降低了系統異構的代價。

下面是工廠模式的示例代碼:

  1. package ppp;  
  2.   
  3. //Person接口定義  
  4. public interface Person {  
  5.     public String sayHello(String name);  
  6.     public String sayGoodbye(String name);  
  7. }  

該接口定義了Person規范,規范要求實現該接口的類必須具有以下兩個的方法:能打招呼,能告別。

  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

  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工廠的代碼:

  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. }  

以上是最簡單的工廠模式框架,其主程序如下:

  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 工廠負責生成和管理。

下面是關於兩個實例的配置文件:

  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>  

主程序部分如下:

  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完全可以提供所有工廠模式的功能。

       下面對主程序部分進行簡單的修改:   

  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之間的依賴關系。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: