---
# System prepended metadata

title: 蠅量級(享元)模式 Flyweight
tags: [Structural Patterns]

---

---
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=閔致]