● 單例模式介紹
單例模式是應用最廣的模式之一,也可能是很多初級工程師唯一會使用的設計模式。在應用這個模式時,代理對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個全局對象,這樣又利於我們協調系統整體的行爲。如在一個應用中,應該只有一個ImageLoader實例,這ImageLoader中又含有線程池、緩存系統、網絡請求等。很消化資源,因此,沒有理由讓它構造多個實例。這種不能自由構造對象的情況,就是單例模式的使用場景。
● 單例模式的定義
確保某一個類只有一個實例,而且自行實例化並像這個系統提供這個實例。
● 單例模式的使用場景
確保某個類有且只有一個對象的場景,避免產生多個對象消耗過多的資源,或者某種類型的對象只應該有且只有一個。例如,創建一個對象需要消耗的資源過多,如要訪問IO和數據庫等資源,這時就要考慮單例模式。
● 單例模式UML類圖
角色介紹:
(1)Client——高層客戶端;
(2)Singlet——單例類。
實現單例模式主要如下幾個關鍵點:
(1)構造函數不對外開放,一般爲Pirvate;
(2)通過一個靜態方法或者枚舉返回單例類對象;
(3)確保單例類的對象有且只有一個,尤其在多線程環境下;
(4)確保單例類對象在反序列化時不會重新構建對象。
通過見單例類的構造函數私有化,使得客戶端代碼不能通過new的形式手動構建單例類的對象。單例類會暴露一個公有的靜態方法,客戶端需要調用這個靜態方法獲取到單例類的唯一對象,在獲取這個單例對象的過程中需要確保線程安全,即在多線程環境下構造單例類的對象也是有且只有一個,這也是單例模式實現中比較困難的地方。
● 單例模式的簡單示例
單例模式是設計模式中比較簡單的,只有一個單例類,沒有其他的層次結構與抽象。該模式需要確保該類只能生成一個對象,通常是該類需要消耗較多的資源或者沒有多個實現的情況。例如一個公司只有一個CEO、一個應用只有一個Appliacton對象等。下面以公司裏的CEO爲例來簡單演示一下,一個公司可以有幾個VP、無數個員工,但是CEO只有一個,請看下面示例。
//普通員工
public class Staff {
public void work() {
//幹活
}
}
//副總裁
public class VP extends Staff{
@Override
public void work() {
//管理下面的經理
}
}
//CEO,惡漢單例模式
public class CEO extends Staff{
private static final CEO mCeo = new CEO();
//構造函數私有
private CEO() {
}
//公有的靜態函數,對外暴露獲取單例對象的接口
public static CEO getCeo() {
return mCeo;
}
@Override
public void work() {
//管理VP
}
}
public class Company {
private ArrayList<Staff> allStaffs = new ArrayList<>();
public void addStaff(Staff per) {
allStaffs.add(per);
}
public void showAllstffs() {
for (Staff per : allStaffs) {
System.out.println("obj : "+per.toString());
}
}
}
public static void main(String[] args) {
Company cp = new Company();
Staff ceo1 = CEO.getCeo();
Staff ce02 = CEO.getCeo();
cp.addStaff(ceo1);
cp.addStaff(ce02);
//通過new 創建VP對象
Staff vp1 = new VP();
Staff vp2 = new VP();
//通過new 來創建Staff對象
Staff staff1 = new Staff();
Staff staff2 = new Staff();
Staff staff3 = new Staff();
cp.addStaff(vp1);
cp.addStaff(vp2);
cp.addStaff(staff1);
cp.addStaff(staff2);
cp.addStaff(staff3);
cp.showAllstffs();
}
輸出結果如下:
obj : text.CEO@7852e922
obj : text.CEO@7852e922
obj : text.VP@4e25154f
obj : text.VP@70dea4e
obj : text.Staff@5c647e05
obj : text.Staff@33909752
obj : text.Staff@55f96302
從上述代碼中可以看到,CEO類不能通過new的形式構建對象,只能通過CEO.getCEO()函數來獲取,而這個CEO對象是靜態對象,並且在聲明的時候就已經初始化,這就保證了CEO對象的唯一性。從輸出結果中發現,CEO兩次輸出的CEO對象都是一樣的,而VP、Staff等類型的對象都是不同的。這個實現的核心在於將CEO類的構造方法私有化,使得外部程序不能通過構造函數來構造CEO對象,而CEO類通過一個靜態方法返回一個靜態對象。