最近在啃《Effective Java》這本神書。簡單記錄一下,友善以後溫習。
靜态工廠方法
靜态工廠方法是什麼?直接從字面應該就很好了解:
工廠方法:大家應該都知道,就是建構執行個體的方法呗。(比如:“江南皮革廠”就是建立“皮包”這個對象的工廠)
靜态方法:這還有解釋的必要麼.......
合起來就是靜态的建構對象的方法呗。。哦了,這就是靜态工廠方法。本文到此結束,感謝大家觀看!
哈哈,開個玩笑。 雖然靜态工廠方法是什麼很好了解。可是靜态工廠方法到底有什麼用呢,我們又為什麼要用它呢?
一般情況下,java中我們建立對象要寫一個公有的構造函數,外部調用構造函數來建立一個對象。靜态工廠方法就是與它相對應的。代碼分别如下:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "窮";//原則上來講,預設資料要符合普遍情況。
//普通構造函數--無參
public People() {
}
//普通構造函數--有參
public People(String sex) {
this.sex = sex;
}
public People(String sex, String appearance) {
this.sex = sex;
this.appearance = appearance;
}
public People(String sex, String appearance, String asset) {
this.sex = sex;
this.appearance = appearance;
this.asset = asset;
}
//靜态工廠方法
public static People createGirlfriend() {//程式員的基本操作,new一個女朋友。
People people = new People();
people.sex = "女";
people.appearance = "傾國傾城";
people.asset = "市中心十棟樓";
return people;
}
}
上面就是一個簡單的靜态工廠方法,其實本質上就是加了一層封裝。有了這層封裝自己可以操作和控制很多東西。
下面看看兩者的差別:
1.靜态工廠方法的優點
1.1 靜态工廠方法是有名稱的
我們在java中應該經常看到類似這種構造函數:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "窮";
//普通構造函數--無參
public People() {
}
//普通構造函數--有參
public People(String sex) {
this.sex = sex;
}
public People(String sex, String appearance) {
this.sex = sex;
this.appearance = appearance;
}
public People(String sex, String appearance, String asset) {
this.sex = sex;
this.appearance = appearance;
this.asset = asset;
}
}
當然,官方文檔的注釋還是很完善和規範的。我們可以看到每個構造函數的用處和每個參數的作用。可是如果在多點參數呢,又如果是自己寫方法呢。(有時候懶再少些兩行注釋~~後期維護兩行淚~) 調用的時候是不是就會一臉懵逼?我該調用哪個?這個參數是什麼?要傳什麼值?況且每次建構對象的時候都要點進去看注釋也挺麻煩的。
為了解決上述問題,我們可以使用靜态工廠方法。因為靜态工廠方法是可以有名字的,這樣建構對象時就非常直覺。比如文章開始的第一個demo。通過靜态工廠方法createGirlfriend()方法可以直接建立一個“People”的執行個體,同時我們可以直接從方法名看出我們建立出來的“people”執行個體是“女朋友”。是不是很友善易懂呢~~
1.2 靜态工廠方法可以不必在每次調用他們的時候都建立一個新對象
這個應該很好了解,因為靜态工廠方法是多一層封裝的。是以在這裡面我們可以自由控制傳回的對象,可以全局隻使用一個對象(單例)或者控制什麼時候建立新的執行個體,什麼時候使用緩存的執行個體。代碼如下:
class People {
private String sex = "男";
private String appearance = "一般";
private String asset = "窮";
//靜态工廠方法
public static People createGirlfriend() {
People people = new People();//這裡是我們可控制的,是以你可以選擇new新的對象或者使用緩存的已建立的對象
people.sex = "女";
people.appearance = "傾國傾城";
people.asset = "市中心十棟樓";
return people;
}
}
1.3 靜态工廠方法可以傳回原傳回類型的任何子類型
這一點也好了解,先看代碼:
class People {
//靜态工廠方法
public static People createChildren() {
Children people = new Children();
return people;
}
}
//People的子類
class Children extends People {
public Children() {
}
}
這個很好了解了,Children是People的子類。是以在靜态工廠方法中可以直接傳回Children的執行個體。
不過這裡我們不得不提一下設計原則--裡式替換原則:任何基類可以出現的地方,子類一定可以出現。 --百度百科
最通俗的講,就是我們在寫子類的時候要注意:可以拓展父類功能和屬性,但是不能修改。(也就是對擴充開放,對修改關閉)
1.4 靜态工廠方法傳回的對象的類可以随着每次調用而發生改變,這取決于所傳參數值
了解:靜态工廠方法可以根據參數不同傳回不同的子類對象。代碼如下:
class People {
/**
* @param timeForSingle 單身時長
* @return 對象
*/
public static People createGirlfriend(int timeForSingle) {
if (timeForSingle < 5) {//根據所傳參數,傳回不同的子類對象。
EighteenBeauty eighteenBeauty = new EighteenBeauty();
return eighteenBeauty;
} else {//單身超過五年,看男生都感覺很清秀了呢~~
Man man = new Man();
return man;
}
}
}
//18歲年輕漂亮的小姑娘
class EighteenBeauty extends People {
public EighteenBeauty() {
}
}
//男人
class Man extends People {
public Man() {
}
}
OK,看代碼就很好了解了。根據所傳遞參數不同傳回了不同的子類對象。
1.5 靜态工廠方法傳回的對象所屬的類,在編寫包含該靜态工廠方法的類時可以不存在(該篇唯一難點)
重頭戲到了,這是本篇唯一難點。這個概念了解起來倒是很簡單:靜态工廠方法傳回對象所屬的類,在編寫包含該靜态工廠方法時可以不存在。(好像是把概念又抄了一遍,不過單從字面實在沒什麼可解釋的了~~)。
不過重點在于,怎麼實作呢?
這裡直接從 這種靜态工廠方法最典型的實作--服務提供者架構 來探讨。
服務提供者架構包含四大元件:(概念不太好了解,可以直接先看下面的例子講解,然後回過頭來再看概念)
- 服務接口:這是服務提供者要去實作的接口
- 服務提供者接口:生成服務接口執行個體的工廠對象(就是用來生成服務接口的)(可選)
- 提供者注冊API:服務者 提供服務者自身的實作
- 服務通路API:根據用戶端指定的某種條件去實作對應的服務提供者
概念太拗口,上栗子講解:
//四大組成之一:服務接口
public interface LoginService {//這是一個登入服務
public void login();
}
//四大組成之二:服務提供者接口
public interface Provider {//登入服務的提供者。通俗點說就是:通過這個newLoginService()可以獲得一個服務。
public LoginService newLoginService();
}
/**
* 這是一個服務管理器,裡面包含了四大組成中的三和四
* 解釋:通過注冊将 服務提供者 加入map,然後通過一個靜态工廠方法 getService(String name) 傳回不同的服務。
*/
public class ServiceManager {
private static final Map<String, Provider> providers = new HashMap<String, Provider>();//map,儲存了注冊的服務
private ServiceManager() {
}
//四大組成之三:提供者注冊API (其實很簡單,就是注冊一下服務提供者)
public static void registerProvider(String name, Provider provider) {
providers.put(name, provider);
}
//四大組成之四:服務通路API (用戶端隻需要傳遞一個name參數,系統會去比對服務提供者,然後提供服務) (靜态工廠方法)
public static LoginService getService(String name) {
Provider provider = providers.get(name);
if (provider == null) {
throw new IllegalArgumentException("No provider registered with name=" + name);
}
return provider.newLoginService();
}
}
OK,代碼中注釋的很清楚了。
思考下,這麼做有什麼好處呢??重點看一下上面demo中的 “四大組成之四:服務通路API ”
想一想,是不是以後想要增加服務時隻需要實作服務提供者接口、服務接口,然後約定一個服務名就可以了?
2.靜态工廠方法的缺點
2.1 如果類不含公有的或者受保護的構造器時,就是能被子類化
如果類的構造方法是私有的,那麼這個類就不能被繼承了。這時候建議用複合代替繼承實作類的拓展。
2.2 靜态工廠方法實際上就是靜态方法,如果命名不規範的話程式員很難發現他們
這時候就需要我們自我限制了,利用靜态工廠方法時一定要遵循命名規範,下面是一些常用的靜态工廠方法命名:
- from 類型轉換方法,它隻有單個參數,傳回該類型的一個執行個體,并把它們合并起來。
- of 聚合方法,帶有多個參數,傳回該類型的一個執行個體,把他們合并起來。
- valueOf 比from和of更繁瑣的一種替代方法。
- instance或者getInstance 傳回的執行個體是通過方法的(如有)參數來描述的,但是不能說與參數具有同樣的值。
- create或者newInstance 像instance或者getInstance一樣,但create或者newInstance能夠確定每次調用都傳回一個新的執行個體
- getType 像getInstance一樣,但是在工廠方法處于不同的類中的時候使用。Type表示工廠方法所傳回的對象類型。
- newType 像getInstance一樣,但是在工廠方法處于不同的類中的時候使用。
- type getType和newType的精簡版。