行为型模式(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)}
评论