水不激不躍,人不激不奮
一,定義
使用共享對象可有效地支持大量的細粒度的對象
享元模式是對象池的一種實現,用來盡可能減少內存使用量,它適合用于可能存在大量重復對象的場景,來緩存可共享的對象,達到對象共享,避免創建過多對象的效果,這樣一來就可以提升性能,避免內存溢出等。
在享元模式中會建立一個對象容器,在經典的享元模式中該容器為一個Map,它的鍵是享元對象的內部狀態,它的值就是享元對象本身。客戶端程序通過這個內部狀態從享元工廠中獲取享元對象,如果有緩存,則使用緩存對象,否則創建一個享元對象并且存入容器中,這樣一來就避免了創建過多對象的問題。
二,使用場景
1,系統中存在大量的相似對象
2,細粒度的對象都具備較接近的外部狀態,而且內部狀態與環境無關,就是說對象沒有特定身份
3,需要緩沖池的場景
三,角色介紹
Flyweight:享元對象抽象基類或接口
ConcreteFlyweight:具體的享元對象
FlyweightFactory:享元工廠,負責管理享元對象池和創建享元對象
四,使用案例
假設在吃雞游戲中,每局游戲都會有100個玩家參與游戲,所以每局游戲都會創建100個玩家角色,如果一個玩家進行了10局游戲,他的角色就會被創建10次。這樣就會造成頻繁的GC,從而影響性能。如果我們使用享元模式,將玩家的姓名作為key,玩家角色作為value存放在內存中,這樣就不會頻繁的去創建玩家角色,避免造成不必要的資源浪費。
首先,創建一個玩家角色接口作為享元對象抽象接口,該接口定義玩家自己選擇皮膚玩游戲:
public interface Player {/*** 玩游戲* @param skin 自己選擇的皮膚* */void playGame(String skin);
}
然后創建具體的享元對象 :
public class ConcretePlayer implements Player{private String name;public ConcretePlayer(String name) {this.name = name;}@Overridepublic void playGame(String skin) {System.out.println("玩家"+name+"使用"+skin+"角色玩游戲");}
}
最后創建享元工廠:
public class PlayerFactory {private static HashMap<String,Player> map =new HashMap<>();public static Player createPlayer(String name){if(map.get(name)==null){System.out.println("創建新的角色");ConcretePlayer concretePlayer =new ConcretePlayer(name);map.put(name,concretePlayer);return concretePlayer;}else {System.out.println("從緩存中拿取角色");ConcretePlayer player = (ConcretePlayer) map.get(name);return player;}}
}
使用:
Player player = PlayerFactory.createPlayer("N港之王");
player.playGame("紅螳螂");
Player player1 = PlayerFactory.createPlayer("N港之王");
player1.playGame("黃螳螂");
Player player2 = PlayerFactory.createPlayer("N港之王");
player2.playGame("紫螳螂");
輸出:
這樣,即使玩家使用同一角色玩了好多把游戲,都只創建了一次角色,其余的都是從緩存中獲取的
五,總結
享元模式實現比較簡單,但是它的作用在某些場景確實及其重要。它可以大大減少應用程序創建的對象,降低程序內存的占用,增強程序的性能,但它同時也提高了系統的復雜性,需要分離出外部狀態和內部狀態,而且外部狀態具有固化特性,不應該隨內部狀態改變而改變,否則導致系統的邏輯混亂。
享元模式的優點在于它大幅度地降低內存中對象的數量,但是,它做到這一點所付出的代價也是很高的。
享元模式使得系統更加復雜。為了使對象可以共享,需要將一些狀態外部化,這使得程序的邏輯復雜化。
享元模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。
參考文獻:Android源碼設計模式解析與實戰