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