为了账号安全,请及时绑定邮箱和手机立即绑定

go设计模式之创建型模式

标签:
Go 设计模式

结构型模式解决什么问题

结构模式关注类和对象的组合,解决如何将类和对象组装成较大结构的同时,保持结构的灵活和可复用性。

1.装饰模式(俄罗斯套娃)

装饰模式是对基类进行包装(装饰)从而为对象增加新功能或改变原有功能,操作对象和装饰器对象由于实现了同一接口,
因而操作对象可以用装饰器进行多次(套娃式)封装,对象将获得所有装饰器作用叠加后的功能。

装饰模式图解

下面代码描述了如何通过层层装饰返回一个满足顾客要求的披萨,并计算价格:

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

2.适配器模式

适配器模式可以通过一个中间层使不兼容的两个对象互相合作,适配器接收对象对其调用,并将此调用装换为对另一个对象的调用。适配就好比现实世界中的扩展坞将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接口

3.代理模式

代理模式可以替代原对象,处理对原对象的调用,通常会在对原对象的调用前后做一些同一的处理,例如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!

*/

4.总结

下面是分别是这3种设计模式的常见应用场景:

设计模式 常见应用场景
装饰器模式 不修改原有对象结构,运行时为对象新增额外功能
适配器模式 想使用某个类,但这个类和其他代码不兼容时,创建一个中间层类
代理模式 延迟初始化真实对象,先使用虚拟代理,请求代理(记录日志,请求缓存,请求限流,代理远程服务)
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消