概念內部狀態、外部狀態、享元池角色 & UMLDemo: 編輯器圖片重用 - JavaReference概念享元模式(Flyweight Pattern),是以 共用 的方式,對 大量細粒度對象 重用,來減少記憶體的使用(避免大量重覆地創建、銷毀對象)。名稱中的Flyweight,是搏擊比賽中體重級別... ...
概念
享元模式(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源碼看設計模式——享元模式