享元模式
標籤 : Java與設計模式
內存屬於稀缺資源, 不能隨便浪費. 如果有很多相同/相似的對象, 我們可以通過享元節省內存.
內部狀態 vs. 外部狀態
享元模式(Flyweight): 運用共享技術有效地重用大量細粒度的對象.
- 享元對象能做到共享的關鍵是區分了內部狀態和外部狀態:
- 在享元對象內部並且不會隨環境改變而改變的共享部分, 可稱之爲享元對象的內部狀態.
- 而隨環境改變而改變的、不可以共享的狀態是外部狀態.
在設計開發中,有時需要生產大量細粒度對象來表徵數據, 如果這些對象除個別參數外基本相同, 此時如果能把那些參數移到類實例外面, 在方法調用時將其傳入, 就可以通過共享大幅度減少類實例數目.
模式實現
案例: 圍棋設計
有下棋經驗的同學都知道一盤棋的棋子大小、材質、顏色(黑/白)往往都是確定的, 而圍棋落子的位置卻不一定(看水平高低了O(∩_∩)O!), 因此我們可以將棋子位置從棋子對象中剝離, 然後讓棋子對象共享大小、材質、顏色屬性, 並在調用時將位置傳入, 就可大大減少棋子對象的數量:
Flyweight
所有具體享元類的超類或接口, 通過該接口, Flyweight可以接受並作用於外部狀態:
/**
* @author jifang
* @since 16/8/26 上午10:27.
*/
public interface Flyweight {
void operation(Location location);
}
ConcreteFlyweight
實現Flyweight接口, 併爲內部狀態增加存儲空間:
class GoFlyweight implements Flyweight {
private String color;
private double radius;
private String material;
public GoFlyweight(String color, double radius, String material) {
this.color = color;
this.radius = radius;
this.material = material;
}
public String getColor() {
return color;
}
public double getRadius() {
return radius;
}
public String getMaterial() {
return material;
}
@Override
public void operation(Location location) {
System.out.println("[" + color + "]棋 [" + material + "]材質 半徑[" + radius + "]CM 落在" + location);
}
}
UnsharedConcreteFlyweight
指不需要共享的Flyweight子類, 因爲Flyweight接口共享成爲可能, 但它並不強制共享. UnsharedConcreteFlyweight用於解決那些不需要共享對象的問題:
class Location {
private int locX;
private int locY;
public Location() {
}
public Location(int locX, int locY) {
this.locX = locX;
this.locY = locY;
}
public int getLocX() {
return locX;
}
public void setLocX(int locX) {
this.locX = locX;
}
public int getLocY() {
return locY;
}
public void setLocY(int locY) {
this.locY = locY;
}
@Override
public String toString() {
return "{" +
"locX=" + locX +
", locY=" + locY +
'}';
}
}
- FlyweightFactory
享元工廠,用來創建並管理Flyweight對象,作用是確保合理地共享Flyweight, 當用戶請求一個Flyweight時, FlyweightFactory提供一個共享實例:
public class FlyweightFactory {
private static Map<String, GoFlyweight> map = new ConcurrentHashMap<>();
public static GoFlyweight getGoFlyweight(String color) {
GoFlyweight flyweight = map.get(color);
if (flyweight == null) {
flyweight = new GoFlyweight(color, 1.1, "陶瓷");
map.put(color, flyweight);
}
return flyweight;
}
}
小結
享元模式可以極大減少內存中對象的數量: 相同/相似對象只保留一份, 節約資源, 提高性能. 且將外部狀態剝離, 使外部狀態相對獨立, 不影響內部狀態. 但相比原先的設計, 增加了實現複雜度, 且讀取外部狀態使得運行時間變長(時間換空間).
- 場景
如果一個應用使用了大量對象從而造成很大的存儲開銷時;
如果對象的有大量外部狀態, 且剝離外部狀態就可用相對較少的共享對象取代很多實例時;
- ‘池’化資源, 如: 線程池、數據庫連接池.
String
類設計.