當我們需要創建大量相似對象時,享元模式可以幫助我們節省內存空間和提高性能。該模式通過共享相同的數據來減少對象的數量。
在享元模式中,有兩種類型的對象:享元(Flyweight)和非享元(Unshared Flyweight)。享元對象是可共享的,它包含內部狀態和外部狀態。內部狀態是不變的,它可以在多個對象之間共享。外部狀態是會變化的,它由客戶端代碼傳遞給享元對象,因此它不能被共享。
享元模式的核心思想是將相同的外部狀態提取出來作為共享對象,在使用時通過傳遞外部狀態進行對象的定制。這樣就可以避免創建大量相同的對象,從而減少內存占用。
享元模式的適用場景
享元模式適用于需要創建大量相似對象,并希望節省內存空間和提高性能的場景。它通過共享相同的狀態來減少對象的數量,以達到優化性能的目的。
- 當一個類有大量的相似對象,且這些對象可以共享一些相同的狀態時,可以考慮使用享元模式。通過共享相同的狀態,可以減少對象的數量,節省內存空間。
- 當大量對象導致內存占用過高,而且這些對象的狀態可以被外部化時,可以使用享元模式來共享這些外部狀態。外部狀態可以由客戶端代碼傳遞給享元對象,從而避免創建大量重復的對象。
- 當需要在多個對象之間共享和復用狀態時,可以使用享元模式。通過共享狀態,可以實現對象的復用,提高性能。
- 當對象的數量很大,但每個對象只包含少量的狀態時,可以考慮使用享元模式。通過共享狀態,可以減少對象的數量,降低系統的復雜性和維護成本。
- 當希望將對象的內部狀態和外部狀態分離,并通過外部狀態對對象進行定制時,可以使用享元模式。內部狀態是不變的,可以在多個對象之間共享,而外部狀態會變化,可以通過客戶端代碼傳遞給享元對象。
享元模式主要包含以下幾個角色:
在享元模式中,具體享元對象之間可以共享內部狀態,而外部狀態是可變的,由客戶端代碼傳遞。享元工廠負責管理和創建享元對象,避免重復創建相同的享元對象。客戶端通過享元工廠獲取享元對象,并根據需要傳入外部狀態,從而定制享元對象的行為。這樣可以在節省內存空間的同時,實現定制化的復用。
- 享元(Flyweight):它是一個接口或抽象類,定義了具體享元對象的共享方法和獲取外部狀態方法。
- 具體享元(Concrete Flyweight):實現了享元接口,包含內部狀態和外部狀態兩部分。內部狀態是不變的,可以被多個享元對象共享;外部狀態是可變的,需要在使用時傳入。
- 享元工廠(Flyweight Factory):管理和創建享元對象,通過一個數據結構(如哈希表)存儲已經創建的享元對象,并根據需要進行復用或創建新的享元對象。
- 客戶端(Client):通過享元工廠來獲取享元對象,并根據需要傳入外部狀態。客戶端可以通過共享享元對象的內部狀態來節省內存空間和提高性能。
享元模式具體實現
以下實例通過創建歌曲享元工廠,實現歌曲的播放
享元接口
public interface Song {void play();
}
具體享元
/*** 國風歌曲*/
public class ChineseSong implements Song {private String songName;public ChineseSong(String songName) {this.songName = songName;}@Overridepublic void play() {System.out.println("A song called" + songName + " was played");}}
享元工廠
/*** 享元工廠類*/
public class FlyweightFactory {//定義一個集合,用于共享里面的對象private static Map<String, Song> songMap = new HashMap<>();public static ChineseSong getSong(String songName) {ChineseSong chineseSong = (ChineseSong) songMap.get(songName);if (chineseSong == null) {chineseSong = new ChineseSong(songName);songMap.put(songName, chineseSong);System.out.println("Add a new ChineseSong with : " + songName);}return chineseSong;}}
客戶端
/*** 享元模式* 利用享元模式實現播放歌曲*/
public class Flyweight {public static void main(String[] args) {Song 稻香 = FlyweightFactory.getSong("稻香");稻香.play();Song 花田錯 = FlyweightFactory.getSong("花田錯");花田錯.play();Song 稻香2 = FlyweightFactory.getSong("稻香");稻香2.play();}}
運行結果
Add a new ChineseSong with : 稻香
A song called稻香 was played
Add a new ChineseSong with : 花田錯
A song called花田錯 was played
A song called稻香 was played
在 FlyweightFactory中,使用了一個哈希表 Map 來存儲已經創建的 Song對象。在獲取 Song對象時,首先檢查 Map 中是否已存在該歌曲的對象,如果存在則直接返回,如果不存在則創建一個新的 Song對象,并將其加入到 Map 中。
享元模式的優缺點
享元模式的優點:
- 減少內存使用:享元模式通過共享對象來減少內存使用,特別是當有大量相似對象需要創建時。通過共享對象,可以節省大量的內存空間。
- 提高性能:由于享元模式共享對象,避免了頻繁地創建和銷毀對象,從而提高了系統的性能。
- 簡化復雜對象:享元模式可以將復雜對象拆分成多個簡單的共享對象,使得對象的創建和管理更加簡單。
享元模式的缺點:
- 共享對象的狀態不可變:由于享元對象被多個客戶端共享,因此其內部狀態必須是不可變的。如果某個客戶端修改了共享對象的狀態,可能會影響其他客戶端的操作。
- 對象共享可能增加復雜性:在實現享元模式時,需要對對象進行合理的劃分和管理,這可能增加系統的復雜性。
- 不適用于所有情況:享元模式主要適用于有大量相似對象需要共享的場景。對于不需要共享對象或者對象之間差異較大的情況,使用享元模式可能并不適合。
享元模式在需要創建大量相似對象且需要節省內存的場景下具有很好的優勢,但也需要注意其適用性和狀態管理的復雜性。