创建型模式(Creational Pattern)

单例模式(Singleton Pattern)

主要用于保证一个类仅有一个实例,并提供一个访问它的全局访问点

(1) 限制调用者直接实例化该对象

(2) 为该对象的单例提供一个全局唯一的访问方法

package msgpool
...
// 消息池
type messagePool struct {
	pool *sync.Pool
}
// 消息池单例
var msgPool = &messagePool {
	// 如果消息池里没有消息,则新建一个Count值为0的Message实例
	pool: &sync.Pool{New: func() interface{} { return &Message{Count: 0}},
}
// 访问消息池单例的唯一方法
func Instence() *messagePool {
	return msgPool
}
// 向消息池里添加消息
func (m *messagePool) AddMsg(msg *Message) {
	m.pool.Put(msg)
}
// 从消息池里获取消息
func (m *messagePool) GetMsg() *Message {
	return m.pool.Get().(*Message)
}
...
package test
...
func TestMessagePool(t *testing.T) {
	msg0 := msgpool.Instance().GetMsg()
	if msg0.Count != 0 {
		t.Errorf("expect msg count %d, but actual %d.", 0, msg0.Count)
	}
	msg0.Count = 1
	msgpool.Instance().AddMsg(msg0)
	msg1 := msgpool.Instance().GetMsg()
	if msg1.Count != 1 {
		t.Errorf("expect msg count %d, but actual %d.", 1, msg1.Count)
	}
}

饿汉模式与懒汉模式

懒汉模式会带来线程安全问题,可以通过普通加锁,或者双重检验锁来优化

// 单例模式的“懒汉模式”实现
package msgpool
...
var once = &sync.Once{}
var msgPool *messagePool
func Instance() *messagePool {
	// 在匿名函数中实现初始化逻辑,Go语言保证只会调用一次
	once.Do(func() {
		msgPool = &messagePool{
			// 如果消息池里没有消息, 则新建一个Count值为0的Message实例
			pool:&sync.Poll{New: func() interface{} {return &Message{Count:0} }},
		}
	})
	return msgPool
}
...

建造者模式(Builder Pattern)

(1) 封装复杂对象的创建过程,使对象使用者不感知复杂的创建逻辑

(2) 可以一步步按照顺序对成员进行赋值,或者创建嵌套对象,并最终完成目标对象的创建

(3) 对多个对象复用同样的对象创建逻辑

package msg
...
type Message struct {
	Header *Header
	Body *Body
}

type Header struct {
	SrcAddr string
	SrcPort uint64
	DestAddr string
	DestPort uint64
	Items map[string] string
}
type Body struct {
	Item []string
}
...
package msg
...
// Message对象的Builder对象
type builder struct {
	once *sync.Once
	msg *Message
}
// 返回Builder对象
func Builder() *builder {
	return &builder{
		once: &sync.Once{},
		msg: &Message{Header: &Header{}, Body: &Body{}},
	}
}

// 以下是对Message成员的构建方法
func (b *builder) WithSrcAddr(srcAddr string) *builder {
	b.msg.Header.SrcAddr = srcAddr
	return b
}
func (b *builder) WithSrcPort(srcPort uint64) *builder {
	b.msg.Header.SrcPort = srcPort
	return b
}
func (b *builder) WithDestAddr(destAddr string) *builder {
	b.msg.Header.DestAddr = destAddr
	return b
}
func (b *builder) WithHeaderItem(key, value, string) *builder {
	// 保证map只初始化一次
	b.once.Do(func() {
		b.msg.Header.Items = make(map[string]string)
	})
	b.msg.Header.Items[key] = value
	return b
}
func (b *builder) WithBodyItem(record string) *builder {
	b.msg.Body.Items = append(b.msg.Body.Items, record)
	return b
}
func (b *builder) Build() *Message {
	return b.msg
}
package test
...
func TestMessageBuilder(t *testing.T) {
	// 使用消息建造者进行对象创建
	message := msg.Builder().
		WithSrcAddr("192.168.0.1").
		WithSrcPost(1234).
		...
		Build()
	if message.Header.SrcAddr != "192.168.0.1" {
		t.Errorf("expect src address 192.168.0.1, but actual %d.", message.Header.SrcAddr)
	if message.Body.Items[0] != "record1" {
		t.Errorf("expect body item0 record1, but actual %s.",message.Body.Items[0])
	}
}

工厂方法模式(Factory Method Pattern)

package event
...
type Type uint8
// 事件类型定义
const (
	Start Type = iota
	End
)
// 事件抽象接口
type Event interface {
	EventType() Type
	Content() string
}
// 开始事件,实现了Event接口
type StartEvent struct{
	content string
}
...
// 结束事件,实现了Event接口
type EndEvent struct{
	content string
}
...
package event
...
// 事件工厂对象
type Factory struct{}
// 根据事件类型创建具体事件
func (e *Factory) Create(etype Type) Event {
	switch etype {
	case Start:
		return &StartEvent{
			content: "this is start event",
		}
	case End:
		return &EndEvent{
			content: "this is end event",
		}
	default:
		return nil
	}
}
package event 
...
func TestEvent(t *testing.T) {
	e := event.OfStart()
	if e.EventType() != event.Start {
		t.Errorf("expect event.Start, but actual %v.", e.EventType())
	}
	e = factory.Create(event.End)
	if e.EventType() != event.End {
		t.Errorf("expect event.End, but actual %v.", e.EventType())
	}
}

抽象工厂模式(Abstract Factory Pattern)

package plugin
...
// 插件抽象接口定义
type Plugin interface{}
// 输入插件,用于接收消息
type Input interface {
	Plugin
	Receive() string
}
// 过滤插件,用于处理消息
type Fliter interface {
	Plugin
	Process(msg string) string
}
// 输出插件,用于发送消息
type Output interface {
	Plugin
	Send(msg string)
}

package pipeline
...
// 消息管道的定义
type Pipeline struct {
	input plugin.Input
	filter plugin.Filter
	output plugin.Output
}
// 一个消息的处理流程为 input -> filter -> output
func (p *Pipeline) Exec() {
	msg := p.intput.Receive()
	msg = p.filter.Process(msg)
	p.output.Send(msg)
}
package plugin
...
// input插件名称与类型的映射关系,主要用于通过反射创建input对象
var inputNames = make(map[string]reflect.Type)
// Hello input插件,接收”Hello World“消息
type HelloInput struct {}

 func (h *HelloInput) Receive() string {
	return "Hello World"
}
// 初始化input插件映射关系表
func init() {
	inputNames["hello"] = reflect.TypeOf(HelloInput{})
}

package plugin
...
// filter插件名称与类型的映射关系.主要用于通过反射创建filter对象
var filterNames = make(map[string]reflect.Type)
// Upper filter插件,将消息全部字母转成大写
type UpperFilter struct {}

func (u *UpperFilter) Process(msg string) string {
	return strings.ToUpper(msg)
}
// 初始化filter插件映射关系表
func init() {
	filterNames["upper"] = reflect.TypeOf(UpperFilter{})
}

package plugin
...
// output插件名称与类型的映射关系,主要用于通过反射创建output对象
var outputNames = make(map[string]reflect.Type)
// Console output插件,将消息输出到控制台上
type ConsoleOutput struct {}

func (c *ConsoleOutput) Send(msg string) {
	fmt.Println(msg)
}
// 初始化output插件映射关系表
func init() {
	outputNames["console"] = reflect.TypeOf(ConsoleOutput{})
}

package plugin
...
// 插件工厂接口
type Factory interface {
	Create(conf Config) Plugin
}
// input插件工厂对象,实现Factory接口
type InputFactory struct{}
// 读取配置,通过反射机制进行对象实例化
func (i *InputFactory) Create(conf Config) Plugin {
	t, _ := inputNames[conf.Name]
	return reflect.New(t).Interface().(Plugin)
}
// filter和output插件工厂实现类似
type FilterFactory struct{}
func (f *FilterFactory) Create(conf Config) Plugin {
	t, _ := filterNames[conf.Name]
	return reflect.New(t).Interface().(Plugin)
}
type OutputFactory struct{}
func (o *OutputFactory) Create(conf Config) Plugin {
	r, _ := outputNames[conf.Name]
	return reflect.New(t).Interface().(Plugin)
}

package pipeline
...
// 保存用于创建Plugin的工厂实例,其中map的key为插件类型,value为抽象工厂接口
var pluginFactories = make(map[plugin.Type]plugin.Factory)
// 根据plugin.Type返回对应Plugin类型的工厂实例
func factoryOf(t plugin.Type) plugin.Factory {
	factory, _ := pluginFactories[t]
	return factory
}
// pipeline工厂方法,根据配置创建一个Pipeline实例
func Of(conf Config) *Pipeline {
	p := &Pipeline{}
	p.intput = factoryOf(plugin.InputType).Create(conf.Input).(plugin.Input)
	p.filter = factoryOf(plugin.FilterType).Create(conf.Filter).(pulgin.Filter)
	p.output = factoryOf(plugin.OutputType).Create(conf.Output).(plugin.Output)
	return p	
)
// 初始化插件工厂对象
func init() {
	pluginFactories[plugin.InputType] = &plugin.InputFactory{}
	pluginFactories[plugin.FilterType] = &plugin.FilterFactory{}
	pluginFactories[plugin.OutputType] = &plugin.OutputFactory{}
}

package test
...
func TestPipeline(t *testing.T) {
	// 其中pipeline.DefaultConfig()的配置内容见[抽象工厂模式示例图]
	// 消息处理流程为 HelloInput -> UpperFilter -> ConsoleOutput
	p := pipeline.Of(pipeline.Deafult.Config())
	p.Exec()
}

原型模式(Prototype Pattern)

package prototype
...
// 原型复制抽象接口
type Prototype interface {
 clone() Prototype
}

type Message struct {
	Header *Header
	Body *Body
}

func (m *Message) clone() Prototype {
	msg := *m
	return &msg
}
package test
...
func TestPrototype(t *testing.T) {
	message := msg.Builder().
		WithSrcAddr("192.168.0.1").
		WithSrcPort(1234).
		WithDestAddr("192.168.0.2").
		WithDestPort(8080).
		WithHeaderItem("contents","application/json").
		WithBodyItem("record1").
		WithBodyItem("record2").
		Build()
	// 复制一份消息
	newMessage := message.Clone().(*msg.Message)
	if newMessage.Header.SrcAddr != message.Header.SrcAddr {
		t.Errorf("Clone Message failed.")
	}
	if newMessage.Body.Items[0] != message.Body.Items[0] {
		t.Errorf("Clone Message failed.")
	}

结构型模式(Structural Pattern)

组合模式(Composite Pattern)

Go实现组合模式的方式有两种

直接组合(Direct Composition)

嵌入组合(Embedding Composition)

type Message struct {
	Header *Header
	Body *Body
}
package plugin
...
// 插件运行状态
type Status uint8

const (
	Stopped Status = iota
	Started
)

type Plugin interface {
	// 启动插件
	Start()
	// 停止插件
	Stop()
	// 返回插件当前的运行状态
	Status() Status
}
// 这里使用Message结构体替代了原来的string,使语义更清晰
type Input interface {
	Plugin
	Receive() *msg.Message
}

type Filter interface {
	Plugin
	Process(msg *msg.Message) *msg.Message
}

type Output interface {
	Plugin
	Send(msg *msg.Message)
}
package pipeline
...
// 一个Pipeline由input filter output三个Plugin组成
type Pipeline struct {
	status plugin.Status
	input plugin.Input
	filter plugin.Filter
	output plugin.Output
}

func (p *Pipeline) Exec() {
	msg := p.input.Receive()
	msg = p.filter.Process(msg)
	p.output.Send(msg)
}
// 启动的顺序 output -> filter -> input
func (p *Pipeline) Start() {
	p.output.Start()
	p.filter.Start()
	p.input.Start()
	p.status = plugin.Started
	fmt.Println("Hello input plugin started.")
}
// 停止的顺序 input -> filter -> output
func (p *Pipeline) Stop() {
	p.input.Stop()
	p.filter.Stop()
	p.output.Stop()
	p.status = plugin.Stopped
	fmt.Println("Hello input plugin stopped.")
}

func (p *Pipeline) Status() plugin.Status {
	return p.status
}

一个PipelineInputFilterOutput三类插件组成,形成了“部分-整体”的关系,而且它们都实现了Plugin接口,这就是一个典型的组合模式的实现。Client无需显式地启动和停止InputFilterOutput插件,在调用Pipeline对象的StartStop方法时,Pipeline就已经帮你按顺序完成对应插件的启动和停止。

package plugin
...
type HelloInput struct {
	status Status
}

func (h *HelloInput) Receive() *msg.Message {
	// 如果插件未启动,则返回nil
	if h.status != Started {
		fmt.Println("Hello input plugin is not running, input nothing.")
		return nil
	}
	return msg.Builder().
		WithHeaderItem("content", "text").
		WithBodyItem("Hello World").
		Build()
}

func (h *HelloInput) start()  {
	h.status = Started
	fmt.Println("Hello input plugin started.")
}

func (h *HelloInput) Stop() {
	h.status = Stopped
	fmt.Println("Hello input plugin stopped.")
}

func (h *HelloInput) Status {
	return h.status
}

func (u *UpperFilter) Process(msg *msg.Message) *msg.Message {
	if u.status != Started {
	fmt.Println("Upper filter plugin is not running, filter nothing.")
	return msg
	}
	for i, val := range msg.Body.Items {
		msg.Body.Items[i] = strings.ToUpper(val)
	}
	return msg
}

func (u *UpperFilter) Start() {
	u.status = Started
	fmt.Println("Upper filter plugin started.")
}

func (u *UpperFilter) Stop() {
	u.status = Stopped
	fmt.Println("Upper filter plugin stopped.")
}

func (u *UpperFilter) Status() Status {
	return u.status
}

package plugin
...
type ConsoleOutput struct {
	status Status
}

func (c *ConsoleOutput) Send(msg *msg.Message) {
	if c.status != Started {
		fmt.Println("Console output is not running, output nothing.")
		return
	}
	fmt.Printf("Output:\n\tHeader:%+v, Body:%+v\n", msg.Header.Items, msg.Body.Items)
}

func (c *ConsoleOutput) Start() {
	c.status = Started
	fmt.Println("Console output plugin started.")
}

func (c *ConsoleOutput) Stop() {
	c.status = Stopped
	fmt.Println("Console output plugin stopped.")
}

func (c *ConsoleOutput) Status() Status {
	return c.status
}

package test
...
func TestPipeline(t *testing.T) {
	p := pipeline.Of(pipeline.DefaultConfig())
	p.Start()
	p.Exec()
	p.Stop()
}

组合模式的另一种实现,嵌入组合(Embedding Composition),是利用了Go的匿名成员特性,本质上跟直接组合是一致的

type Message struct {
	Header
	Body
}
// 使用时,Message可以引用Header和Body的成员属性
msg := &Message{}
msg.SrcAddr = "192.168.0.1"

适配器模式(Adapter Pattern)

适配器模式是最常用的结构型模式之一,它让原本因为接口不匹配而无法一起工作的两个对象能够一起工作.适配器模式所做的就是将一个接口Adaptee,通过适配器Adapter转换成Client所期望的另一个接口Target来使用,实现原理也很简单,就是Adapter通过实现Target接口,并在对应的方法中调用Adaptee的接口实现

一个典型的应用场景是,系统中一个老的接口已经过时即将废弃,但因为历史包袱没法立即将老接口全部替换为新接口.可以新增一个适配器,将老的接口适配成新的接口来使用.适配器模式很好的践行了面向对象设计里的开闭原则(open/closed principle),新增一个接口时也无需修改老接口,只需多加一个适配器即可.

假设需要给系统新增从kafka 消息队列中接收数据的功能,其中Kafka消费者的接口如下

package kafka
...
type Records struct {
	Items []string
}

type Consumer interface {
	Poll() Records
}

由于当前Pipeline设计时通过plugin.Input接口来进行数据接收,因此kafka.Consumer并不能直接集成到系统中.

所以需要使用适配器模式

为了能让Pipeline能够使用kafka.Consumer接口,我们需要定义一个适配器:

package plugin
...
type KafkaInput struct {
	status Status
	consumer kafka.Consumer
}

func (k *KafkaInput) Receive() *msg.Message {
	records := k.consumer.Poll()
	if k.status != Started {
		fmt.Println("Kafka input plugin is not running, input nothing.")
		return nil
	}
	return msg.Builder().
		WithHeaderItem("content", "kafka").
		WithBodyItem(records.Item).
		Build()
}

// 在输入插件映射关系中加入kafka,用于通过反射创建input对象
func init() {
	inputNames["hello"] = reflect.TypeOf(HelloInput{})
	inputNames["kafka"] = reflect.TypeOf(KafkaInput{})
}

因为Go语言没有构造函数,如果按照抽象工厂模式来创建KafkaInput,那么得到的实例中的consumer成员因为没有被初始化而会是nil.因此,需要给Plugin接口新增一个Init方法,用户定义插件的一些初始化操作,并在工厂返回实例前调用.

package plugin
...
type Plugin interface {
	Start()
	Stop()
	Status() Status
	// 新增初始化方法,在插件Init函数,完成相关初始化方法
	Init()
}

// 修改后的插件工厂实现如下
func (i *InputFactory) Create(conf Config) Plugin {
	t, _ := inputNames[conf.Name]
	p := reflect.New(t).Interface().(Plugin)
	p.Init()
	return p
}

// KafkaInput的Init函数实现
func (k *KafkaInput) Init() {
	k.consumer = &kafka.MockConsumer{}
}

上述代码中的kafka.MockConsumer为我们模拟Kafka消费者的一个实现,

package kafka
...
type MockConsumer struct {}

func (m *MockConsumer) Poll() *Records {
	records := &Records{}
	records.Items = append(records.Items, "i am mock consumer.")
	return records
}

package test
...
func TestKafkaInputPipeline(t *testing.T) {
	config := pipeline.Config{
		Name: "pipeline2",
		Input: plugin.Config{
			PluginType: plugin.InputType,
			Name: "kafka",
		},
		Filter: plugin.Config{
			PluginType: plugiin.FilterType,
			Name: "upper",
		},
		Output: plugin.Config{
			PluginType: plugin.OutputType,
			Name: "console",
		},
	}
	p := pipeline.Of(config)
	p.Start()
	p.Exec()
	p.Stop()
}

桥接模式(Bridge Pattern)

桥接模式主要用于将抽象部分和实现部分进行解耦,使得它们能够各自往独立的方向变化.

这个例子中,我们通过将形状和颜色抽象为一个接口,使产品不再依赖于具体的形状和颜色细节,从而达到了解耦的目的.桥接模式本质上就是面向接口编程,可以给系统带来很好的灵活性和可扩展性.如果一个对象存在多个变化的方向,而且每个变化方向都需要扩展,那么使用桥街模式进行设计比较合适.

回到消息处理系统的例子,一个Pipeline对象主要由Input、Filter、Output三类插件组成(3个特征),因为是插件化的系统,不可避免的就要求支持多种Input、Filter、Output的实现,并能够灵活组合(有多个变化的方向).显然,Pipeline就非常适合使用桥街模式进行设计,实际上我们也这么做了.我们将Input、Filter、Output分别设计成一个抽象的接口,它们按照各自的方向去扩展.Pipeline只依赖的这3个抽象接口,并不感知具体实现的细节.

package plugin
...
type Input interface {
	Plugin
	Receive() *msg.Message
}
type Filter interface {
	Plugin
	Process(msg *msg.Message) *msg.Message
}

type Output interface {
	Plugin
	Send(msg *msg.Message)
}

package pipeline
...
// 一个Pipeline由input、filter、output三个Plugin组成
type Pipeline struct {
	status plugin.Status
	input plugin.Input
	filter plugin.Filter
	output plugin.Output
}
// 通过抽象接口来使用,看不到底层的实现细节
func (p *Pipeline) Exec() {
	msg := p.input.Receive()
	msg = p.filter.Process(msg)
	p.output.Send(msg)
}
package test
...
func TestPipeline(t *testing.T) {
	p := pipeline.Of(pipeline.DefaultConfig())
	p.Start()
	p.Exec()
	p.Stop()
}

本文主要介绍了结构型模式中的组合模式、适配器模式和桥接模式。组合模式主要解决代码复用的问题,相比于继承关系,组合模式可以避免继承层次过深导致的代码复杂问题,因此面向对象设计领域流传着组合优于继承的原则,而Go语言的设计也很好实践了该原则;适配器模式可以看作是两个不兼容接口之间的桥梁,可以将一个接口转换成client所希望的另外一个接口,解决了模块之间因为接口不兼容而无法一起工作的问题;桥接模式将模块的抽象部分和实现部分进行分离,让它们能够往各自的方向扩展,从而达到解耦的目的。

代理模式(Proxy Pattern)

代理模式为一个对象提供一种代理以控制对该对象的访问

// 当Client不方便直接访问一个对象时,提供一个代理对象控制该对象的访问

代理模式分以下几种:

远程代理(remote proxy): 远程代理适用于提供服务的对象处在远程的机器上,通过普通的函数调用无法使用服务,需要经过远程代理来完成.因此并不能直接访问本体对象,所有远程代理对象通常不会直接持有本体对象的引用,而是持有远端机器的地址,通过网络协议去访问本体对象

虚拟代理(virtual proxy):对一些重量级的服务对象,如果一直持有该对象实例回非常消耗系统资源,这时可以通过虚拟代理来对该对象进行延迟初始化.

保护代理(protection proxy):保护代理用于控制对本体对象的访问,常用于需要给Client的访问加上权限验证的场景.

缓存代理(cache proxy):缓存代理主要在Client与本体对象之间加上一层缓存,用于加速本体对象的访问,常见于连接数据库的场景.

智能引用(smart reference):智能引用为本体对象的访问提供了额外的动作,常见的实现为C++中智能指针,为对象的访问提供了计数功能,当访问对象的计数为0时销毁该对象.

package db
...
// Key-Value数据库接口
type KvDb interface {
	// 存储数据
	// 其中reply为操作结果,存储成功为true, 否则为false
	// 当连接数据库失败时返回error,成功则返回nil
	Save(record Record, reply *bool) error
	// 根据key获取value,其中value通过函数参数中指针类型返回
	// 当连接数据库失败时返回error,成功则返回nil
	Get(key string, value *string) error
}

type Record struct {
	Key string
	Value string
}

数据库是一个Key-Value数据库,使用map存储数据,下面为数据库的服务端实现,db.Server实现了db.KvDb接口:

package db
...
// 数据库服务端实现
type Server struct {
	// 采用map存储key-value数据
	data map[string]string
}

func (s *Server) Save(record Record, reply *bool) error {
	if s.data == nil {
		s.data = make(map[string]string)
	}
	s.data[record.Key] = record.Value
	*reply = true
	return nil
}

func (s *Server) Get(key string, reply *string) error {
	val, ok := s.data[key]
	if !ok {
		*reply = ""
		return errors.New("Db has no key " + key)
	}
	*reply = val
	return nil
}

消息处理系统和数据库并不在同一台机器上,因此消息处理系统不能直接调用db.Server的方法进行数据存储,需要使用远程代理的方式

在远程代理中,最常见的一种实现是远程过程调用(Remote Procedure Call)

package db
...
// 启动数据库,对外提供RPC接口进行数据库的访问
func Start() {
	rpcServer := rpc.NewServer()
	server := &Server{data: make[string]string}
	// 将数据库接口注册到RPC服务器上
	if err := rpc.Server.Register(server); err != nil {
		fmt.Printf("Register Server to rpc failed, error: %v", err)
		return
	}
	l, err := net.Listen("tcp", "127.0.0.1:1234")
	if err != nil {
		fmt.Printf("Listen tcp failed, error: %v", err)
		return	
	}
	go rpcServer.Accept(l)
	time.Sleep(1 * time.Second)
	fmt.Println("RPC server start success.")
}

上面已经为数据库提供了对外访问的方式.现在,需要一个远程代理来连接数据库服务端,并进行相关的数据库的操作.对消息处理系统而言,它不需要,也不应该知道远程代理与数据库服务端交互的底层细节,这样可以减轻系统之间的耦合.因此,远程代理需要实现db.KvDb

package db
...
// 数据库服务端远程代理,实现db.KvDb接口
type Client struct {
	// RPC客户端
	cli *rpc.Client
}

func (c *Client) Save(record Record, Reply *bool) error {
	var ret bool
	// 通过RPC调用服务端的接口
	err := c.cli.Call("Server.Save", record, &ret)
	if err != nil {
		fmt.Printf("Call db Server.Save rpc failed, error: %v", err)
		*reply = false
		return err
	}
	*reply = ret
	return nil
}

func (c *Client) Get(key string, reply *string) error {
	var ret string
	// 通过RPC调用服务端的接口
	err := c.cli.Call("Server.Get", record, &ret)
	if err != nil {
		fmt.Printf("Call db Server.Get rpc failed, error: %v", err)
		*reply = false
		return err
	}
	*reply = ret
	return nil
}

// 工厂方法,返回远程代理实例
func CreateClient() *Client {
	rpcCli, err := rpc.Dial("tcp", "127.0.0.1:1234")
	if err != nil {
		fmt.Printf("Create rpc client failed, error: %v.",err)
		return nil
	}
	return &Client{cli: rpcCli}
}

作为远程代理的db.Client并没有直接持有db.Server的引用,而是持有了它的ip:port

通过RPC客户端调用了它的方法

装饰模式(Decorator Pattern)

装饰模式使用组合而非继承的方式,能够动态的为本体对象叠加新的行为

装饰模式最经典的应用当属Java的I/O流体系,通过装饰模式,使用者可以动态地为原始的输入输出流添加功能,比如按照字符串输入输出,添加缓存等

从结构上看,装饰模式和代理模式具有很高的相似性,但是两种所强调的点不一样.装饰模式强调的是为本体对象添加新的功能,代理模式强调的是对本体对象的访问控制

行为型模式(Behavioral Pattern)