go設(shè)計(jì)模式之結(jié)構(gòu)型模式

結(jié)構(gòu)型模式解決什么問(wèn)題

結(jié)構(gòu)模式關(guān)注類和對(duì)象的組合折柠,解決如何將類和對(duì)象組裝成較大結(jié)構(gòu)的同時(shí)睬辐,保持結(jié)構(gòu)的靈活和可復(fù)用性。

1.裝飾模式(俄羅斯套娃)

裝飾模式是對(duì)基類進(jìn)行包裝(裝飾)從而為對(duì)象增加新功能或改變?cè)泄δ埽僮鲗?duì)象和裝飾器對(duì)象由于實(shí)現(xiàn)了同一接口丸边,
因而操作對(duì)象可以用裝飾器進(jìn)行多次(套娃式)封裝凸舵,對(duì)象將獲得所有裝飾器作用疊加后的功能祖娘。

下面代碼描述了如何通過(guò)層層裝飾返回一個(gè)滿足顧客要求的披薩,并計(jì)算價(jià)格:

package main

import "fmt"

type IPizza interface {
    getPrice() int
}

// 基類: 素食披薩
type Vegetable struct {
}

func (v Vegetable) getPrice() int {
    return 10
}

// 裝飾器1: 奶酪裝飾器
type Cheese struct {
    pizza IPizza
}

func (c Cheese) getPrice() int {
    return c.pizza.getPrice() + 3
}

// 裝飾器2:
type Tomato struct {
    pizza IPizza
}

func (c Tomato) getPrice() int {
    return c.pizza.getPrice() + 4
}

func main() {

    vegetablePizza := Vegetable{}

    cheeseVegePizza := Cheese{vegetablePizza}

    tomatoCheeseVegePizza := Tomato{cheeseVegePizza}

    fmt.Printf("加了番茄和奶酪的披薩最終價(jià)格:%d\n", tomatoCheeseVegePizza.getPrice())

}


// output
// 加了番茄和奶酪的披薩最終價(jià)格:17

2.適配器模式

適配器模式可以通過(guò)一個(gè)中間層使不兼容的兩個(gè)對(duì)象互相合作啊奄,適配器接收對(duì)象對(duì)其調(diào)用渐苏,并將此調(diào)用裝換為對(duì)另一個(gè)對(duì)象的調(diào)用。適配就好比現(xiàn)實(shí)世界中的擴(kuò)展塢將A和B
兩個(gè)接口之間做了一層裝換菇夸。

下面代碼描述了如何通過(guò)適配器讓只支持usb的windows電腦琼富,也能使用雷電接口:

package main

import "fmt"

type Computer interface {
    InsertIntoLightningPort()
}

type Client struct {
}

// 給電腦插入雷電接口
func (t Client) InsertLightIntoComputer(c Computer) {
    c.InsertIntoLightningPort()
}

type Mac struct {
}

// mac電腦使用雷電接口
func (m Mac) InsertIntoLightningPort() {
    fmt.Println("給mac電腦插入雷電接口")
}

type Windows struct {
}

// windows電腦使用usb接口
func (m Windows) InsertIntoUsbPort() {
    fmt.Println("給windows電腦插入usb接口")
}

type WindowsAdapter struct {
    windows Windows
}

// 適配器 將雷電接口轉(zhuǎn)為usb接口
func (w WindowsAdapter) InsertIntoLightningPort() {
    fmt.Println("轉(zhuǎn)換雷電接口為usb接口")
    w.windows.InsertIntoUsbPort()
}

func main() {
    mac := Mac{}
    client := Client{}

    client.InsertLightIntoComputer(mac)

    windows := Windows{}

    adapter := WindowsAdapter{windows: windows}

    client.InsertLightIntoComputer(adapter)
}


// output
// 給mac電腦插入雷電接口
// 轉(zhuǎn)換雷電接口為usb接口
// 給windows電腦插入usb接口

3.代理模式

代理模式可以替代原對(duì)象,處理對(duì)原對(duì)象的調(diào)用庄新,通常會(huì)在對(duì)原對(duì)象的調(diào)用前后做一些同一的處理鞠眉,例如nginx代理web應(yīng)用處理請(qǐng)求,在流量真正到達(dá)
web應(yīng)用程序前做請(qǐng)求的負(fù)載均衡摄咆,之后決定將請(qǐng)求轉(zhuǎn)發(fā)給哪臺(tái)服務(wù)器凡蚜。

下面代碼實(shí)現(xiàn)了nginx代理web應(yīng)用程序做接口限流:

package main

import "fmt"

// web服務(wù)應(yīng)該具有處理請(qǐng)求的能力
type Server interface {
    handleRequest(url, method string) (int, string)
}

// web應(yīng)用程序
type Application struct {
}

func (a Application) handleRequest(url, method string) (int, string) {
    if url == "/app/status" && method == "GET" {
        return 200, "Ok"
    }

    if url == "/create/user" && method == "POST" {
        return 200, "User Created Success!"
    }
    return 404, "404 Not Found"
}

// nginx 代理web應(yīng)用處理請(qǐng)求,做api接口請(qǐng)求限流
type NginxServer struct {
    application  Application
    MaxReqNum    int            // 最大請(qǐng)求數(shù)
    LimitRateMap map[string]int // 緩存每個(gè)接口的請(qǐng)求數(shù)
}

func NewNginxServer(app Application, max int) *NginxServer {
    return &NginxServer{
        application:  app,
        MaxReqNum:    max,
        LimitRateMap: make(map[string]int),
    }
}

// 代理web應(yīng)用請(qǐng)求
func (n NginxServer) handleRequest(url, method string) (int, string) {
    if !n.checkReqRate(url) {
        return 403, "Not Allowed"
    }

    // 接口限流后轉(zhuǎn)發(fā)請(qǐng)求到真實(shí)web應(yīng)用
    return n.application.handleRequest(url, method)
}

// 接口限流和緩存
func (n *NginxServer) checkReqRate(url string) bool {
    reqNum := n.LimitRateMap[url]

    if reqNum >= n.MaxReqNum {
        return false
    }
    n.LimitRateMap[url]++

    return true
}

func main() {

    nginx := NewNginxServer(Application{}, 2)
    respCode, respBody := nginx.handleRequest("/app/status", "GET")
    fmt.Printf("URL:%s \n返回狀態(tài)碼:%d,響應(yīng)內(nèi)容:%s \n\n", "/app/status", respCode, respBody)

    respCode, respBody = nginx.handleRequest("/app/status", "GET")
    fmt.Printf("URL:%s \n返回狀態(tài)碼:%d,響應(yīng)內(nèi)容:%s \n\n", "/app/status", respCode, respBody)

    // 超過(guò)了最大限流數(shù) 返回403
    respCode, respBody = nginx.handleRequest("/app/status", "GET")
    fmt.Printf("URL:%s \n返回狀態(tài)碼:%d,響應(yīng)內(nèi)容:%s \n\n", "/app/status", respCode, respBody)

    respCode, respBody = nginx.handleRequest("/create/user", "POST")
    fmt.Printf("URL:%s \n返回狀態(tài)碼:%d,響應(yīng)內(nèi)容:%s \n\n", "/create/user", respCode, respBody)

}

/* output
URL:/app/status
返回狀態(tài)碼:200,響應(yīng)內(nèi)容:Ok

URL:/app/status
返回狀態(tài)碼:200,響應(yīng)內(nèi)容:Ok

URL:/app/status
返回狀態(tài)碼:403,響應(yīng)內(nèi)容:Not Allowed

URL:/create/user
返回狀態(tài)碼:200,響應(yīng)內(nèi)容:User Created Success!

*/

4.總結(jié)

下面是分別是這3種設(shè)計(jì)模式的常見(jiàn)應(yīng)用場(chǎng)景:

設(shè)計(jì)模式 常見(jiàn)應(yīng)用場(chǎng)景
裝飾器模式 不修改原有對(duì)象結(jié)構(gòu)吭从,運(yùn)行時(shí)為對(duì)象新增額外功能
適配器模式 想使用某個(gè)類朝蜘,但這個(gè)類和其他代碼不兼容時(shí),創(chuàng)建一個(gè)中間層類
代理模式 延遲初始化真實(shí)對(duì)象涩金,先使用虛擬代理谱醇,請(qǐng)求代理(記錄日志,請(qǐng)求緩存步做,請(qǐng)求限流副渴,代理遠(yuǎn)程服務(wù))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市全度,隨后出現(xiàn)的幾起案子煮剧,更是在濱河造成了極大的恐慌,老刑警劉巖将鸵,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勉盅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡顶掉,警方通過(guò)查閱死者的電腦和手機(jī)草娜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痒筒,“玉大人宰闰,你說(shuō)我怎么就攤上這事茬贵。” “怎么了移袍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵解藻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我咐容,道長(zhǎng)舆逃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任戳粒,我火速辦了婚禮路狮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔚约。我一直安慰自己奄妨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布苹祟。 她就那樣靜靜地躺著砸抛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪树枫。 梳的紋絲不亂的頭發(fā)上直焙,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音砂轻,去河邊找鬼奔誓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛搔涝,可吹牛的內(nèi)容都是我干的厨喂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼庄呈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蜕煌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起诬留,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斜纪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后文兑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體傀广,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年彩届,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誓酒。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡樟蠕,死狀恐怖贮聂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寨辩,我是刑警寧澤吓懈,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站靡狞,受9級(jí)特大地震影響耻警,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甸怕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一甘穿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梢杭,春花似錦温兼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至咒唆,卻和暖如春届垫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背全释。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工装处, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恨溜。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓符衔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糟袁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子判族,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容