聲明:原創(chuàng)作品,轉載時請注明文章來自SAP師太技術博客( 博/客/園www.cnblogs.com):m.survivalescaperooms.com/jiangzhengjun,并以超鏈接形式標明文章原始出處,否則將追究法律責任!原文鏈接:http://m.survivalescaperooms.com/jiangzhengjun/p/4254987.html 第一章 前言 略...
第二章 創(chuàng)建和銷毀對象 1、 考慮用靜態(tài)工廠方法代替構造器創(chuàng)建對象方法:一是最常用的公有構造器,二是靜態(tài)工廠方法。下面是一個Boolean的簡單示例:
public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
}
l靜態(tài)工廠方法與構造器不同的第一大優(yōu)勢在于,它們有名稱。
作用不同的公有構造器只能通過參數(shù)來區(qū)別(因為一個類只有一個帶有指定簽名的構造器,所以多個構造器只能使用不同的參數(shù)列表來區(qū)分),如果使用靜態(tài)的工廠方法,則方法名會很清楚地表達方法的作用。
l靜態(tài)工廠方法與構造器不同的第二大優(yōu)勢在于,不必在每次調用它們的時候都創(chuàng)建一個新對象。
不可變類完全可以使用預先構建好的實例,而不必每次使用時都創(chuàng)建一個對象。另外,將構建好的實例緩存起來重復使用,從而避免創(chuàng)建不必要的重復對象。Boolean.valueOf(boolean)方法就使用了這項技術——它從來不創(chuàng)建對象。
l靜態(tài)工廠方法與構造器不同的第三大優(yōu)勢在于,它們可以返回原返回類型的任何子類型的對象。
這樣我們在選擇返回對象的類時就有了更大的靈活性。這種靈活性的一種應用是,API可以返回對象,同時又不會使對象的類變成公有的,比如我們完全可以先定義一個產(chǎn)品接口類,然后采用私有的內部類去實現(xiàn)這個接口,靜態(tài)工廠方法返回這個類的實例,這樣就隱藏了具體的實現(xiàn)。另外,使用靜態(tài)工廠方時,要求客戶端通過接口來引用被返回的對象,而不是通過它的實現(xiàn)類來引用被返回的對象,這是一種良好的編程習慣。
公有靜態(tài)工廠方法所返回的對象的類不僅可以是PRivate,而且通過靜態(tài)工廠方法的參數(shù),還可以隨著每次的返回不同的類的實例,只要是已聲明返回類型的子類型。這樣的好處是,可以在以后的版本中刪除這個類重新實現(xiàn)也不會影響到已使用的客戶。
靜態(tài)工廠方法返回的對象所屬的類,在編寫包含該靜態(tài)工廠方法的類時可能不必存在。這種靈活的靜態(tài)工廠方法構成了服務提供者框架的基礎,例如JDBC API。服務提供者框架是指這樣一個系統(tǒng):多個服務提供者實現(xiàn)一個服務,系統(tǒng)為服務提供者的客戶端提供多個實現(xiàn),并把他們從多個實現(xiàn)中解耦出來。
服務提供者框架有三個重要組件:服務接口(Service Interface),這是提供者實現(xiàn)的;提供者注冊API(Provider Registration API),這是系統(tǒng)用來注冊實現(xiàn),讓客戶端訪問它們的;服務訪問API(Service access API),是客戶端用來獲取服務的實例的方法接口。服務訪問API一般允許但是不要求客戶端指定某種選擇提供者的條件。如果沒有這樣的規(guī)定,API就會返默認實現(xiàn)的一個實例。服務訪問API是“靈活的靜態(tài)工廠”,它構成了服務提供者框架的基礎。
服務提供者框架的第四個組件是可選的:服務提供者接口(Service Provider Interface)(即工廠方法模式中的工廠接口),這些提供者負責創(chuàng)建其服務實現(xiàn)的實例。如果沒有服務提供者接口,實現(xiàn)就按照類名稱注冊,并通過反射方式進行實例化。對于JDBC來說,Connection就是它的服務接口,DriverManager.registerDriver是提供者注冊API,DriverManager.getConnection是服務訪問API,Driver就是服務提供者接口。
下面看看這四個組件的應用:
//服務接口,就是我們的業(yè)務接口。(相當于Connection接口,由Sun提供)
publicinterfaceService {
// ...
}
//服務提供都接口,即業(yè)務工廠接口。(相當于Driver接口,由第三方廠家實現(xiàn))
publicinterfaceProvider{
Service newService();
}
//服務提供者注冊與服務提供者接口(好比DriverManager)
publicclassServices{
privateServices() {}
//服務名與服務映射,即注冊容器
privatestaticfinalMapproviders=newConcurrentHashMap();
publicstaticfinalStringDEFAULT_PROVIDER_NAME= "def";
//服務提供者注冊API,即注冊工廠實現(xiàn),相當于DriverManager.registerDriver
publicstaticvoidregisterDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
publicstaticvoidregisterProvider(String name, Provider p) {
providers.put(name, p);
}
//服務訪問API,向外界提供業(yè)務實現(xiàn),相當于DriverManager.getConnection
publicstaticService newInstance() {
returnnewInstance(DEFAULT_PROVIDER_NAME);
}
publicstaticService newInstance(String name) {
Provider p = (Provider)providers.get(name);
if(p ==null) {
thrownewIllegalArgumentException(
"NO provider registered with name:" + name);
}
returnp.newService();
}
}
靜態(tài)工廠方法的第四大優(yōu)勢在于,在創(chuàng)建參數(shù)化類型實例的時候,它們使代碼變得更加簡潔。比如要創(chuàng)建一個參數(shù)化的HashMap,我們需要如下做:
Map<String,List<String>>m=newHashMap<String, List<String>>();
這么長的類型參數(shù)實在是不太好,而且隨著類型參數(shù)變得越來越長,也越來越復雜。但如果有了靜態(tài)工廠方法,編譯器就可以替你推導出類型,new時不需要提供參數(shù)類型。例如,假設HashMap提供了這個靜態(tài)工廠:
publicstatic<k,v> HashMap<k,v>newInstance(){
returnnewHashMap<k,v>();
}
那么你就可以使用以下簡潔的代碼來代替上面這段繁瑣的聲明:
Map<String,List<String>>m= HashMap.newInstance();
但可惜的是,到現(xiàn)在發(fā)行的版本1.6止還未加入,不過我們可以把這些方法放在自己的工具類中。
靜態(tài)工廠方法的一些慣用名稱:
valueOf——不太嚴格地講,該方返回的實例與它的參數(shù)具有相同的值。這樣的靜態(tài)工廠方法實際上是類型轉換方法。
of——valueOf的一種更為簡潔的替換,在EnumSet中使用并流行起來。
getInstance——返回的實例是通過方法的參數(shù)來描述的,但是不能夠說與參數(shù)具有同樣的值。對于Singleton來說,該方法沒有參數(shù),并返回唯一值。
newInstance——像getInstance一樣,但newInstance能夠確保返回每個實例都與把有其他實例不同。
getType——像getInstance一樣,但是在工廠方法處于不同的類中的時候使用。Type表示工廠方法所返回的對象類型。
newType——像newInstance一樣,但是在工廠方法處于不同的類中的時候使用。Type表示工廠方法所返回的對象類型。
2、 遇到多個構造器參數(shù)時要考慮構造器如果實例化時需要多個參數(shù)時,且這些參數(shù)中只有少數(shù)幾個是必須的,而很多是可選的,這時我們一般考慮使用構造器的方式,而不是使用靜態(tài)工廠方法。
對于此情況,我們可以使用重疊構造器模式——你提供一個只有必要參數(shù)的構造器,第二構造器有一個可選參數(shù),第三個有兩個可選參數(shù),依此類推,最后一個構造器包含所有可選參數(shù)。
publicclassNutritionFacts {
privatefinalintservingSize; //必選參數(shù)
privatefinalintservings; //必選參數(shù)
privatefinalintcalories; //可選參數(shù)
privatefinalintfat; //可選參數(shù)
privatefinalintsodium; //可選參數(shù)
privatefinalintcarbohydrate; //可選參數(shù)
//第一個構造器帶上所有必選參數(shù)
publicNutritionFacts(intservingSize,intservings) {
//調用另一個構造器
this(servingSize, servings, 0);//第三個參數(shù)為默認值
}
//第二個構造器在第一個構造器的基礎上加上一個可先參數(shù)
publicNutritionFacts(intservingSize,intservings,
intcalories) {
//第四個參數(shù)為默認值
this(servingSize, servings, calories, 0);
}
Word-spacing: 0px; text-transform: none; margin: 0cm 0cm 0pt; letter-spacing: normal; line-height: normal; text-indent: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-wid
新聞熱點
疑難解答