Spring實現兩種設計模式:工廠模式和單態模式
工廠模式可將Java 物件的呼叫者從被呼叫者的實現邏輯中分離出來,呼叫者只需關心被呼叫者必須滿足的規則(介面) ,而不必關心例項的具體實現過程。這是面向介面程式設計的優勢,能提高程式的解耦,避免所有的類以硬編碼方式耦合在一起。
如果所有的類直接耦合,極易形成"骨牌效應",假如B 類呼叫了A 類,一旦A 類需要修改,則B 類也需要修改;假如C 類呼叫了B 類,則C 類也需要修改......依次類推,從而導致整個系統都需要改寫。造成"牽一髮而動全身",而系統重構的代價是相當高的。
Spring 倡導”面向介面程式設計“,可以避免上述的問題,使設計良好的架構可保證系統重構的工作被封閉在重構的層內,絕不會影響其他層。
Spring 容器是例項化和管理全部bean 的工廠,Spring 預設將所有的bean 設定成單態模式,無須自己完成單態模式,即對所有相同id 的bean 請求都將返回同一個共享例項。因此,單態模式可大大降低Java 物件在建立和銷燬時的系統開銷。
一. 單態模式的回顧
單態模式限制了類例項的建立,但採用這種模式設計的類,可以保證僅有一個例項,並可提供訪問該例項的全域性訪問點。J2EE應用的大量元件,都需要保證一個類只有一個例項。比如資料庫引擎訪問點只能有一個。
更多的時候,為了提高效能,程式應儘量減少Java 物件的建立和銷燬時的開銷。使用單態模式可避免Java 類被多次例項化,讓相同類的全部例項共享同一記憶體區。
為了防止單態模式的類被多次例項化,應將類的構造器設成私有,這樣就保證了只能通過靜態方法獲得類例項。而該靜態方法則保證每次返回的例項都是同一個,這就需將該類的例項設定成類屬性,由於該屬性需要被靜態方法訪問,因此該屬性應設成靜態屬性。
下面給出單態模式的示例程式碼:
package ppp; //單態模式測試類 public class SingletonTest { //該類的一個普通屬性 int value; //使用靜態屬性儲存該類的一個例項 private static SingletonTest instance; //構造器私有化,避免該類被多次例項化 private SingletonTest(){ System.out.println("正在執行構造器..."); } //提供靜態方法返回該類例項 public static SingletonTest getInstance(){ //例項化類例項前,先檢查該例項是否存在 if(instance == null){ //如果不存在,則新建一個例項 instance = new SingletonTest(); } //返回該類的成員變數:該類的例項 return instance; } //以下提供對普通屬性value的getter和setter方法 public int getValue(){ return value; } public void setValue(int values){ this.value = values; } public static void main(String args[]){ SingletonTest t1 = SingletonTest.getInstance(); SingletonTest t2 = SingletonTest.getInstance(); t2.setValue(9); System.out.println(t1 == t2); } }
從程式最後的列印結果可以看出,該類的兩個例項完全相同。這證明單態模式類的全部例項是同一共享例項。程式裡雖然獲得了類的兩個例項,但實際上只執行一次構造器,因為對於單態模式的類,無論有多少次的建立例項請求,都只執行一次構造器。
二. 工廠模式的回顧
工廠模式是根據呼叫資料返回某個類的一個例項,此類可以是多個類的某一個類。通常,這些類滿足共同的規則(介面)或父類。呼叫者只關心工廠生產的例項是否滿足某種規範,即實現的某個介面是否可供自己正常呼叫(呼叫者僅僅使用)。該模式給物件之間作出了清晰的角色劃分,降低程式的耦合。
介面產生的全部例項通常用於實現相同介面,接口裡定義了全部例項共同擁有的方法,這些方法在不同的實現類中實現的方式不同。從而使程式呼叫者無須關心方法的具體實現,降低了系統異構的代價。
下面是工廠模式的示例程式碼:
package ppp;
//Person介面定義
public interface Person {
public String sayHello(String name);
public String sayGoodbye(String name);
}
該介面定義了Person規範,規範要求實現該介面的類必須具有以下兩個的方法:能打招呼,能告別。
package ppp; //American類實現Person介面 public class American implements Person { public String sayHello(String name){ return name+",hello"; } public String sayGoodbye(String name) { return name+",goodbye"; } }
下面是Person類的另一個實現類:Chinese
package ppp; //Chinese類實現Person介面 public class Chinese implements Person { public String sayHello(String name){ return name+",您好"; } public String sayGoodbye(String name) { return name+",下次再見"; } }
然後再看Person工廠的程式碼:
package ppp;
public class PersonFactory {
public Person getPerson(String ethnic)
{
if(ethnic.equalsIgnoreCase("chin"))
{
return new Chinese();
}else{
return new American();
}
}
}
以上是最簡單的工廠模式框架,其主程式如下:
package ppp;
public class FactoryTest {
public static void main(String[] args){
//建立PersonFactory例項 ,獲得工廠例項
PersonFactory pf = new PersonFactory();
//定義介面Person例項,面向介面程式設計
Person p = null;
//使用工廠獲得person例項
p = pf.getPerson("chin");
//下面呼叫Person介面方法
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodbye("wawa"));
//使用工廠獲得Person的另一個例項
p = pf.getPerson("ame");
//再次呼叫Person介面的方法
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodbye("wawa"));
}
}
由此可看出,主程式從Person 介面的具體類中解耦出來,而且程式呼叫者無須關心Person 的例項化過程,主程式僅僅與工廠服務定位結合在一起,可獲得所有工廠能產生的例項。具體類的變化,介面無須發生任何改變,呼叫者程式程式碼部分也無須發生任何改動。
下面是Spring 對這兩種模式的實現。
三. Spring 對單態與工廠模式的實現
隨著Spring 提供工廠模式的實現,在使用Spring 時,無須自己提供工廠類。因為Spring容器是最大的工廠,而且是個功能超強的工廠。Spring 使用配置檔案管理所有的bean ,其配置檔案中bean 由Spring 工廠負責生成和管理。
下面是關於兩個例項的配置檔案:
<!--下面是xml檔案的檔案頭-->
<?xml version = "1.0" encoding = "gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springsource.org/dtd/spring-beans.dtd">
<!--beans是Spring配置檔案的根元素-->
<beans>
<!--定義第一個bean,該bean的id是chinese-->
<bean id = "chinese" class = "ppp.Chinese"/>
<!--定義第二個bean,該bean的id是American-->
<bean id = "american" class = "ppp.American"/>
</beans>
主程式部分如下:
package ppp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class SpringTest {
public static void main(String[] args) {
//例項化Spring容器
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//定義Person介面例項
Person p = null;
//通過Spring上下文獲得Chinese例項
p = (Person)ctx.getBean("chinese");
//執行chinese例項的方法
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodbye("wawa"));
p = (Person)ctx.getBean("american");
System.out.println(p.sayHello("wawa"));
System.out.println(p.sayGoodbye("wawa"));
}
}
使用Spring 時,即使沒有工廠類PersonFactory ,程式一樣可以使用工廠模式, Spring完全可以提供所有工廠模式的功能。
下面對主程式部分進行簡單的修改:
package ppp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class SpringTest{
public static void main(String[] args){
//例項化Spring容器
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//定義p1介面的例項p1
Person p1 = null;
//通過Spring上下文獲得Chinese例項
p1 = (Person)ctx.getBean("Chinese");
//定義p2介面的例項p2
Person p2 = null;
p2 = (Person)ctx.getBean("Chinese");
System.out.println(p1 == p2);
}
}
程式的執行結果是:true
表明Spring對接受容器管理的全部的bean,預設採用單態模式管理,建議不要隨意更改bean的行為方式。因為從效能上講,單態的bean比非單態的bean效能更為優秀。
仔細檢查上面的程式碼就會發現如下的特點;
(1)除測試部分的主程式外,程式碼並未出現Spring的特定類和介面。
(2)呼叫者的程式碼,也就是測試用的主程式部分,僅僅面向Person的介面程式設計,而無需知道實現類的具體名稱。同時,通過修改配置檔案來徹底切換底層的具體實現類。
(3)由於廠無需多個例項,因此工廠應該採用單態模式設計,其中Spring上下文也就是Spring工廠,已被設計成單態。
Spring工廠模式,不僅提供了建立bean的功能,還提供了對bean的生命週期的管理。最重要的是還以管理bean和bean之間的依賴關係