结构模式关注类和对象的组合,解决如何将类和对象组装成较大结构的同时,保持结构的灵活和可复用性。
装饰模式是对基类进行包装(装饰)从而为对象增加新功能或改变原有功能,操作对象和装饰器对象由于实现了同一接口,
因而操作对象可以用装饰器进行多次(套娃式)封装,对象将获得所有装饰器作用叠加后的功能。
下面代码描述了如何通过层层装饰返回一个满足顾客要求的披萨,并计算价格:
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("加了番茄和奶酪的披萨最终价格:%d\n", tomatoCheeseVegePizza.getPrice()) } // output // 加了番茄和奶酪的披萨最终价格:17
适配器模式可以通过一个中间层使不兼容的两个对象互相合作,适配器接收对象对其调用,并将此调用装换为对另一个对象的调用。适配就好比现实世界中的扩展坞将A和B
两个接口之间做了一层装换。
下面代码描述了如何通过适配器让只支持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 } // 适配器 将雷电接口转为usb接口 func (w WindowsAdapter) InsertIntoLightningPort() { fmt.Println("转换雷电接口为usb接口") w.windows.InsertIntoUsbPort() } func main() { mac := Mac{} client := Client{} client.InsertLightIntoComputer(mac) windows := Windows{} adapter := WindowsAdapter{windows: windows} client.InsertLightIntoComputer(adapter) } // output // 给mac电脑插入雷电接口 // 转换雷电接口为usb接口 // 给windows电脑插入usb接口
代理模式可以替代原对象,处理对原对象的调用,通常会在对原对象的调用前后
做一些同一的处理,例如nginx代理web应用处理请求,在流量真正到达
web应用程序前做请求的负载均衡,之后决定将请求转发给哪台服务器。
下面代码实现了nginx代理web应用程序做接口限流:
package main import "fmt" // web服务应该具有处理请求的能力 type Server interface { handleRequest(url, method string) (int, string) } // web应用程序 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应用处理请求,做api接口请求限流 type NginxServer struct { application Application MaxReqNum int // 最大请求数 LimitRateMap map[string]int // 缓存每个接口的请求数 } func NewNginxServer(app Application, max int) *NginxServer { return &NginxServer{ application: app, MaxReqNum: max, LimitRateMap: make(map[string]int), } } // 代理web应用请求 func (n NginxServer) handleRequest(url, method string) (int, string) { if !n.checkReqRate(url) { return 403, "Not Allowed" } // 接口限流后转发请求到真实web应用 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返回状态码:%d,响应内容:%s \n\n", "/app/status", respCode, respBody) respCode, respBody = nginx.handleRequest("/app/status", "GET") fmt.Printf("URL:%s \n返回状态码:%d,响应内容:%s \n\n", "/app/status", respCode, respBody) // 超过了最大限流数 返回403 respCode, respBody = nginx.handleRequest("/app/status", "GET") fmt.Printf("URL:%s \n返回状态码:%d,响应内容:%s \n\n", "/app/status", respCode, respBody) respCode, respBody = nginx.handleRequest("/create/user", "POST") fmt.Printf("URL:%s \n返回状态码:%d,响应内容:%s \n\n", "/create/user", respCode, respBody) } /* output URL:/app/status 返回状态码:200,响应内容:Ok URL:/app/status 返回状态码:200,响应内容:Ok URL:/app/status 返回状态码:403,响应内容:Not Allowed URL:/create/user 返回状态码:200,响应内容:User Created Success! */
下面是分别是这3种设计模式的常见应用场景:
设计模式 | 常见应用场景 |
---|---|
装饰器模式 | 不修改原有对象结构,运行时为对象新增额外功能 |
适配器模式 | 想使用某个类,但这个类和其他代码不兼容时,创建一个中间层类 |
代理模式 | 延迟初始化真实对象,先使用虚拟代理,请求代理(记录日志,请求缓存,请求限流,代理远程服务) |