-
概念
- 內部狀態、外部狀態、享元池
- 角色 & UML
- Demo: 編輯器圖片重用 - Java
- Reference
概念
享元模式(Flyweight Pattern),是以 共享 的方式,對 大量細粒度物件 重用,來減少記憶體的使用(避免大量重復地創建、銷毀物件),
名稱中的Flyweight,是搏擊比賽中體重級別之一,中文稱為 蠅量級 或 次最輕量級 ,把這個單詞移植到軟體工程中,也是用來表示特別小的物件,即細粒度物件,由此可見,享元模式的特點不僅是 共享,更是強調 細粒度的共享,
內部狀態、外部狀態、享元池
享元類中可以共享的相同的內容稱為 內部狀態(Intrinsic State),需要外部環境設定的特異內容稱為 外部狀態(Extrinsic State),二者相互獨立,彼此解耦,組合在一起共同構成了享元類,不同但是有關系的(比如同一類的)享元物件聚合在一起構成了一個享元池,使用一個享元工廠來維護這個享元池,
這樣在使用的時候,根據需求,呼叫享元工廠(在其維護的享元池中)獲取需要的享元物件,然后對其配置外部狀態,來使用,
由于區分了 內部狀態 和 外部狀態,因此可以通過設定不同的外部狀態使得相同的物件具有一些不同的特征,而相同的內部狀態使可以共享的,所以,享元模式的本質是 將完整的物件分解出更細的粒度,來解耦變與不變,并共享不變,實作減少物件數量并節約記憶體的目的,
享元模式借鑒單例模式的共享,借鑒工廠模式的統一供應,其特點是對細粒度物件重用,
與單例模式的區別在于,細粒度重用,而不是把完整的物件重用,
角色 & UML
抽象享元類
具體享元類
享元工廠類
客戶端

Demo: 編輯器圖片重用 - Java
需求:
享元模式在編輯器軟體中大量使用,如在一個檔案中多次出現相同的圖片,則只需要創建一個圖片物件,通過在應用程式中設定該圖片出現的位置,來實作該圖片在不同地方多次重復顯示,
UML:

代碼結構:
./
├── Client.java
├── ImageNodeFactory.java
├── flyweight
│ ├── BirdImage.java
│ ├── ImageNode.java
│ ├── TreeImage.java
│ ├── bird.jpeg
│ └── tree.jpg
└── result.pdf
在這里,PDF編輯使用itextpdf包,pom.xml中配置
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
ImageNode <Interface>: 抽象享元類
package homework.Flyweight.flyweight;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import java.io.IOException;
public interface ImageNode {
// 回傳包裝了坐標(外部狀態 << 引數)的圖片(內部狀態)物件
public Image getImageInPdf(int x, int y) throws IOException, BadElementException;
}
BirdImage <Class>: 具體享元類
package homework.Flyweight.flyweight;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
public class BirdImage implements ImageNode {
// 享元維護的內部狀態(圖片,共享)
private byte[] image;
// 加載內部狀態(圖片)
public BirdImage() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(
ImageIO.read(new File("src/main/java/homework/Flyweight/flyweight/bird.jpeg")),
"jpeg",
baos);
this.image = baos.toByteArray();
}
@Override
public Image getImageInPdf(int x, int y) throws IOException, BadElementException {
Image imageInPdf = Image.getInstance(this.image);
// 內部狀態(圖片物件) + 外部狀態(圖片位置)
imageInPdf.scaleAbsolute(30, 30);
imageInPdf.setAbsolutePosition(x, y);
// 列印圖片在記憶體中的地址
System.out.println(this.image);
return imageInPdf;
}
}
TreeImage <Class>: 具體享元類
package homework.Flyweight.flyweight;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
public class TreeImage implements ImageNode {
// 享元維護的內部狀態(圖片,共享)
private byte[] image;
// 加載內部狀態(圖片)
public TreeImage() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(
ImageIO.read(new File("src/main/java/homework/Flyweight/flyweight/tree.jpg")),
"jpg",
baos);
this.image = baos.toByteArray();
}
@Override
public Image getImageInPdf(int x, int y) throws IOException, BadElementException {
Image imageInPdf = Image.getInstance(image);
// 內部狀態(圖片物件) + 外部狀態(圖片位置)
imageInPdf.scaleAbsolute(30, 30);
imageInPdf.setAbsolutePosition(x, y);
// 列印圖片在記憶體中的地址
System.out.println(this.image);
return imageInPdf;
}
}
ImageNodeFactory <Class>: 享元工廠
package homework.Flyweight;
import homework.Flyweight.flyweight.BirdImage;
import homework.Flyweight.flyweight.ImageNode;
import homework.Flyweight.flyweight.TreeImage;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ImageNodeFactory {
// 享元工廠維護的享元池
private static Map<String, ImageNode> imagePool = new ConcurrentHashMap<>();
// 獲取享元
public static ImageNode getImageNode(String type) throws IOException {
if(!imagePool.containsKey(type)) {
switch (type) {
case "bird":
imagePool.put(type, new BirdImage());
break;
case "tree":
imagePool.put(type, new TreeImage());
break;
}
}
return imagePool.get(type);
}
}
Client <Class>: 客戶端
package homework.Flyweight;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfWriter;
import homework.Flyweight.flyweight.ImageNode;
import java.io.*;
public class Client {
public static void main(String[] args) throws IOException, DocumentException {
// 1. 初始化PDF
Rectangle rectangle = new Rectangle(PageSize.A4); // 設定 PDF 紙張矩形,大小采用 A4
rectangle.setBackgroundColor(BaseColor.WHITE); // 設定背景色
// 創建一個檔案物件,設定初始化大小和頁邊距
Document document = new Document(rectangle, 10, 10, 10, 10); // 上、下、左、右頁間距
String pdfPath = "src/main/java/homework/Flyweight/result.pdf"; // PDF 的輸出位置
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfPath));
document.open(); // 打開檔案物件
// 2. PDF中插入圖片
for(int i=0; i<10; i++){
ImageNode imageNode = ImageNodeFactory.getImageNode("tree");
// 圖片坐標(外部狀態)由客戶端維護
document.add(imageNode.getImageInPdf(10*i,100*i));
System.out.println("Object ImageNode: "+imageNode);
}
document.close(); // 關閉檔案
}
}
控制臺列印:
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
[B@16b4a017
Object ImageNode: homework.Flyweight.flyweight.TreeImage@5ecddf8f
Process finished with exit code 0
生成的PDF:

Reference
- https://www.jianshu.com/p/f9997c0c2a55
- 享元模式-連連看的圖片共享 https://blog.csdn.net/ABAP_Brave/article/details/78528416
- 結合JDK原始碼看設計模式——享元模式
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/184102.html
標籤:Java
