蠅量模式
為了防止被“殺”了祭天,學點設計模式,并總結下還是有必要的。
一:理解
- 蠅量這個詞匯來自于拳擊,是比輕量更輕的一個級別。在設計模式中,蠅量表示的是通過減少不必要的對象實例以減小系統的負擔。
- 蠅量模式也稱享元模式,享元是共享對象實例(或共享部分屬性)的意思。在設計模式中,享元是手段,蠅量是結果。
- 蠅量模式可以是完全共享和局部共享,完全共享可以減少對象實例,局部共享可以共享不同對象中相同的部分。
二:例子
你是個富二代。
你喜歡開車,然而你比較節儉,每次開車都向租車公司借。
租車公司有汽車對應的接口,包含一個drive方法。
public interface CarI {
void drive();
}
對于你而言,開什么車其實也沒什么,主要是牌子brand要對。
為方便舉例,汽車類Car中只包含brand一個屬性。
// 汽車類
public class Car implements CarI {
private String brand;
public Car(String brand) {
this.brand = brand;
}
@Override
public void drive() {
System.out.println("開" + brand + "car");
}
}
老司機開車代碼:
public class Client {
public static void main(String[] args) {
Car car1 = new Car("寶馬");
car1.drive();
Car car2 = new Car("奔馳");
car2.drive();
Car car3 = new Car("奧迪");
car3.drive();
Car car4 = new Car("寶馬");
car4.drive();
}
}
你發現,你每次開車之前,都得先租一輛車,即新建一個對象。
例如雖然都是開寶馬車,卻要新建car1和car4兩個對象。
對于高富帥而言,這有點麻煩,也有點不夠優雅。
在現實系統中,高并發情況下,每個請求都新建一個對象,會導致內存占用量過大。
高并發時新建過多實例會導致來不及GC或者是GC太頻繁。
于是,你叫來程序員小菜幫你解決這個問題。
小菜覺得你作為高富帥,應該在第一次借車的時候,直接把車買下來,下次再開的時候,直接從車庫取就可以了。
說到要買買買,作為高富帥的你覺得還是挺有意思的,同意讓小菜這么干。
買買買
映射到真實系統,在調用方發起第一次請求時,新建對象并將其保存,之后的請求就無需再新建對象。
小菜決定使用蠅量模式進行重構。
于是,小菜上來就是一頓敲。
就是一把梭
public class CarKeeper {
private Map<String, Car> carKeeper = Maps.newConcurrentMap();
public Car getCar(String brand) {
Car car = carKeeper.get(brand);
if (car == null) {
car = new Car(brand);
carKeeper.put(brand, car);
System.out.println("新建了" + brand + "car對象!");
}
return car;
}
}
他新建了一個CarKeeper類,該類中包含一個map,用于保存已經建立的Car對象。
該過程和單例模式新建對象類似,為懶加載模式。
不過這里會為每個品牌的車子保存一個實例。
同樣的,懶加載會遇到多線程安全問題,可用雙重檢查解決。
測試代碼:
public class ClientV2 {
public static void main(String[] args) {
CarKeeper carKeeper = new CarKeeper();
Car car1 = carKeeper.getCar("寶馬");
car1.drive();
Car car2 = carKeeper.getCar("奔馳");
car2.drive();
Car car3 = carKeeper.getCar("奧迪");
car3.drive();
Car car4 = carKeeper.getCar("寶馬");
car4.drive();
Car car5 = carKeeper.getCar("奔馳");
car5.drive();
Car car6 = carKeeper.getCar("奧迪");
car6.drive();
}
}
輸入/輸出:
新建了寶馬car對象!
開寶馬car
新建了奔馳car對象!
開奔馳car
新建了奧迪car對象!
開奧迪car
開寶馬car
開奔馳car
開奧迪car
經過重構,發現只有在第一次借對應品牌車子時才新建對象,之后的請求直接開車就可以了。
你覺得這個模式非常棒,開心地飆起了車。
老司機開車
留下小菜一個人默默地和蒼蠅玩耍。
三:再理解
- 例子中相同牌子的車子可以做到完全共享,使用時直接從map中取出即可。如果不同對象只有局部屬性共享,則需要在取出之后,設置一些非共享屬性。
- 該模式共享了對象,執行的方法為無狀態的,例如例子中的drive方法只輸出字符串。如果方法帶狀態,需要考慮結果是否正確。
- 蠅量模式節約了內存空間,減少了對象創建時間。
- Java中的字符串常量用到了該模式。