1、關於享元模式 享元模式有點類似於單例模式,都是只生成一個對象被共用使用。享元模式主要目的就是讓多個對象實現共用,減少不會要額記憶體消耗,將多個對同一對象的訪問集中起來,不必為每個訪問者創建一個單獨的對象,以此來降低記憶體的消耗。 2、享元模式結構圖 因為享元模式結構比較複雜,一般結合工廠模式一起使用 ...
1、關於享元模式
享元模式有點類似於單例模式,都是只生成一個對象被共用使用。享元模式主要目的就是讓多個對象實現共用,減少不會要額記憶體消耗,將多個對同一對象的訪問集中起來,不必為每個訪問者創建一個單獨的對象,以此來降低記憶體的消耗。
2、享元模式結構圖
因為享元模式結構比較複雜,一般結合工廠模式一起使用,在它的結構圖中包含了一個享元工廠類。
在享元模式結構圖中包含如下幾個角色:
Flyweight(抽象享元類):通常是一個介面或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些方法可以向外界提供享元對象的內部數據(內部狀態),同時也可以通過這些方法來設置外部數據(外部狀態)。
ConcreteFlyweight(具體享元類):它實現了抽象享元類,其實例稱為享元對象;在具體享元類中為內部狀態提供了存儲空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元對象。
UnsharedConcreteFlyweight(非共用具體享元類):並不是所有的抽象享元類的子類都需要被共用,不能被共用的子類可設計為非共用具體享元類;當需要一個非共用具體享元類的對象時可以直接通過實例化創建。
FlyweightFactory(享元工廠類):享元工廠類用於創建並管理享元對象,它針對抽象享元類編程,將各種類型的具體享元對象存儲在一個享元池中,享元池一般設計為一個存儲“鍵值對”的集合(也可以是其他類型的集合),可以結合工廠模式進行設計;當用戶請求一個具體享元對象時,享元工廠提供一個存儲在享元池中已創建的實例或者創建一個新的實例(如果不存在的話),返回新創建的實例並將其存儲在享元池中。
3、享元模式的實現
在享元模式中引入了享元工廠類,享元工廠類的作用在於提供一個用於存儲享元對象的享元池,當用戶需要對象時,首先從享元池中獲取,如果享元池中不存在,則創建一個新的享元對象返回給用戶,併在享元池中保存該新增對象。
接下來,實現一個登陸的享元模式。
1、用戶類
1 /** 2 * 用戶類 3 * @author 董秀才 4 * 5 */ 6 public class User { 7 private String username; // 用戶名 8 private String password; // 密碼 9 10 public User(String username,String password) { 11 this.username = username; 12 this.password = password; 13 } 14 15 public String getUsername() { 16 return username; 17 } 18 19 public void setUsername(String username) { 20 this.username = username; 21 } 22 23 public String getPassword() { 24 return password; 25 } 26 27 public void setPassword(String password) { 28 this.password = password; 29 } 30 }
2、抽象的登陸者(抽象享元類)
1 /** 2 * 登陸者--抽象享元類 3 * @author 董秀才 4 * 5 */ 6 public abstract class Loginer { 7 8 //登陸--享元類公共方法 9 public abstract void login(User user); 10 11 }
3、具體的登陸者(具體享元類)
1 /** 2 * 具體享元類 3 * @author 董秀才 4 * 5 */ 6 public class ConcreteLoginer extends Loginer{ 7 8 // 登陸者憑證 9 private String loginerKey = ""; 10 public ConcreteLoginer(String loginerKey) { 11 this.loginerKey = loginerKey; 12 } 13 14 @Override 15 public void login(User user) { 16 System.out.println("登陸者憑證:" + this.loginerKey+",用戶名:" + user.getUsername() + ",密碼:" + user.getPassword()); 17 } 18 19 }
4、具體登陸者的工廠類(享元工廠類)
1 /** 2 * 享元工廠類 3 * @author 董秀才 4 * 5 */ 6 public class ConcreteLoginerFactory { 7 8 // map充當對象享元池 9 private static Map<String,ConcreteLoginer> loginerMap = new HashMap<String, ConcreteLoginer>(); 10 11 public static ConcreteLoginer getConcreteLoginer(String key) { 12 // 從享元池中拿 登陸者對象 13 ConcreteLoginer concreteLoginer = loginerMap.get(key); 14 // 如果享元池中沒有此對象 15 if(concreteLoginer == null) { 16 // 創建對象 17 concreteLoginer = new ConcreteLoginer(key); 18 // 存到享元池中 19 loginerMap.put(key, concreteLoginer); 20 } 21 // 返回對象 22 return concreteLoginer; 23 } 24 25 26 // 返回享元池對象數量 27 public static int getSize() { 28 return loginerMap.size(); 29 } 30 }
5、測試類
1 /** 2 * 博客測試類 3 * @author 董秀才 4 * 5 */ 6 public class MainTest { 7 8 public static void main(String[] args) { 9 // 去工廠拿對象 10 ConcreteLoginer concreteLoginer_1 = ConcreteLoginerFactory.getConcreteLoginer("csdn"); 11 concreteLoginer_1.login(new User("董秀才","123456")); 12 13 ConcreteLoginer concreteLoginer_2 = ConcreteLoginerFactory.getConcreteLoginer("csdn"); 14 concreteLoginer_2.login(new User("董秀才","123456")); 15 16 ConcreteLoginer concreteLoginer_3 = ConcreteLoginerFactory.getConcreteLoginer("csdn"); 17 concreteLoginer_3.login(new User("董秀才","123456")); 18 19 // 測試是否是同一個對象 20 System.out.println("是否是同一個對象:" + ((concreteLoginer_1==concreteLoginer_2)&&(concreteLoginer_2 == concreteLoginer_3))); 21 22 23 // 第二登陸者 24 ConcreteLoginer concreteLoginer_4 = ConcreteLoginerFactory.getConcreteLoginer("博客園"); 25 concreteLoginer_4.login(new User("董才才","654321")); 26 27 ConcreteLoginer concreteLoginer_5 = ConcreteLoginerFactory.getConcreteLoginer("博客園"); 28 concreteLoginer_5.login(new User("董才才","654321")); 29 30 ConcreteLoginer concreteLoginer_6 = ConcreteLoginerFactory.getConcreteLoginer("博客園"); 31 concreteLoginer_6.login(new User("董才才","654321")); 32 33 System.out.println("是否是同一個對象:" + ((concreteLoginer_4==concreteLoginer_5)&&(concreteLoginer_5 == concreteLoginer_6))); 34 // 工廠類中享元池中對象數量 35 System.out.println("享元池size:" + ConcreteLoginerFactory.getSize()); 36 } 37 38 }
6、運行結果
4、總結
從上面代碼和運行結果這可以看到,同一個登陸者登陸時是 "享" 用同一個登陸者對象。在享元對象池中只有兩個對象。
享元模式優點
享元模式的外部狀態相對獨立,使得對象可以在不同的環境中被覆用(共用對象可以適應不同的外部環境)
享元模式可共用相同或相似的細粒度對象,從而減少了記憶體消耗,同時降低了對象創建與垃圾回收的開銷
享元模式缺點
外部狀態由客戶端保存,共用對象讀取外部狀態的開銷可能比較大
享元模式要求將內部狀態與外部狀態分離,這使得程式的邏輯複雜化,同時也增加了狀態維護成本