iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0

享元模式透過重複使用物件來減少資料量並節省記憶體。

生活範例

圖書館有很多藏書,同一本書可能有多本館藏,以滿足讀者需求。假設每次購書都要將書本的基本資料輸入圖書管理系統,例如書名、作者和出版社,我們不必為相同的書籍重複建檔,而是應該為每一本館藏加上編號,並讓相同的書籍共用同一份資料。這些書籍資料就像享元物件一樣,透過重複使用書籍資料,可以避免重複並減少資料量。

舉個例子

當瀏覽器加載圖片時,會先檢查該圖片是否存在快取中,再根據結果決定要使用快取或是從伺服器下載圖片。這種機制不僅能提升網頁的回應速度,還能減輕對伺服器的負擔。我們試著用享元模式模擬這套快取機制。

ImageData 是一個享元物件,負責從伺服器下載並儲存圖片資料。這些資料可以重複渲染同一張圖片,以實現資源共享。

class ImageData {
  readonly url: string;

  constructor(url: string) {
    this.url = url;
    console.log(`Loading image from ${url}`);
  }
}

Image 類別負責在畫面上渲染圖片。每個實例包含圖片的座標位置和對應的圖片資料。

class Image {
  private x: number;
  private y: number;
  private imageData: ImageData;

  constructor(x: number, y: number, imageData: ImageData) {
    this.x = x;
    this.y = y;
    this.imageData = imageData;
  }

  render() {
    console.log(
      `Rendering image from: ${this.imageData.url} at position ` +
        `(${this.x}, ${this.y})`
    );
  }
}

ImageDataStore 是圖片資料的管理中心,負責圖片資料的建立與快取管理。它只會在必要時加載圖片,以避免重複加載相同的資料。

class ImageDataStore {
  private dataMap: Map<string, ImageData> = new Map();

  getOne(url: string) {
    if (!this.dataMap.has(url)) {
      this.dataMap.set(url, new ImageData(url));
    }
    return this.dataMap.get(url)!;
  }
}

這個類別用來模擬一個網頁文件,它提供新增圖片和渲染文件的方法,渲染方法渲染文件中的所有圖片。

class Document {
  private images: Image[] = [];
  private imageDataStore: ImageDataStore = new ImageDataStore();

  addImage(x: number, y: number, url: string) {
    const imageData = this.imageDataStore.getOne(url);
    const image = new Image(x, y, imageData);
    this.images.push(image);
  }

  render() {
    this.images.forEach((image) => {
      image.render();
    });
  }
}

以下範例展示 ImageDataStore 如何通過重複使用圖片資料來避免重複載入並節省資源。

class DocumentTestDrive {
  static main() {
    const document = new Document();

    document.addImage(20, 20, "https://foo.com/images/sample1.png");
    document.addImage(40, 40, "https://foo.com/images/sample2.png");
    document.addImage(60, 60, "https://foo.com/images/sample1.png");

    document.render();
  }
}

DocumentTestDrive.main();

執行結果:

Loading image from https://foo.com/images/sample1.png
Loading image from https://foo.com/images/sample2.png
Rendering image from: https://foo.com/images/sample1.png at position (20, 20)
Rendering image from: https://foo.com/images/sample2.png at position (40, 40)
Rendering image from: https://foo.com/images/sample1.png at position (60, 60)

定義

Flyweight Pattern

  • 享元物件介面(Flyweight): 定義享元物件的操作行為
  • 具體享元物件(ConcreteFlyweight): 封裝不變的狀態,並提供介面中的具體實現
  • 享元物件工廠(FlyweightFactory): 負責建立物件,避免建立相同的物件

享元模式的主要目的是節省記憶體用量,特別適合處理數量龐大且重複性高的物件。它將物件的狀態分為可變與不變,將可變的狀態抽離,並將不變的狀態封裝成享元物件。透過重複使用享元物件,可以減少重複性資料,藉此減少記憶體的消耗。當物件中的重複性資料越多,其帶來的效益就越顯著。

總結

  • 將物件的狀態分為可變與不變,重複使用不變的部分,並在需要時使用可變的部分
  • 透過重複使用物件來減少重複性資料,進而節省記憶體
  • 享元模式在面對大量重複性高的物件時帶來的效益最為顯著

完整範例

https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/structural/flyweight.ts


上一篇
Day 22 - Mediator 中介者
下一篇
Day 24 - Bridge 橋接
系列文
前端也想學設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言