PS一句:最終還是選擇CSDN來整理發表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown文法了,牛逼啊!
【工匠若水 http://blog.csdn.net/yanbober】 閱讀前一篇《設計模式(結構型)之外觀模式(Facade Pattern)》http://blog.csdn.net/yanbober/article/details/45476527
概述
當一個軟體系統在運作時産生的對象數量太多,将導緻運作代價過高,帶來系統性能下降等問題。是以需要采用一個共享來避免大量擁有相同内容對象的開銷。在Java中,String類型就是使用了享元模式。String對象是final類型,對象一旦建立就不可改變。在Java中字元串常量都是存在常量池中的,Java會確定一個字元串常量在常量池中隻有一個拷貝。
核心
概念: 運用共享技術有效地支援大量細粒度對象的複用。系統隻使用少量的對象,而這些對象都很相似,狀态變化很小,可以實作對象的多次複用。由于享元模式要求能夠共享的對象必須是細粒度對象,是以它又稱為輕量級模式,它是一種對象結構型模式。
關于享元基礎:
享元對象共享的關鍵是區分了内部狀态(Intrinsic State)和外部狀态(Extrinsic State)。
内部狀态
存儲在享元對象内部并且不會随環境改變而改變的狀态,内部狀态可以共享。
外部狀态
享元對象的外部狀态通常由用戶端儲存,并在享元對象被建立之後,需要使用的時候再傳入到享元對象内部。随環境改變而改變的、不可以共享的狀态。一個外部狀态與另一個外部狀态之間是互相獨立的。
由于區分了内部狀态和外部狀态,我們可以将具有相同内部狀态的對象存儲在享元池中,享元池中的對象是可以實作共享的,需要的時候就将對象從享元池中取出,實作對象的複用。通過向取出的對象注入不同的外部狀态,可以得到一系列相似的對象,而這些對象在記憶體中實際上隻存儲一份。
享元模式分類:
- 單純享元模式
- 複合享元模式
單純享元模式結構重要核心子產品:
抽象享元角色
為具體享元角色規定了必須實作的方法,而外部狀态就是以參數的形式通過此方法傳入。在Java中可以由抽象類、接口來擔當。
具體享元角色
實作抽象角色規定的方法。如果存在内部狀态,就負責為内部狀态提供存儲空間。
享元工廠角色
負責建立和管理享元角色。要想達到共享的目的,這個角色的實作是關鍵!
用戶端角色
維護對所有享元對象的引用,而且還需要存儲對應的外部狀态。
單純享元模式和建立型的簡單工廠模式實作上非常相似,但是它的重點或者用意卻和工廠模式截然不同。工廠模式的使用主要是為了使系統不依賴于實作得細節;而在享元模式的主要目的是避免大量擁有相同内容對象的開銷。
複合享元模式結構重要核心子產品:
抽象享元角色
為具體享元角色規定了必須實作的方法,而外部狀态就是以參數的形式通過此方法傳入。在Java中可以由抽象類、接口來擔當。
具體享元角色
實作抽象角色規定的方法。如果存在内部狀态,就負責為内部狀态提供存儲空間。
複合享元角色
它所代表的對象是不可以共享的,并且可以分解成為多個單純享元對象的組合。
享元工廠角色
負責建立和管理享元角色。要想達到共享的目的,這個角色的實作是關鍵!
用戶端角色
維護對所有享元對象的引用,而且還需要存儲對應的外部狀态。
使用場景
一個系統有大量相同或者相似的對象,造成記憶體的大量耗費。
對象的大部分狀态都可以外部化,可以将這些外部狀态傳入對象中。
在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統資源,是以,應當在需要多次重複使用享元對象時才值得使用享元模式。
程式猿執行個體
單純享元模式執行個體:例子完全就是核心點的文字翻譯代碼,不做過多解釋。
package yanbober.github.io;
import java.util.HashMap;
import java.util.Map;
//抽象享元角色類
interface ICustomerString {
//外部狀态以參數的形式通過此方法傳入
void opt(String state);
}
//具體享元角色類
class CustomerStringImpl implements ICustomerString {
//負責為内部狀态提供存儲空間
private Character mInnerState = null;
public CustomerStringImpl(Character mInnerState) {
this.mInnerState = mInnerState;
}
@Override
public void opt(String state) {
System.out.println("Inner state = "+this.mInnerState);
System.out.println("Out state = "+state);
}
}
//享元工廠角色類
//一般而言,享元工廠對象在整個系統中隻有一個,是以也可以使用單例模式
class CustomerStringFactory {
private Map<Character, ICustomerString> map = new HashMap<>();
public ICustomerString factory(Character state) {
ICustomerString cacheTemp = map.get(state);
if (cacheTemp == null) {
cacheTemp = new CustomerStringImpl(state);
map.put(state, cacheTemp);
}
return cacheTemp;
}
}
//用戶端
public class Main {
public static void main(String[] args) {
CustomerStringFactory factory = new CustomerStringFactory();
ICustomerString customerString = factory.factory(new Character('Y'));
customerString.opt("YanBo");
customerString = factory.factory(new Character('B'));
customerString.opt("Bob");
customerString = factory.factory(new Character('Y'));
customerString.opt("Jesse");
}
}
運作結果:
Inner state = Y
Out state = YanBo
Inner state = B
Out state = Bob
Inner state = Y
Out state = Jesse
上邊示例結果一目了然可以看出來簡單享元模式的特點。
複合享元模式執行個體:
如下例子就是一個複合享元模式,添加了複合對象,具體如下:
package yanbober.github.io;
import java.util.*;
//抽象享元角色類
interface ICustomerString {
//外部狀态以參數的形式通過此方法傳入
void opt(String state);
}
//具體享元角色類
class CustomerStringImpl implements ICustomerString {
//負責為内部狀态提供存儲空間
private Character mInnerState = null;
public CustomerStringImpl(Character mInnerState) {
this.mInnerState = mInnerState;
}
@Override
public void opt(String state) {
System.out.println("Inner state = "+this.mInnerState);
System.out.println("Out state = "+state);
}
}
//複合享元對象
class MultipleCustomerStringImpl implements ICustomerString {
private Map<Character, ICustomerString> map = new HashMap<>();
public void add(Character key, ICustomerString value) {
map.put(key, value);
}
@Override
public void opt(String state) {
ICustomerString temp;
for (Character obj : map.keySet()) {
temp = map.get(obj);
temp.opt(state);
}
}
}
//享元工廠角色類
class CustomerStringFactory {
//一般而言,享元工廠對象在整個系統中隻有一個,是以也可以使用單例模式
private Map<Character, ICustomerString> map = new HashMap<>();
//上例的單純享元模式
public ICustomerString factory(Character state) {
ICustomerString cacheTemp = map.get(state);
if (cacheTemp == null) {
cacheTemp = new CustomerStringImpl(state);
map.put(state, cacheTemp);
}
return cacheTemp;
}
//複合享元模式
public ICustomerString factory(List<Character> states) {
MultipleCustomerStringImpl impl = new MultipleCustomerStringImpl();
for (Character state : states) {
impl.add(state, this.factory(state));
}
return impl;
}
}
//用戶端
public class Main {
public static void main(String[] args) {
List<Character> states = new ArrayList<>();
states.add('Y');
states.add('A');
states.add('N');
states.add('B');
states.add('O');
states.add('Y');
states.add('B');
CustomerStringFactory factory = new CustomerStringFactory();
ICustomerString customerString1 = factory.factory(states);
ICustomerString customerString2 = factory.factory(states);
customerString1.opt("Mutex object test!");
}
}
總結一把
從上面代碼你可以發現,由于享元模式的複雜,實際應用也不是很多,這是我們更加無法看清他的真面目了。不過享元模式并不是雞肋,它的精髓是共享,是對我們系統優化非常有好處的,而且這種思想已經别越來越多的應用,這應該就算是享元模式的應用了吧。
享元模式優點:
- 可以極大減少記憶體中對象的數量,使得相同或相似對象在記憶體中隻儲存一份,進而可以節約系統資源,提高系統性能。
- 享元模式的外部狀态相對獨立,而且不會影響其内部狀态,進而使得享元對象可以在不同的環境中被共享。
享元模式缺點:
- 享元模式使得系統變得複雜,需要分離出内部狀态和外部狀态,這使得程式的邏輯複雜化。
- 為了使對象可以共享,享元模式需要将享元對象的部分狀态外部化,而讀取外部狀态将使得運作時間變長。
【工匠若水 http://blog.csdn.net/yanbober】 繼續閱讀《設計模式(結構型)之代理模式(Proxy Pattern)》 http://blog.csdn.net/yanbober/article/details/45480965#t3