Go笔记 · · By/蜜汁炒酸奶

Go常用设计模式简记-行为型模式

行为型模式(Behavioral Patterns),它的特点是关注对象之间的通信

代理模式

常用场景:需要一个替身或者占位符,以控制对这个对象的访问的场景。

代理模式 (Proxy Pattern),可以为另一个对象提供一个替身或者占位符,以控制对这个对象的访问。

下面实现中 StationProxy 代理了 Station,代理类中持有被代理类对象,并且和被代理类对象实现了同一接口。

package main

import "fmt"

// 

type Seller interface {
	sell(name string)
}

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

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

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

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

func main()  {
	s :=&Station{
		stock: 10,
	}
	s.sell("张三")
	fmt.Println("Other>>>>>")
	prox := &StationProxy{s}
	prox.sell("李四")
}

选项模式

常用场景:

  • 结构体参数很多,期望创建一个携带默认值的结构体变量,并选择性修改其中一些参数的值。
  • 结构体参数经常变动,变动时又不想修改创建实例的函数

使用选项模式,我们可以创建一个带有默认值的 struct 变量,并选择性地修改其中一些参数的值。

对于实现默认值配置的方案,常见的有三种:开发多个创建实例的函数、通过创建一个带默认值的选项实例作为参数、选项模式,下面我们逐个查看其实现方式:

方式一:开发多个创建实例的函数

为实现默认值配置,分别开发两个用来创建实例的函数,一个可以创建带默认值的实例,一个可以定制化创建实例。

使用这种方式,创建同一个 Connection 实例,却要实现两个不同的函数,实现方式很不优雅。

package main

import "time" 
const (
	defaultTimeout = 10
	defaultCaching = false
)

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

func NewConnect(add string)(*Connection,error) {
	return &Connection{
		addr: add,
		cache: defaultCaching,
		timeout: defaultTimeout,
	},nil
}

func NewConnectWithOpt(add string,cache bool, timeout time.Duration)(*Connection,error) {
	return &Connection{
		addr: add,
		cache: cache,
		timeout: timeout,
	},nil
}

方式二:创建一个带默认值的选项实例

创建一个带默认值的选项,并用该选项创建实例。

使用这种方式,虽然只需要实现一个函数来创建实例, 但是也有缺点:为了创建 Connection 实例,每次我们都要创建 ConnectionOptions,操作起来比较麻烦。

package main

import "time"


const (
	defaultTimeoutb = 10
	defaultCachingb = false
)

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


type ConnectionOptions struct {
	Caching bool
	Timeout time.Duration
}

func NewDefaultOptions() *ConnectionOptions  {
	return &ConnectionOptions{
		Caching: defaultCachingb,
		Timeout: defaultTimeoutb,
	}
}

func NewConnectWithOps(addr string,opts *ConnectionOptions) (*Connectionb,error)  {
	return &Connectionb{
		addr: addr,
		cache: opts.Caching,
		timeout: opts.Timeout,
	},nil
}

方式三:选项模式

之前通过Java看设计模式时,没看到关于选项模式,感觉在Go中的实现也比较有特点。在解决目的上感觉和建造者模式差不太多,都是在关注通过可变参数创建对象的问题。

选项模式有很多优点,例如:支持传递多个参数,并且在参数发生变化时保持兼容性;支持任意顺序传递参数;支持默认值;方便扩展;通过 WithXXX 的函数命名,可以使参数意义更加明确,等等.

为了实现选项模式,我们增加了很多代码,如果结构体参数比较少,需慎重考虑要不要采用选项模式。

package main

import (
	"fmt"
	"time"
)
//  默认值
const (
	defaultTimeoutc = 10
	defaultCachingc = false
)
// Connection 结构体,实际需要创建的对象
type Connectionc struct {
	addr string
	cache bool
	timeout time.Duration
}

// options 选项结构体,用于保存变量值
type options struct {
	caching bool
	timeout time.Duration
}

type Option interface {
	apply(*options)
}
// 定义配置选项函数(关键)
type optionFunc func(*options)
// Option 类型的选项参数需要实现apply(*options)函数
func (f optionFunc) apply(o *options)  {
	f(o)
}
// 通过 WithXXX 方法来创建 Option 类型的选项参数
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 对象
func NewConnectc(addr string,opts ...Option)(*Connectionc,error)  {
	// 默认值的options
	options := options{
		timeout: defaultTimeoutc,
		caching: defaultCachingc,
	}
	// 修改属性值
	for _,o := range opts{
		// o.apply(&options)其实就是通过闭包把 WithTimeout、WithCaching 传入的参数赋值给 options 结构体变量,以此动态地设置 options 结构体变量的属性
		o.apply(&options)
	}
	return &Connectionc{
		addr: addr,
		cache: options.caching,
		timeout: options.timeout,
	},nil
}

func main()  {
	connection,_ :=NewConnectc(
		"https://twst",
		WithCaching(true),
	)
	fmt.Println("Connection fild: ", connection)
}

参考资料

Go 语言项目开发实战

评论已关闭

example
C
蜜汁炒酸奶

当前处于试运行期间,可能存在不稳定情况,敬请见谅。

欢迎点击此处反馈访问过程中出现的问题