Go 代碼應用設計模式: 享元模式、代理模式
我們繼續講解結構模式中的享元模式。
享元模式中的享指的的是分享,享元模式就是把一部分屬性進行互相分享,從而減少內存的佔用。
我們還是從一個實例出發,假設模擬三國時期的戰爭,兩軍對壘,各方有百萬大軍。如何通過程序實現百萬大軍呢?仍然從反例代碼看起,可能就會定義如下一個結構,然後實例化一百萬個對象。
type Soldier struct {
Name string // 姓名
Age int // 年齡
Suit string // 制服
Arms string // 武器
}
對於一百萬個 Soldier,其中每個 Soldier 的 Name 肯定不同;部分 Age 相同(在 18 至 30 之間);在古代打仗一方的戰鬥服(Suit)肯定是一樣的,武器(Arms)不同工種也不同,有弓箭、長矛、盾牌等。
由於對象的的量比較大,爲了節約內存,我們是否可以把共有的屬性抽離出來,這樣就可以減少內存佔用了。一個 Age 字段佔 8bit(1byte,假設在 64 位操作系統)。那麼一百萬個 Age 字段將會佔用約 1M 的內存空間,在實際的案例中共用屬性特別會多得多,相同值的屬性佔用內存就更多。
我們來看下把相同屬性抽離出來的代碼:
type Soldier struct {
Name string // 姓名
Age AgeInfo // 年齡
Arms ArmsInfo // 武器
}
type AgeInfo struct {
Age int
}
type ArmsInfo struct {
Arms string
Suit string
}
func (a AgeInfo) GetAge() int {
return a.Age
}
func (a ArmsInfo) GetArms() (string, string) {
return a.Arms, a.Suit
}
雖然需要定義一百萬個 Solidier 對象,但是隻用定義 13 個 AgeInfo 對象,同時 ArmsInfo 只需要三個對象 (如果只有 3 種兵種), 很明顯這已經大大的減少了內存的佔用。
但是這裏我們提一下字符串的內存使用優化並沒有你想象的那麼多,這裏順便給大家提一下 Go 字符串的實現。
Go 的字符串同常量一樣在堆裏申請的內存空間,相同的字符串,是不同字符串對象指針,所以這裏我們只是節省了生成的 string 對象個數,並沒有實際減少對 string 內容的優化。具體的 string 原理,大家可以自行閱讀源碼或者搜資料瞭解。
享元模式就是把公共的屬性抽離出來進行復用,減少大批量的相同屬性在內存的佔用。但是我們同樣要注意,享元屬性一定是不變的,如果發生變更將會影響一批,甚至所有的對象。
接下來我們來了解下結構模式中的代理模式。
代理模式的概念大家應該都比較清楚了,例如 Nginx 的反向代理模型。我們的 Web 服務中使用的一些中間鍵(權限認證、緩存判斷、日誌記錄等)都是通過代理模式實現的。在真正的請求到服務之前請求先到代理層完成上述工作。我們來看下一個簡單的代碼的實現:
type API struct{}
func (API) Do() string {
return "do api work"
}
type Proxy struct {
api API
}
func (p Proxy) Do() string {
p.Before()
res := p.api.Do()
p.After()
return res
}
func (p Proxy) Before() {
fmt.Println("before work")
}
func (p Proxy) After() {
fmt.Println("after work")
}
案例中的 API 比較簡單,在真正的 web 服務中會根據路由抉擇請求的方法,案例中省略了這個步驟。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/hUt4-J2vzCR7AK2kgt6ZsA