標籤:佔用 不同的 表頭 interface sdn ret main img ticket
第二十二章、享元模式
享元模式是結構型設計模式之中的一個。是對對象池的一種實現。就像它的名字一樣,共用對象。避免反覆的建立。
我們經常使用的String
就是使用了共用模式。所以String
類型的對象建立後就不可改變,假設當兩個String
對象所包括的內容同樣時,JVM僅僅建立一個String
對象相應這兩個不同的對象引用。
1.定義
採用一個共用來避免大量擁有同樣內容對象的開銷。使用享元模式可有效支援大量的細粒度對象。
2.使用情境
(1)系統中存在大量的類似對象。
(2)細粒度的對象都具備較接近的外部狀態,並且內部狀態與環境不關,也就是說對象沒有特定身份。
(3)須要緩衝池的情境。
PS:內部狀態與外部狀態:在享元對象內部並且不會隨著環境改變而改變的共用部分。能夠稱之為享元對象的內部狀態,反之隨著環境改變而改變的。不可共用的狀態稱之為外部狀態。
3.UML類圖
享元模式分為單純享元模式和複合享元模式,是複合享元模式。
(1)Flyweight
:享元對象抽象基類或者介面。
(2)ConcreateFlyweight
:詳細的享元對象,假設有內部狀態的話。必須負責為內部狀態提供儲存空間。
(3)UnsharadConcreateFlyweight
:複合享元角色所代表的對象是不能夠共用的,並且能夠分解成為多個單純享元對象的組合。
單純享元模式沒有此項,這也是兩者在結構上的差別。
(4)FlyweightFactoiy
:享元工廠,負責管理享元對象池和建立享元對象。
(5)Client
:維護對全部享元對象的引用,並且還須要儲存相應的外蘊狀態。
4.簡單實現
情景:過春節買火車票的時候,我們須要查詢車票的情況。那麼假設每次查詢車票時都建立一個結果,那麼必定會大量的建立出很多反覆的對象。頻繁的去銷毀他們,使得GC任務繁重。那麼這時我們能夠使用享元模式,將這些對象緩衝起來,查詢時優先使用緩衝,沒有緩衝在又一次建立。
首先是Ticket介面(Flyweight):
public interface Ticket { public void showTicketInfo(String bunk);}
TrainTicket詳細實作類別(ConcreateFlyweight):
//火車票public class TrainTicket implements Ticket{ public String from; // 始發地 public String to; // 目的地 public String bunk; //鋪位 public int price; //價格 public TrainTicket(String from, String to) { this.from = from; this.to = to; } @Override public void showTicketInfo(String bunk) { price = new Random().nextInt(300); System.out.println("購買 從 " + from + " 到 " + to + "的" + bunk + "火車票" + ", 價格:" + price); }}
TicketFactory 管理查詢火車票(FlyweightFactoiy):
public class TicketFactory { static Map<String, Ticket> sTicketMap = new ConcurrentHashMap<String, Ticket>(); public static Ticket getTicket(String from ,String to){ String key = from + "-" + to; if(sTicketMap.containsKey(key)){ System.out.println("使用緩衝 ==> " + key); return sTicketMap.get(key); }else{ System.out.println("建立對象 ==> " + key); Ticket ticket = new TrainTicket(from, to); sTicketMap.put(key, ticket); return ticket; } }}
查詢:
final class Client { public static void main(String[] args) { Ticket ticket01 = TicketFactory.getTicket("北京", "青島"); ticket01.showTicketInfo("上鋪"); Ticket ticket02 = TicketFactory.getTicket("北京", "青島"); ticket02.showTicketInfo("下鋪"); Ticket ticket03 = TicketFactory.getTicket("北京", "西安"); ticket03.showTicketInfo("坐票"); }}
結果
建立對象 ==> 北京-青島購買 從 北京 到 青島的上鋪火車票, 價格:71使用緩衝 ==> 北京-青島購買 從 北京 到 青島的下鋪火車票, 價格:32建立對象 ==> 北京-西安購買 從 北京 到 西安的坐票火車票, 價格:246
5.Android原始碼中的實現1.Message
由於Android是事件驅動的,因此假設通過new建立 Message
就會建立大量的 Message
對象,導致記憶體佔用率高,頻繁GC等問題。那麼 Message
就採用了享元模式。
Message
通過next
成員變數保有對下一個Message
的引用。最後一個可用Message
的next
則為空白。從而構成了一個Message鏈表。
Message Pool
就通過該鏈表的表頭管理著全部閑置的Message
,一個Message
在使用完後能夠通過recycle()
方法進入Message Pool
,並在須要時通過obtain
靜態方法從Message Pool
擷取。
Message
承擔了享元模式中3個元素的職責,即是Flyweight
抽象。又是ConcreateFlyweight
角色。同一時候又承擔了FlyweightFactoiy
管理對象池的職責。
所以使用Message推薦obtain(),不要去new了。
//1。
使用new Message() //Message mess = new Message(); //2。使用Message.obtain() Message mess = Message.obtain(); mess.what = 1; //Message mess = mHandler.obtainMessage(1); 與上兩行的代碼一樣。能夠參考原始碼查看 mHandler.sendMessage(mess);
6.總結1.長處
(1)大大降低應用程式建立的對象,降低程式記憶體的佔用。增強程式的效能。
(2)使用享元模式,能夠讓享元對象能夠在不同的環境中被共用。
2.缺點
(1)使得系統更加複雜。為了使對象能夠共用,須要將一些狀態外部化。這使得程式的邏輯複雜化。
(2)享元模式將需、享元對象的狀態外部化,而讀取外部狀態使得執行時間略微變長。
7.參考
1. 深入淺出享元模式
《Android原始碼設計模式解析與實戰》讀書筆記(二十二)