Go-常见设计模式

Go项目开发中比较常用的设计模式介绍

  • 创建型模式
    • 单例模式
    • 工厂模式
  • 结构型模式
    • 策略模式
    • 模板模式
  • 行为型模式
    • 代理模式
    • 选项模式

创建型模式

单例模式

package singleton

import "sync"

type singleton struct {}

var ins *singleton
var once sync.Once

func GetInsOr() *singleton{
    once.Do(func(){
        ins = &singleton{}
    })
    return nil
}

使用once.Do可以确保ins实例全局被创建一次

单例模式实际上有饿汉方式和懒汉方式,这里只介绍在Go项目中单例模式最优雅的实现方式

工厂模式

工厂模式是面向对象编程中的常用模式。可以通过不同的工厂模式来带得Go项目变得简洁

  • 简单工厂模式
package factory

type Person struct{
    Name string
    Age  string
}

func (p Person) Greet() {
    fmt.Printf("name := %s, age := %s", p.Name, p.Age)
}

func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age: age,
    }
}

通过NewPerson创建实例,可以确保实例的Name和Age属性被设置

  • 抽象工厂模式

抽象工厂模式和简单工厂模式的唯一区别,返回的是接口而不是结构体

通过返回接口,可以在不公开内部实现的情况下,让调用者使用提供的各种功能

package factory

type PersonIntf interface {
  Greet()
}

type person struct {
  name string
  age int
}

func (p person) Greet() {
  fmt.Printf("Hi! My name is %s", p.name)
}

// Here, NewPerson returns an interface, and not the person struct itself
func NewPerson(name string, age int) PersonIntf {
  return person{
    name: name,
    age: age,
  }
}
  • 工厂方式模式

在工厂方法模式,依赖工厂函数,通过实现工厂函数来创建多种工厂,将对象创建从由一个对象辅助所有具体类的实例化,变成由一群子类来负责具体类的实例化

package factory

type Person struct {
 name string
 age int
}

func NewPersonFactory(age int) func(name string) Person {
 return func(name string) Person {
  return Person{
   name: name,
   age: age,
  }
 }
}

结构型模式

结构型模式特点:关注类和对象的组合

策略模式

策略模式定义一个组算法,将每个算法都封装起来,并且使它们之间可以切换,根据不同的场景,采用不同的措施,即是不同的策略。

通过策略模式,定义一些独立的类来封装不同的算法,每个类封装一个具体的算法(策略)

package strategy

type IStrategy interface {
    do(int, int) int
}

// 策略实现:加
type add struct{}

func (*add) do(a, b int) int {
 return a + b
}

// 策略实现:减
type reduce struct{}

func (*reduce) do(a, b int) int {
 return a - b
}

// 具体策略的执行者
type Operator struct {
 strategy IStrategy
}

// 设置策略
func (operator *Operator) setStrategy(strategy IStrategy) {
 operator.strategy = strategy
}

// 调用策略中的方法
func (operator *Operator) calculate(a, b int) int {
 return operator.strategy.do(a, b)
}

模板模式

模板模式定义一个操作中算法的骨架,而将一些步骤延迟到子类中

package template

import "fmt"

type Cooker interface {
 fire()
 cooke()
 outfire()
}

// 类似于一个抽象类
type CookMenu struct {
}

func (CookMenu) fire() {
 fmt.Println("开火")
}

// 做菜,交给具体的子类实现
func (CookMenu) cooke() {
}

func (CookMenu) outfire() {
 fmt.Println("关火")
}

// 封装具体步骤
func doCook(cook Cooker) {
 cook.fire()
 cook.cooke()
 cook.outfire()
}

type XiHongShi struct {
 CookMenu
}

func (*XiHongShi) cooke() {
 fmt.Println("做西红柿")
}

type ChaoJiDan struct {
 CookMenu
}

func (ChaoJiDan) cooke() {
 fmt.Println("做炒鸡蛋")
}

行为型模式

行为型模式,特点是关注对象之间的通信

代理模式

代理模式为另一个对象提供一个替身或者占位符号,以控制对像的访问

package proxy

import "fmt"

type Seller interface {
 sell(name string)
}

// 火车站
type Station struct {
 stock int //库存
}

func (station *Station) sell(name string) {
 if station.stock > 0 {
  station.stock--
  fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, station.stock)
 } else {
  fmt.Println("票已售空")
 }

}

// 火车代理点
type StationProxy struct {
 station *Station // 持有一个火车站对象
}

func (proxy *StationProxy) sell(name string) {
 if proxy.station.stock > 0 {
  proxy.station.stock--
  fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, proxy.station.stock)
 } else {
  fmt.Println("票已售空")
 }
}

StationProxy代理了Station

选项模式

选择模式是Go项目开发中经常使用到的模式。使用选项模式,可以创建一个带有默认值的struct变量,并选择地修改其中某一些参数的值

package options

import (
 "time"
)

type Connection struct {
 addr    string
 cache   bool
 timeout time.Duration
}

const (
 defaultTimeout = 10
 defaultCaching = false
)

type options struct {
 timeout time.Duration
 caching bool
}

// Option overrides behavior of Connect.
type Option interface {
 apply(*options)
}

type optionFunc func(*options)

func (f optionFunc) apply(o *options) {
 f(o)
}

func WithTimeout(t time.Duration) Option {
 return optionFunc(func(o *options) {
  o.timeout = t
 })
}

func WithCaching(cache bool) Option {
 return optionFunc(func(o *options) {
  o.caching = cache
 })
}

// Connect creates a connection.
func NewConnect(addr string, opts ...Option) (*Connection, error) {
 options := options{
  timeout: defaultTimeout,
  caching: defaultCaching,
 }

 for _, o := range opts {
  o.apply(&options)
 }

 return &Connection{
  addr:    addr,
  cache:   options.caching,
  timeout: options.timeout,
 }, nil
}

选项模式通常适用于以下场景:

  • 结构体参数很多,创建结构体时,我们期望创建一个携带默认值的结构体变量,并选择性修改其中一些参数的值。
  • 结构体参数经常变动,变动时我们又不想修改创建实例的函数。例如:结构体新增一个retry参数,但是又不想在NewConnect入参列表中添加retry int这样的参数声明。