[toc]
## 舉個例子
> 假設有一個活動, 內容是:6人一組,每個組完成一幅作畫,每個組會拿到一張彩繪原型圖,然后根據原型圖完成一幅彩繪圖。
> 素材:原型圖每組一張、鉛筆每組一支、空白畫布每組一張、畫刷每組若干;
> 而顏料卻是所有組共用的,有紅、黃、藍、綠、紫五種顏色各一大桶,足夠使用。
> 為什么顏料是共用的? 原因也很簡單,顏料很貴,必須充分利用。
## 代碼示例
```php
<?php
// 顏料
class Pigment {
private $color;
private $user;
public function __construct($color) {
$this->color = $color;
$this->user = "";
}
public function getColor() {
return $this->color;
}
public function setUser($user) {
$this->user = $user;
return $this;
}
public function showInfo() {
echo $this->user . "取得" . $this->color . "色顏料" . PHP_EOL;
}
}
// 資料的工廠類
class PigmengFactory {
private $sigmentSet;
public function __construct() {
$this->sigmentSet = [
"紅" => new Pigment("紅"),
"黃" => new Pigment("黃"),
"藍" => new Pigment("藍"),
"綠" => new Pigment("綠"),
"紫" => new Pigment("紫")
];
}
public function getPigment($color) {
$pigment = $this->sigmentSet[$color];
if (!$pigment) {
echo "沒有{$color}顏色的顏料!";
}
return $pigment;
}
}
// 測試代碼
$factory = new PigmengFactory();
$pigmentRed = $factory->getPigment("紅")->setUser("夢之隊");
$pigmentRed->showInfo();
$pigmentYellow = $factory->getPigment("黃")->setUser("夢之隊");
$pigmentYellow->showInfo();
$pigmentBlue1 = $factory->getPigment("藍")->setUser("夢之隊");
$pigmentBlue1->showInfo();
$pigmentBlue2 = $factory->getPigment("藍")->setUser("和平隊");
$pigmentBlue2->showInfo();
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
夢之隊取得紅色顏料
夢之隊取得黃色顏料
夢之隊取得藍色顏料
和平隊取得藍色顏料
Process finished with exit code 0
```
## 什么是享元模式?
> 運用共享技術有效地支持大量細粒度對象的復用
1. 通過限定顏料的數量并采用共享的方式來達到節約資源、節約成本的目的,在程序的世界中這種方式叫享元模式。
1. 享元模式要求能夠共享的對象必須是輕量級對象,也就是細粒度對象,因此享元模式又稱為輕量級模式
1. 享元模式以共享的方式高效地支持大量的細粒度對象,享元對象能做到共享的關鍵是區分內部狀態和外部狀態。
1. **內部狀態(Intrinsic State)**是存儲在享元對象內部并且不會隨環境改變而改變的狀態,因此內部狀態可以共享人狀態。如上面示例中顏料的顏色就是 Pigment 對象的內部狀態。
1. **外部狀態(Extrinsic State)**是隨環境改變而改變的、不可以共享的狀態。
1. 享元對象的外部狀態必須由客戶端保存,并在享元對象被創建之后,在需要使用的時候再傳入到享元對象內部。

##主要角色
1. **享元對象(Flyweight)**: 即你期望用來共享的對象,享元對象必須是輕量級對象,也就是細粒度對象。
1. **享元工廠(FlyweightFactory)**: 享元模式的核心角色,負責創建和管理享元對象。享元工廠提供一個用于存儲享元對象的享元池,用戶需要對象時,首先從享元池中獲取,如果享元池中不存在,則創建一個新的享元對象返回給用戶,并在享元池中保存該新增對象。
## 設計要點
1. 享元對象必須是輕量級,細粒度的對象;
1. 區分享元對象的內部狀態和外部狀態;
1. 享元對象的內部狀態和屬性,一經創建后不會被隨意改變。因為如果可以改變,則 A 取得這個對象 obj 后,改變了其狀態;B 再去取這個對象 obj 時就已經不是原來的狀態了。
1. 使用對象時通過享元工廠去獲取,使得傳入相同的 key 時獲得相同的對象。
## 優缺點
**優點**:
1. 可以極大減少內存中對象的數量,使得相同對象或相似對象(內部狀態相同的對象)在內存中只保存一份。
1. 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享。
**缺點**:
1. 享元模式使得系統更加復雜,需要分離出內部狀態和外部狀態,這使得程序的邏輯復雜化。
1. 享元對象的內部狀態一經創建后不能被隨意改變。
## 應用場景
1. 一個系統有大量相同或者相似的對象,由于這類對象的大量使用,造成內存的大量耗費。
1. 對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中。
2. 享元模式是一個考慮系統性能的設計模式,通過使用享元模式可以節約內存空間,提高系統的性能;
3. 比如瀏覽器的緩存就可以使用這個設計思想,瀏覽器會對已打開頁面的圖片、文件緩存到本地;
4. 如在一個頁面中多次出現相同的圖片(即一個頁面中多個 img 標簽指向同一個圖片地址),則只需要創建一個圖片對象,在解析到 img 標簽的地方多次重復顯示這個對象即可。