結(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ù)) |