簡介
享元模式,是對象池的一種實現(xiàn)坛芽,主要用于減少創(chuàng)建對象的數(shù)量,以減少內(nèi)存占用和提高性能翼抠。定義:運用共享技術(shù)有效地支持大量細粒度的對象咙轩。意思就是采用對象共享的形式來實現(xiàn)大量對象的情況。有大量對象的情況阴颖,有可能導(dǎo)致內(nèi)存溢出或者重復(fù)創(chuàng)建之前已經(jīng)創(chuàng)建的相同對象活喊。
先舉個簡單的例子,android
中的ViewHolder
緩存view
量愧,為了優(yōu)化性能存在一種寫法钾菊,使用一個集合存儲已經(jīng)被實例化過的view
帅矗,就不需要每次都去創(chuàng)建了,這就是享元模式的一種簡單應(yīng)用煞烫。
public abstract class RvBaseViewHolder<T> extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
public RvBaseViewHolder(@NonNull View itemView) {
super(itemView);
mViews = new SparseArray<>();
}
public abstract void bindData(int position, @Nullable T data);
/**快速獲取view*/
public <V extends View> V findViewById(int viewId){
View view = mViews.get(viewId);
if(null == view){
view = itemView.findViewById(viewId);
if(null != view){
mViews.put(viewId,view);
}else{
return null;
}
}
return (V) view;
}
}
UML圖
- Flyweight:享元模式抽象類或接口
- ConcreateFlyweight:具體的享元對象
- FlyweightFactory:享元工廠浑此,負責(zé)管理享元對象池和創(chuàng)建享元對象
Flyweight代表輕量級的意思。
簡單示例
過年搶票滞详,大家肯定都不陌生凛俱,各種刷票插件、軟件什么的料饥。在用戶設(shè)置好出發(fā)和到達之后蒲犬,每次查詢請求都返回一系列的車票結(jié)果。當(dāng)數(shù)千萬的用戶在不斷請求查詢結(jié)果時岸啡,如果每次查詢結(jié)果都是重新創(chuàng)建返回的原叮,可想而知,肯定會有大量的重復(fù)對象的創(chuàng)建巡蘸、銷毀奋隶,內(nèi)存占用和GC的壓力都會隨之增大。而享元模式就能很好的應(yīng)對這種情況赡若,車次都是固定的达布,根據(jù)出發(fā)地和到達地查詢出來的車次基本都是相同的(當(dāng)然你還可以添加更多的篩選條件)。
我們可以將這些共享的對象緩存起來逾冬,用戶查詢時優(yōu)先使用緩存黍聂,如果沒有緩存則重新創(chuàng)建,這樣就不必要在重復(fù)創(chuàng)建和銷毀對象了身腻。
首先产还,創(chuàng)建一個Ticket
接口,定義輸出車票信息的方法
public interface Ticket {
public void showTicketInfo(String info);
}
再是具體的實現(xiàn)TrainTicket
類
public class TrainTicket implements Ticket {
//出發(fā)地
private final String from;
//到達地
private final String to;
//鋪位
private String bunk;
//價格
private int price;
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String bunk) {
price = new Random().nextInt(200);
System.out.println("查詢 從 "+from+" 到 " + to + " 的 " + bunk + " 車票嘀趟,價格:" + price);
}
}
接著就是TicketFactory
類脐区,不同于之前的工廠模式,工廠模式每個返回的對象都是新創(chuàng)建的她按,而享元模式需要做緩存牛隅,具體代碼如下:
public class TicketFactory {
private static Map<String,Ticket> ticketMap = new ConcurrentHashMap<>();
public static Ticket getTicket(String from,String to){
final String key = from + "-" + to;
if(ticketMap.containsKey(key)){
return ticketMap.get(key);
}else{
Ticket ticket = new TrainTicket(from, to);
ticketMap.put(key,ticket);
return ticket;
}
}
}
這就面了,每次獲取車票對象時的創(chuàng)建酌泰,享元模式有效的減少了重復(fù)對象的創(chuàng)建媒佣。
Android中的享元模式
當(dāng)我們需要用到Handler
發(fā)送信息的時候可能會注意到一點,并不是每次都是創(chuàng)建一個Message
對象陵刹,Handler
有個obtainMessage
方法其實還有幾個重寫方法默伍,這個方法可以從已經(jīng)創(chuàng)建過的Message
中重新獲取一個Message
對象,以此來降低創(chuàng)建Message
對象的內(nèi)存開銷。
Handler
的obtainMessage
方法調(diào)用了Message
的obtain
方法也糊,最后具體的代碼如下:
public final class Message implements Parcelable {
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
//代碼省略...
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
//代碼省略...
}
其中sPoolSync
為同步鎖對象炼蹦,而sPool
是一個Message
對象,這里可能會有點奇怪為什么會叫sPool
卻是一個Message
對象狸剃。其實這里Message
的實現(xiàn)是以列表的形式實現(xiàn)的掐隐,next
也是一個Message
對象指向的就是下一個Message
對象。這樣子钞馁,這段代碼大概就能理解了瑟枫。
這里源碼只說這么多,有興趣的可以閱讀源碼指攒。
總結(jié)
享元模式還是比較簡單的,在創(chuàng)建重復(fù)對象的情況中大大降低了內(nèi)存的消耗僻焚,提高了程序的性能允悦。同時也提高了系統(tǒng)的復(fù)雜性,需要分離出外部狀態(tài)和內(nèi)部狀態(tài)虑啤,而且外部狀態(tài)應(yīng)具有固化性隙弛,不應(yīng)該隨內(nèi)部狀態(tài)改變而改變,這樣就使得程序變的很混亂狞山。