--- tags: Structural Patterns --- # 蠅量級(享元)模式 Flyweight [參考](https://ithelp.ithome.com.tw/articles/10245053) 運用共享技術有效地支持大量細粒度的物件。 減少需要建立的物件數量、避免大量類似物件的創建,進而提高資源使用率。 享元模式又分成單純享元模式以及複合享元模式。 **實作** 把參數(指的這些與類別實體不同的參數)移動到類別實體外面,要用時將他們傳遞進來,就可以通過共享減少單個實體的數目(享元模式的實現要領)。 享元物件內部定義稱為**內部狀態**,類別實體外面的參數稱為享元物件的**外部狀態**。 **內部狀態** 不會隨著環境改變而產生變化,所以內部狀態是可以共享的部分。 **外部狀態** 會隨著環境改變而產生變化的部分,故這部分不可共享。 **例子** 在一間公司內,在錄取之後都會有員工資訊,也會有名片。假設名片是個類別,分別需要CTO、CEO以及COO的名片。而每個名片的實體,也只有資訊不一樣而已,這時就可以套用享元模式。 ## 角色 **FlyweightFactoy:** 負責建立及管理享元物件。當Client呼叫享元物件時,++FlyweightFactory檢查是否存在符合要求的物件,存在則提供,不存在建立新的享元物件。++ **Flyweight:** 為介面或抽象類別的接口,其中定義ConcreteFlyweight的方法,非享元的外部狀態以參數的形式通過方法傳入。 **ConcreteFlyweight:** 實現Flyweight。 **UnsharedConcreteFlyweight:** 稱作複合享元物件,不可共享。但可分解成多個單純享元物件,則可共享。複合享元物件可對多個單純享元物件設定相同的外部狀態。 ## 單純享元模式 所有物件的都是共用的,而且不存在UnsharableFlyweight 會檢查有沒有重複 ![](https://i.imgur.com/K5k11rH.png) ## 複合享元模式 將單純享元物件使用組合形成複合享元物件,此物件不可共享。但若將其分解成單純享元物件,則可以。複合享元物件可對多個單純享元物件設定相同的外部狀態。 簡單來說支援享元模式,然後有另一個建構子負責處理複合模式,在這個模式不會檢查有沒有重複 ![](https://i.imgur.com/FLlDgfC.png) >[name=貴全] ### 優點 1. 降低內存中物件的數量,以減少記憶體使用,且物件可被集中於一處來管理。 2. 外部狀態獨立,可使享元對象在不同環境被共享。 **可以結合Composite、Singleton、Visitor、和其他Patter建立非常優雅的程式。** ### 缺點 1. 需要分離出內部狀態和外部狀態,使得系統變複雜。 2. 為了使對象可以共享,享元模式需要將享元對象的狀態外部化,讀取外部狀態的時間變長。 ## Flyweight pattern is often combined with the Composite pattern. 享元模式通常與復合模式結合使用,以將層次結構表示為具有共享葉節點的圖。 因為共享的結果是享元葉節點無法存儲指向其父節點的指針,但是父指針可以作為其外部狀態的一部分傳遞給享元模式。 ## The differences between ConcreteFlyweight and UnsharedFlyweight. 享元模式有分為可以共享的資料和不能共享的資料,可以共享的資料稱為內部狀態,不能共享的資料稱為外部狀態。非共享的具體實例沒有內在的數據可以共享,但是它可以消耗外在的“數據”來產生它的操作輸出,因此它實現了相同的接口。 >[name=YuanChangLee] ## Flyweight + Singleton 範例 範例:輸出大型文字。 簡單來看就是BigString是Client BigCharFactory是負責產生BigChar的class ```java= //Flyweight public class BigChar { // 字元名稱 private char charname; // 輸出成大型文字的字串(有'#' '.' '\n'的行) private String fontdata; // 建構子 public BigChar(char charname) { this.charname = charname; try { BufferedReader reader = new BufferedReader( new FileReader("big" + charname + ".txt") ); String line; StringBuffer buf = new StringBuffer(); while ((line = reader.readLine()) != null) { buf.append(line); buf.append("\n"); } reader.close(); this.fontdata = buf.toString(); } catch (IOException e) { this.fontdata = charname + "?"; } } // 輸出大型文字 public void print() { System.out.print(fontdata); } } //FlyweightFactory public class BigCharFactory { // 管理現有的BigChar的物件個體 private Hashtable pool = new Hashtable(); // Singleton Pattern private static BigCharFactory singleton = new BigCharFactory(); // 建構子 private BigCharFactory() { } // 取得唯一的物件個體 public static BigCharFactory getInstance() { return singleton; } // 產生(共用)BigChar的物件個體 public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { bc = new BigChar(charname); // 在此產生BigChar的物件個體 pool.put("" + charname, bc); } return bc; } } //Client public class BigString { // 「大型文字」的陣列 private BigChar[] bigchars; // 建構子 public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } } // 顯示 public void print() { for (int i = 0; i < bigchars.length; i++) { bigchars[i].print(); } } } public class Main { public static void main(String[] args) { if (args.length == 0) { System.exit(0); } BigString bs = new BigString(args[0]); bs.print(); } } ``` >[name=閔致] ### 常常跟composite pattern 一起討論 ## Flyweight 享元模式 範例 根據Flyweight + Singleton 範例程式碼更改 在 BigString 中新增如下的建構子。BigString(String string, boolean shared),若 shared 為 true 則共用 BigChar;若為 false 則不共用。 結果可以得知使用**共用**可以節省記憶體。 ```java= public class BigString { // 大型文字」的陣列 private BigChar[] bigchars; // 建構子 public BigString(String string) { init_shared(string); } // 建構子 public BigString(String string, boolean shared) { if (shared) { init_shared(string); } else { init_unshared(string); } } // 共用且初始化 private void init_shared(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } } // 不共用的初始化 private void init_unshared(String string) { bigchars = new BigChar[string.length()]; for (int i = 0; i < bigchars.length; i++) { bigchars[i] = new BigChar(string.charAt(i)); } } // 輸出到畫面上 public void print() { for (int i = 0; i < bigchars.length; i++) { bigchars[i].print(); } } } public class Main { public static void main(String[] args) { if (args.length == 0) { System.out.println("Usage: java Main digits"); System.out.println("Example: java Main 1212123"); System.exit(0); } BigString bs; bs = new BigString(args[0], false); // 不共用 bs.print(); bs = new BigString(args[0], true); // 共用 bs.print(); } } ``` ## Flyweight Pattern與Singleton Pattern相反: Flyweight:需要建立某個Class的多個實例。 Singleton:需要建立某個Class的最多一個實例。 >[name=閔致]