创建型模式
1.单例模式
单例模式就是定义一个全局的实例。比如,Gin框架中,使用gin.New或者gin.Default创建一个唯一的路由Engine,这就是单例模式。还有数据库示例、全局配置、全局任务池等。(这也是一个懒汉模式,在第一次使用到这个实例的时候,才会初始化)
func main() {
// 1.单例模式
for i := 0; i < 10; i++ {
go func() {
s := GetSingleInstance()
s.Pip()
}()
}
time.Sleep(time.Second * 2)
}
// 1.单例模式
type single struct{}
var (
singleInstance *single
once sync.Once
)
func GetSingleInstance() *single {
once.Do(func() {
fmt.Println("init")
singleInstance = &single{}
})
return singleInstance
}
func (s *single) Pip() {
fmt.Println("pip")
}
2.工厂模式
工厂模式是面向对象编程中常用的模式。在Go语言中,就可以把结构体看作面向对象编程中的类。
工厂模式主要实现复杂对象的创建,将一些具有相同特征的对象,给它设计为一个接口,通过这个子结构体去实现这个接口。实际项目中,主要 就比如去连接MySQL、Redis就通过这个模式就可以设计一个访问数据存储的接口,然后就可以创建2个子结构体来实现这个接口,并且也支持扩展。 除此之外,也比如用微信、支付宝来支付,也可以设计工厂模式。
type Car interface {
Run()
}
// ConcreteProductA
type BMW struct{}
func (b *BMW) Run() {
fmt.Println("BMW 正在行驶...")
}
// ConcreteProductB
type Audi struct{}
func (a *Audi) Run() {
fmt.Println("Audi 正在行驶...")
}
// Factory 工厂函数
func CarFactory(brand string) Car {
switch brand {
case "BMW":
return &BMW{}
case "Audi":
return &Audi{}
default:
return nil
}
}
// main
func main() {
car1 := CarFactory("BMW")
car1.Run()
car2 := CarFactory("Audi")
car2.Run()
}
结构型模式
1.策略模式
策略模式,其实就如同这个名字一样,是有策略的。那就是说,我做的是同一件事,只是方式不同,那么怎么在做这件事的时候,能够平滑的切换不同的策略呢?这就是策略模式要解决的问题。
简单总结就是:
- 同一件事,用不同的方式解决。
- 提前定义好统一的接口。
- 可以通过策略函数动态切换策略。
我的理解就是:策略模式会给一些通用的方式定义一个统一的接口,如果你想换用其它的策略解决问题时,那是不需要切换示例的,而是再新写这一部分策略的逻辑之后,直接设置这个新的策略,就可以平滑切换了。就比如以下代码:
type PaymentStrategy interface {
Pay(amount float64)
}
type AliPay struct{}
func (a *AliPay) Pay(amount float64) {
fmt.Println("支付宝支付")
}
type WechatPay struct{}
func (w *WechatPay) Pay(amount float64) {
fmt.Println("微信支付")
}
// PaymentContext
// @Description: context上下文,持有策略接口
type PaymentContext struct {
strategy PaymentStrategy
}
func (ctx *PaymentContext) SetStrategy(strategy PaymentStrategy) {
ctx.strategy = strategy
}
func (ctx *PaymentContext) Pay(amount float64) {
ctx.strategy.Pay(amount)
}
func main() {
ctx := &PaymentContext{}
ctx.SetStrategy(&AliPay{})
ctx.Pay(100)
ctx.SetStrategy(&WechatPay{})
ctx.Pay(200)
}
2.模板模式
模板模式是一种行为设计模式,最主要的作用就是定义一套功能的模板,然后可以将某些不是公共的步骤,延迟在子类中实现。
就比如以下的代码,模板模式,最主要的就是能够实现代码复用、能够控制具体操作的流程、可以扩展一些子类功能的实现等。
type BeverageTemplate interface {
BoilWater() // 公共步骤1: 烧水
Brew() // 抽象步骤2: 冲泡
PourInCup() // 公共步骤3: 倒入杯子
AddCondiments() // 抽象步骤4: 添加调料
WantCondiments() bool // 钩子方法: 是否加调料
}
// 2. 定义模板基类(实现公共方法)
type BaseBeverage struct{}
func (b *BaseBeverage) BoilWater() {
fmt.Println("将水煮沸")
}
func (b *BaseBeverage) PourInCup() {
fmt.Println("将饮料倒入杯子中")
}
func (b *BaseBeverage) WantCondiments() bool {
return true // 默认需要调料
}
// 3. 实现具体模板 - 咖啡
type Coffee struct {
BaseBeverage
}
func (c *Coffee) Brew() {
fmt.Println("用沸水冲泡咖啡粉")
}
func (c *Coffee) AddCondiments() {
fmt.Println("加入糖和牛奶")
}
// 4. 实现具体模板 - 茶
type Tea struct {
BaseBeverage
}
func (t *Tea) Brew() {
fmt.Println("用沸水浸泡茶叶")
}
func (t *Tea) AddCondiments() {
fmt.Println("加入柠檬")
}
func (t *Tea) WantCondiments() bool {
return false // 茶不需要调料
}
// 5. 模板方法执行器
func MakeBeverage(b BeverageTemplate) {
fmt.Println("====== 开始制作饮料 ======")
b.BoilWater() // 公共步骤
b.Brew() // 具体步骤
b.PourInCup() // 公共步骤
if b.WantCondiments() {
b.AddCondiments() // 可选步骤
}
fmt.Println("====== 饮料制作完成 ======\n")
}
// 6. 客户端代码
func main() {
// 制作咖啡
coffee := &Coffee{}
MakeBeverage(coffee)
// 制作茶
tea := &Tea{}
MakeBeverage(tea)
}
行为型模式
1.代理模式
代理模式需要一个替身或者占位符,来控制对象的访问场景。它为其他对象提供一种代理以控制对这个对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来间接访问原始类,从而可以在访问原始类时添加额外的功能。
type Subject interface {
DoAction()
}
// RealSubject 是实际对象
type RealSubject struct{}
func (r *RealSubject) DoAction() {
fmt.Println("RealSubject: Doing the real action.")
}
type Cookie struct{}
func (c *Cookie) DoAction() {
fmt.Println("do cookie! lets go!")
}
// Proxy 是代理对象
type Proxy struct {
real *RealSubject
coo *Cookie
}
func (p *Proxy) DoAction() {
fmt.Println("Proxy: Before calling real subject")
p.real.DoAction()
fmt.Println("Proxy: After calling real subject")
p.coo.DoAction()
}
func main() {
proxy := &Proxy{real: &RealSubject{}}
proxy.DoAction()
p := &Proxy{coo: &Cookie{}}
p.DoAction()
}
2.选项模式
选项模式是一种构造函数参数的变体设计模式,用于应对 构造函数参数过多 或 参数组合不灵活 的问题。(起初看到选项模式,我以为可以链式调用的,其实,选项模式是为了方便起初在初始化结构体时配置信息的,所以没有必要搞链式调用,并且链式调用也增加的复杂度。)
type Server struct {
IP string
Port int
Protocol string
}
// Option 是配置函数类型
type Option func(*Server)
// NewServer 构造函数,应用所有 Option
func NewServer(opts ...Option) *Server {
s := &Server{
IP: "127.0.0.1",
Port: 80,
Protocol: "http",
}
for _, opt := range opts {
opt(s)
}
return s
}
func WithIP(ip string) Option {
return func(s *Server) {
s.IP = ip
}
}
func WithPort(port int) Option {
return func(s *Server) {
s.Port = port
}
}
func WithProtocol(protocol string) Option {
return func(s *Server) {
s.Protocol = protocol
}
}
func main() {
srv := NewServer(WithPort(9091))
fmt.Println(srv)
}