享元模式是設計模式中少數幾個以提高系統性能為目的的模式之一。它的核心思想是:如果在一個系統中存在多個相同的對象,那么只需要共享一份對象的拷貝,而不必為每一次使用都創建新的對象。在享元模式中,由于需要構造和維護這些可以共享的對象,因此,常會出現一個工廠類,用于維護和創建對象。
享元模式的主要角色由享元工廠、抽象享元、具體享元類和主函數幾部分組成。他們的功能如下:
##享元工廠:用于創建具體享元類,維護相同的享元對象。它保證相同的享元對象可以被系統共享。即,其內部使用了類似單例模式的方法,當請求對象已經存在時,直接返回對象,不存在時,在創建對象。
##抽象享元:定義需要共享的對象業務接口。享元類被創建出來總是為了實現某些特定的業務邏輯,而抽象享元便定義這些邏輯的語義行為。
##具體享元類:實現抽象享元類的接口,完成某一具體邏輯。
##客戶端:使用享元模式的組件,通過享元工廠取得享元對象。
享元工廠是享元模式的核心,它需要確保系統可以共享相同的對象。一般情況下,享元工廠會維護一個對象列表,當任何組件嘗試獲取享元類時,如果請求的享元類已經被創建,則直接返回已有的享元類:若沒有,則創建一個新的享元對象,并將它加入到維護隊列中。
下面就以一個售賣圖書的例子來講解向元模式是如何應用的。
例如一家書店有很多圖書,現有很多用戶來店進行買書。那么顯然我們可以將圖書的名稱作為共享的,而不必為每賣一本書而單獨生成。代碼實現如下:
下定義抽象業務邏輯接口:
package pattern;
public interface FlyWeight {
void sell();
}
然后是具體實現圖書售賣的實現類:
package pattern;
public class BookOrder implements FlyWeight{
private String name;
BookOrder(String name){
this.name = name;
}
@Override
public void sell() {
System.out.println("賣了一本書,書名為'"+this.name+"'");
}
}
緊接著實現享元工廠類:
package pattern;
import java.util.HashMap;
import java.util.Map;
public class FlyWeightFactory {
private Map<String, FlyWeight> bookPools = new HashMap<String, FlyWeight>();
private static FlyWeightFactory factory = new FlyWeightFactory();
public static FlyWeightFactory getInstance(){
return factory;
}
//添加訂單
public FlyWeight getOrder(String bookName){
FlyWeight order = null;
if (bookPools.containsKey(bookName)) {
order = bookPools.get(bookName);
}else{
order = new BookOrder(bookName);
bookPools.put(bookName, order);
}
return order;
}
public int getTotalObjects(){
return bookPools.size();
}
}
最后是客戶端模擬售賣圖書的過程:
package pattern;
import java.util.ArrayList;
import java.util.List;
public class PatternTest {
private static List<FlyWeight> orders = new ArrayList<FlyWeight>();
private static FlyWeightFactory factory;
public static void main(String[] args) {
factory = FlyWeightFactory.getInstance();
takeOrders("三國演義");
takeOrders("水滸傳");
takeOrders("封神榜");
takeOrders("三體");
takeOrders("紅樓夢");
takeOrders("三國演義");
takeOrders("封神榜");
takeOrders("水滸傳");
for (FlyWeight order : orders) {
order.sell();
}
// 打印生成的訂單java對象數量
System.out.println("\n客戶一共買了 " + orders.size() + " 本書! ");
// 打印生成的訂單java對象數量
System.out.println("共生成了 " + factory.getTotalObjects()
+ " 個 FlyWeight java對象! ");
}
private static void takeOrders(String bookName){
orders.add(factory.getOrder(bookName));
}
}
打印結果如下:
賣了一本書,書名為'三國演義'
賣了一本書,書名為'水滸傳'
賣了一本書,書名為'封神榜'
賣了一本書,書名為'三體'
賣了一本書,書名為'紅樓夢'
賣了一本書,書名為'三國演義'
賣了一本書,書名為'封神榜'
賣了一本書,書名為'水滸傳'
客戶一共買了 8 本書!
共生成了 5 個 FlyWeight java對象!
由上面的打印結果我們可以得出如下結論:
- 可以節省重復創建對象的開銷,因為被享元模式維護的相同對象只會被創建一次,當創建對象比較耗時時,便可以節省大量時間。
- 由于創建對象的數量減少,所以對系統內存的需求也減小,這將使得GC的壓力也相應地降低,進而使得系統擁有一個更健康的內存結構和更快的反應速度。