設計模式 - 享元模式 (Structural Patterns - Flyweight Design Pattern)

前言

甚麼是Flyweight Pattern?

  • 幫助你節省記憶體開銷
  • 透過共享物件的方式

最常見的例子 String : 每次新增一個String的時候,都會將String丟進String pool,再讓物件指向這個String memory。

String str1 = "test";
String str2 = "test";

System.out.println(str1 == str2) // true

實踐方式

  • 撰寫Flyweight介面與物件
  • 撰寫Flyweight工廠(主要節省記憶體邏輯在此),負責管理和創建Flyweight

類別圖

範例

我們可以思考一下,在英雄遊戲中有那些東西可以使用享元模式。

  • 重複製造
  • 同物件的性質一樣

我想武器添加物系統可以使用享元模式,因為武器中插的寶石不管插在哪種類的武器裡,他們的功效都是一樣的,而如果為每把武器都創建新的寶石物件,那太消耗記憶體了。

程式行為

  • 為武器加上寶石
  • 若是已經存在的寶石則不在new 物件
  • 透過DiamondFactory去控制要不要new新物件的邏輯
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

abstract class Weapon {
    ArrayList<Diamond> diamonds = new ArrayList<>();
    public void addDiamond(Diamond diamond) {
        diamonds.add(diamond);
    }
    abstract void attack();
}

class Sword extends Weapon {
    @Override
    void attack() {
        System.out.println("Sword attack");
        diamonds.forEach((x) -> {
            System.out.println("Use " + x.name + " to attack");
        });
    }
}

class Diamond {
    String name;

    public Diamond(String name) {
        this.name = name;
    }
}

class DiamondFactory {
    private static Map<String, Diamond> diamondMap = new HashMap<>();

    public Diamond getDiamond(String diamondName) {
        if (diamondMap.containsKey(diamondName)) {
            System.out.println("Without create " + diamondName + " diamond");
            return diamondMap.get(diamondName);
        } else {
            Diamond newDiamond = new Diamond(diamondName);
            diamondMap.put(diamondName, newDiamond);
            return newDiamond;
        }
    }
}

public class Flyweight {

    public static void main(String[] args) {
        Weapon sword = new Sword();
        DiamondFactory diamondFactory = new DiamondFactory();
        sword.addDiamond(diamondFactory.getDiamond("Yellow"));
        sword.addDiamond(diamondFactory.getDiamond("Blue"));
        sword.addDiamond(diamondFactory.getDiamond("Black"));
        sword.addDiamond(diamondFactory.getDiamond("Black"));
        sword.attack();
    }
}

原始碼下載

結論

使用Flyweight可以節省Java Process的記憶體空間。

當自己的程式常常OOM(OutOfMemory)時,除了考慮修改原本邏輯之外,可以參考使用Flyweight Pattern,將沒有必要每次都new的物件透過Flyweight去改善使用記憶體的方式。

留言

這個網誌中的熱門文章

Java Lambda Map篇

(InterviewBit) System Design - Design Cache System