文章目錄
- 一、基本介紹
- 1、定義
- 2、優點
- 3、缺點
- 4、角色
- 1)抽象享元類
- 2)具體享元類
- 3)享元工廠類
- 4)組合享元類
- 5、內部狀態和外部狀態
- 二、應用情景
- 1、執行緒池
- 2、String
- 3、Integer
- 4、五子棋
- 三、與其他模式的搭配使用
- 1)單例模式
- 2)組合模式
- 3)工廠模式
- 四、代碼決議
一、基本介紹
1、定義
享元模式(FlyWeight),又稱為蠅量模式,常用于有著大量重復使用的細粒度物件時,實質上是一種共享技術,
2、優點
利用享元池,可以使得用戶在呼叫一個物件時可以不必去new,只需要在享元池中直接獲取即可,節約了記憶體空間,
注:太多的物件不僅會影響性能,更嚴重的情況下可能會造成記憶體溢位
3、缺點
1)邏輯會更加的復雜,
2)對于一些享元池中不能直接取出的物件,還是需要去new,這時利用了享元模式獲取物件的時間會變得更長,
4、角色
1)抽象享元類
介面/抽象類
2)具體享元類
實作介面或者抽象類的具體子類,這些子類會被大量的重復使用,而且這些子類物件都是細粒度,
3)享元工廠類
工廠其實就是一個享元池
4)組合享元類
組合享元類是依賴基本享元類產生的一個整體類,例如享元池中,如果有字符A、B,我們其實可以利用這兩個字符物件構成一個AB字串,這就是一個組合享元類,
5、內部狀態和外部狀態
細粒度物件,當物件數量較多時不可避免性質相似,如何區分如此多的不同的細粒度物件呢?此時我們就將這些物件的資訊分為兩個部分:內部狀態和外部狀態,
- 內部狀態指物件共享出來的資訊,存盤在享元物件內部并且不會隨環境的改變而改變;
- 外部狀態指物件得以依賴的一個標記,是隨環境改變而改變的、不可共享的狀態,
如何理解內部狀態和外部狀態呢?比如坦克大戰中,有著不同型別的子彈,這些子彈就是因為內部狀態的不同而改變的,例如是燃燒彈還是煙霧彈就是看其內部狀態了,但是我們的坦克可以朝著不同的方向發射子彈,子彈的方位坐標就是子彈的一個外部狀態了,
再比如為圍棋,只有黑白兩顆子,內部狀態控制顏色,而外部狀態控制棋在棋中的位置,
二、應用情景
1、執行緒池
2、String
在JVM中有一個字串常量池,例如,當String=“a"時,并不是直接在堆中
new String("a"),而是會先去常量池中進行查詢,如果常量池中有“a”,則直接會回傳常量池中字串"a"的參考地址,字串相加,其實是呼叫了StringBuilder()的append,然后再toString,所以相加后的參考不在常量池而在堆中,

3、Integer
在Integer中,也有一些已經存在的物件(-128,127),這些物件被放入緩沖區中,當我們用
Integer a=1;或者Integer a=Integer.ValueOf(1)時,就會先與緩沖區中的物件相匹配,如果緩沖區有,則直接回傳緩沖區中的物件,沒有的話,例如128在緩沖區沒有,就只能return Integer a=new Integer(128);

4、五子棋
在五子棋游戲中,如果共需要500多顆黑白子,那么在沒有利用享元模式或者單例模式時,我們就必須要
new500多個物件,然而,如果利用享元模式,就可以只有兩個物件:一個黑棋,一個白棋,[注:單例模式也可以實作],這樣極大的節省了記憶體的空間,也節省了new的時間,空間和時間性能都有著極大的提升,
三、與其他模式的搭配使用
享元模式通常與單例模式、組合模式、工廠模式搭配使用,
1)單例模式
工廠可以使用單例模式.(前提是工廠不是泛型工廠)
2)組合模式
復合的享元模式是使用了組合模式的
3)工廠模式
工廠其實就是一個享元池
四、代碼決議
//抽象享元
package pattern.flyweight;
public abstract class FlyWeight {
//內部狀態
String instate;
//外部狀態
String outstate;
public FlyWeight(String outstate) {
this.instate=outstate;
}
//與外部狀態相關的邏輯操作
abstract void operation();
//獲取或者設定內部狀態
public String getInstate() {
return instate;
}
public void setInstate(String instate) {
this.instate = instate;
}
}
//具體享元
package pattern.flyweight;
public class A extends FlyWeight{
public A(String outstate) {
super(outstate);
// TODO Auto-generated constructor stub
}
//根據外部狀態進行一系列的邏輯操作
@Override
void operation() {
// TODO Auto-generated method stub
System.out.println(outstate);
}
}
//享元工廠
package pattern.flyweight;
import java.util.HashMap;
import java.util.Map;
//泛型時,不能用單例模式,否則泛型將會沒有意義
public class FlyWeightFactory {
private FlyWeightFactory() {};
// volatile是避免重排序
private static volatile FlyWeightFactory INSTANCE = null;
public static FlyWeightFactory getINSTANCE() {
if (INSTANCE == null) {
synchronized (FlyWeightFactory.class) {
if (INSTANCE == null)
INSTANCE = new FlyWeightFactory();
}
}
return INSTANCE;
}
static Map<Character,FlyWeight> FlyWeightMap=new HashMap<Character,FlyWeight>();
public FlyWeight getConcreteFlyWeight(char c) {
if(!FlyWeightMap.containsKey(c))
FlyWeightMap.put(c,new A());
return FlyWeightMap.get(c);
}
}
//測驗類
package pattern.flyweight;
public class Main {
public static void main(String[] args) {
/*
* Integer a=Integer.valueOf(3);//有快取機制 Integer c=new Integer(3);//沒有用到快取機制
* Integer b=new Integer(3); Integer d=3;//有用到快取機制 Integer e=129; Integer
* f=129;//超過了快取池 System.out.println(a==d); System.out.println(e==d);
* System.out.println(c==b);
*
* String a0="a"; String a1="b"; String a2="a"+"b"; String b="ab"; String
* c="ab"; String a=a0+a1;
* System.out.println(a==b);//字串相加其實是StringBuilder.append()然后toString
* System.out.println(a.intern()==b);//intern是找常量池中的參考
* System.out.println(b==c);//兩個都指向常量池中
* System.out.println(a2==b);//如果是定義的時候直接相加,編譯器會進行優化,直接看成String a2="ab";
*/
FlyWeight a=FlyWeightFactory.getINSTANCE().getConcreteFlyWeight('a',"outstate");
FlyWeight b=FlyWeightFactory.getINSTANCE().getConcreteFlyWeight('a',"outstate");
System.out.println(a==b);
}
}

UML圖:

幾行代碼真的很難說清享元模式,而且也沒有找到比較合適的例子,所以只能在這里簡單介紹了基本享元模式,文中測驗也只測驗了a和b是否是指向同一參考,在一些規模比較小的程式中享元模式的優點真的是很難體現出來,如果還是有很多疑惑可以去查看java原始碼,執行緒池和Integer的緩沖區等都非常牛的運用了享元模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/265497.html
標籤:其他
上一篇:普歌-允異團隊-【Java實體】一起做一個簡單的王者榮耀RPG吧!從設計思路到代碼實作一條龍!-登錄與注冊(IO流)/記錄時間/屬性面板呈現
