從各種資料上我們知道享元模式的特點有:
1.對象是細(xì)粒度的, 比如字符串(python的短字符串對象就是這樣設(shè)計的), 數(shù)字類別這樣, 而不是{attr1, attr2, attr3 ....}這樣的
2.內(nèi)部狀態(tài)是穩(wěn)定的, 數(shù)量不多, 經(jīng)常重復(fù), 比如黑白棋的黑色子與白色子
3.外部狀態(tài)都不一樣, 棋子的位置.
然后享元模式的核心就在于工廠方法.每次被請求創(chuàng)建對象的時候,通過判斷池子里面有沒有來決定是否產(chǎn)生新的對象,進(jìn)而達(dá)到節(jié)約內(nèi)存的目的.
網(wǎng)上的很多資料介紹到這里也就結(jié)束了, 但是這樣留給我這樣的初級讀者的卻是一個極大的錯誤認(rèn)識---那就是內(nèi)存是大量的節(jié)約. 為什么這樣說呢, 就拿剛才的黑白棋的例子來說, 如果棋盤上有100個棋子, 我們認(rèn)為只產(chǎn)生了2個棋子對象(黑子和白子), 然后位置通過參數(shù)傳遞. 這樣內(nèi)存對象的壓縮率就達(dá)到了98%.
但是學(xué)過信息論的同學(xué)都知道, 就算使用各種神奇算法來編碼壓縮也逃不過香農(nóng)定律的極限.然而通過上面的直覺估算,反而是信息越多,壓縮比越高,比如10000000個棋子那么幾乎是100%的壓縮比了. 造成這個結(jié)果的原因就是我們一直在關(guān)注共享內(nèi)部狀態(tài),而忽視了外部狀態(tài)的管理.
我就以一款以前玩過的游戲--三國志來說:
故事設(shè)定---一個中級將領(lǐng)可以帶領(lǐng)100個純種兵員, 要么全是步兵, 或者全是騎兵, 或者全是象兵.在對打開始時(初始化), 100個兵出現(xiàn)在10*10的方陣中, 打的過程中還有可能死掉. 所以我們就用享元模式表示做這個戰(zhàn)斗過程中士兵對象.
大一剛學(xué)編程的我是這樣寫:
class Soldier{
? ? private type; ? //infantry
? ? private cor_x;
? ? private col_y;
? ? private is_alive;
}
我們假設(shè)每個屬性都是一個字節(jié),那么100個士兵就需要400 bytes的存儲.
a.按照我最初對享元模式的理解,那么壓縮比是99%
b.按照圍棋設(shè)計方案,我們把位置和死活剝離出來
class Soldier{
? ? private type; ?//infantry
? ? public void display( new Is_alive_and_position oooo);
}
class Is_alive_and_position{
? ? private cor_x;
? ? private col_y;
? ? private is_alive;
}
這樣的話,100個士兵就有1個Soldier對象和100個Is_alive_and_position對象,占用內(nèi)存是:
1*1 + 100*3 = 301, 壓縮比是25%
c.我們把種類和死活作為基礎(chǔ)共享類,然后位置對象剝離處理:
class Soldier{
? ? private type; ?//infantry
? ? private is_alive;
? ? public void display( new Position oooo);
}
class Position{
? private cor_x;
? private col_y;
}
占用內(nèi)存:2 * 2 + 100*2 = 204, 壓縮比是50%
d.我們進(jìn)一步剝離,把橫坐標(biāo)也作為基礎(chǔ)類的屬性, 因為只有1~10:
class Soldier{
? ? private type; ?//infantry
? ? private is_alive;
? ? private cor_x;
? ? public void display( new Position_y oooo);
}
class Position_y{
? ? private col_y;
}
這時,活的基礎(chǔ)士兵有10個,死的也有10個,共享類有20個. 在縱向的位置類有100個,所以
內(nèi)存:20*3 + 100*1=160, 那么壓縮比是60%
通過上面的對象壓縮比來看: 99%> 60% > 50% > 25%
? ? 第一個是騙人的,我們不管,?
? ? 最后一個節(jié)約不多,但是設(shè)計簡單,內(nèi)部狀態(tài)只有一個,共享對象也只能有一個
? ? 60%節(jié)約最多,但是共享類設(shè)計稍微復(fù)雜,而且共享對象也有20個
以上的分析是不考慮程序執(zhí)行耗時, 真實內(nèi)存狀態(tài)的. 只是建立一個模型來分析. 享元模式的重點不是享元工廠的設(shè)計,而是結(jié)合程序環(huán)境, 內(nèi)存節(jié)約度, 程序執(zhí)行效率, 類和對象管理復(fù)雜度來設(shè)計享元類和外部類. 這就好比在我們使用react或者vue.js時,核心的虛擬DOM樹并沒有減少必要的真實DOM操作,只是優(yōu)化的執(zhí)行過程.