一文彻底弄懂23种设计模式


写在前面

三年前在github上创建了一个仓库:https://github.com/ZBIGBEAR/design_pattern, 最近发现都是空的,没有实现。最近也准备找找工作,于是重新学一下设计模式,并且用go实现。

分类

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型模式(五种)

工厂模式

工厂模式一般有简单工厂模式、工厂方法模式、抽象工厂模式,太多太杂了,不便于学习与记忆。作者认为只要学习抽象工厂模式这一种就型了,如果在工作中要用到工厂模式就用这种。

1.定义工厂的接口

// 定义工厂
type Factory interface {
	ProducePizza() string // 生产pizza
}

2.定义创建工厂的函数,给抽象工厂对象调用,创建工厂

// 创建工厂的回调函数
type NewFactory func(name string) Factory

3.定义抽象工厂的接口

// 定义创建工厂的接口
type CreateFactory interface {
	RegisterCallback(name string, callback NewFactory) // 注册创建工厂的回调函数
	NewFactory(name string) Factory // 创建工厂,传入功能名称
}

至此,一个抽象工厂模式的结构就定义完成了,下面是具体的实现

4.实现抽象工厂的接口,并定义创建函数

// 实现创建工厂的接口
type createFactory struct {
	factories map[string]NewFactory
}

func NewCreateFactory() CreateFactory {
	return &createFactory{
		factories: map[string]NewFactory{},
	}
}

func (c *createFactory) RegisterCallback(name string, callback NewFactory) {
	if _, ok := c.factories[name]; ok {
		// 已经存在,则报错或者直接返回
		return
	}
	c.factories[name] = callback
}

func (c *createFactory) NewFactory(name string) Factory {
	callback, ok := c.factories[name]
	if !ok {
		// 不存在,则报错或者返回nil
		return nil
	}
	return callback(name)
}

5.实现具体的工厂,即实现Factory接口

(1)伦敦工厂

// 伦敦工厂
type ldFactory struct {
	name string
}

func NewLondon(name string) Factory {
	return &ldFactory{
		name: name,
	}
}

func (l *ldFactory) ProducePizza() string {
	return fmt.Sprintf("this pizza from:%s\n", l.name)
}

(2)巴黎工厂

// 巴黎工厂
type parisFactory struct {
	name string
}

func NewParis(name string) Factory {
	return &parisFactory{
		name: name,
	}
}

func (l *parisFactory) ProducePizza() string {
	return fmt.Sprintf("this pizza from:%s\n", l.name)
}

至此,抽象工厂的创建以及具体的工厂都实现了,下面是测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestAbstractFactory(t *testing.T) {
	// 1.定义一个创建工厂的对象
	absFactory := NewCreateFactory()
	// 2.添加创建工厂的回调函数
	absFactory.RegisterCallback("London", NewLondon)
	absFactory.RegisterCallback("Paris", NewParis)
	// 3.测试
	// 创建伦敦工厂,并且生产一个pizza
	ldFact := absFactory.NewFactory("London")
	assert.Equal(t, "this pizza from:London\n", ldFact.ProducePizza())
	// 创建巴黎工厂,并且生产一个pizza
	paFact := absFactory.NewFactory("Paris")
	assert.Equal(t, "this pizza from:Paris\n", paFact.ProducePizza())
}

结果

=== RUN   TestAbstractFactory
--- PASS: TestAbstractFactory (0.00s)
PASS

单例模式

定义:确保一个类最多只有一个实例,并提供一个全局访问点

1.定义一个工厂接口

// 定义一个单例工厂的接口
type SingleFactory interface {
	Pizza() string // 生产pizza
}

2.定义创建工厂的接口

// 定义创建单例工厂的接口
type AbstractSingleFactory interface {
	Get() SingleFactory
}

至此,单例模式接口定义完成,下面是具体的实现

3.实现创建工厂的接口

// 实现创建单例工厂
type abstractSingleFactory struct {
	once       sync.Once
	singleFact SingleFactory
}

func NewAbstractSingleFactory() AbstractSingleFactory {
	return &abstractSingleFactory{}
}

func (a *abstractSingleFactory) Get() SingleFactory {
	if a.singleFact == nil {
		a.once.Do(func() {
			a.singleFact = &singleFactory{}
		})
	}
	return a.singleFact
}

4.实现工厂的接口

// 实现工厂
type singleFactory struct {
}

func (s *singleFactory) Pizza() string {
	return fmt.Sprintf("this pizza is from single factory")
}

至此,单例工厂模式实现完成,下面是测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestSingleFactory(t *testing.T) {
	absSingleFact := NewAbstractSingleFactory()
	single := absSingleFact.Get()
	assert.Equal(t, "this pizza is from single factory", single.Pizza())
}

结果

=== RUN   TestSingleFactory
--- PASS: TestSingleFactory (0.00s)
PASS

这里用的是go中的sync.Once类来确保工厂只会初始化一次,并且在第一次加载的时候初始化,从而实现懒加载。

如果不用sync.Once来实现懒加载,也可以在init()函数中初始化工厂,在Get() SingleFactory函数中直接返回初始化好的工厂实例即可,这里就不赘述了。

生成器模式

又叫构建者模式,封装一个复杂对象构造过程,并允许按步骤构造。

下面用电脑作为实例,电脑又主机、键盘、鼠标等。这里电脑就是复杂对象,主机、键盘、鼠标就是各个小模块。

1.定义电脑和各个小模块的接口

// 电脑
type Computer interface {
	Name() string
}

// 主机
type Master interface {
	Name() string
}

// 键盘
type Keyboard interface {
	Name() string
}

// 鼠标
type Mouse interface {
	Name() string
}

2.实现电脑

type computer struct {
	master   Master
	keyboard Keyboard
	mouse    Mouse
}

func (c *computer) Name() string {
	return fmt.Sprintf("i am a computer. master:%s,keyboard:%s,mouse:%s", c.master.Name(), c.keyboard.Name(), c.mouse.Name())
}

3.实现各个小模块以及他们的创建函数

type master struct {
	name string
}

func NewMaster(name string) Master {
	return &master{name: name}
}

func (m *master) Name() string {
	return m.name
}

type keyboard struct {
	name string
}

func NewKeyboard(name string) Keyboard {
	return &keyboard{name: name}
}

func (k *keyboard) Name() string {
	return k.name
}

type mouse struct {
	name string
}

func NewMouse(name string) Mouse {
	return &mouse{name: name}
}

func (m *mouse) Name() string {
	return m.name
}

4.实现电脑的创建

func NewComputer(master, keyboard, mouse string) Computer {
	return &computer{
		master:   NewMaster(master),
		keyboard: NewKeyboard(keyboard),
		mouse:    NewMouse(mouse),
	}
}

至此,生成器模式已经实现,下面是单测

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestBuild(t *testing.T) {
	computer := NewComputer("mastername", "keyboardname", "mousename")
	assert.Equal(t, "i am a computer. master:mastername,keyboard:keyboardname,mouse:mousename", computer.Name())
}

结果

=== RUN   TestBuild
--- PASS: TestBuild (0.00s)
PASS

原型模式

通过复制现有实例来创建新的实例,无需知道相应类的信息。

说白了就是拷贝,基于现有对象做简单的修改,就可以使用原型模式。

1.定义一个对象

type Person interface {
	Clone() Person
	Info() string
	SetName(name string)
}

接口里面定义了Clone() Person函数

2.实现对象并且定义创建对象的方法

type person struct {
	name  string
	grade string
	tel   string
}

func NewPerson(name, grade, tel string) Person {
	return &person{
		name:  name,
		grade: grade,
		tel:   tel,
	}
}

func (p *person) Info() string {
	return fmt.Sprintf("name:%s,grade:%s,tel:%s", p.name, p.grade, p.tel)
}

func (p *person) SetName(name string) {
	p.name = name
}

func (p *person) Clone() Person {
	return &person{
		name:  p.name,
		grade: p.grade,
		tel:   p.tel,
	}
}

下面是测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestPrototype(t *testing.T) {
	p := NewPerson("张三", "男", "13212345678")
	assert.Equal(t, "name:张三,grade:男,tel:13212345678", p.Info())
	p1 := p.Clone()
	p1.SetName("李四")
	assert.Equal(t, "name:李四,grade:男,tel:13212345678", p1.Info())
}

结果

=== RUN   TestPrototype
--- PASS: TestPrototype (0.00s)
PASS

拷贝有两种:深拷贝和浅拷贝。可以根据需要自己实现。

结构型模式(七种)

适配器模式

定义: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

但是go语言中,遵循的是面向接口编程,对象之间的以来都通过接口来解耦,因此这里主要讲一下接口适配器。

下面以电视投影(可以投影到电脑、墙壁)为例

1.定义电视接口

// 定义一个电视接口
type TV interface {
	Name() string
	Play() string       // 播放
	Projection() string // 投影
	SetProjection(project Projection)
}

2.定义投影接口

// 定义投影接口
type Projection interface {
	Project() string // 投影
}

3.实现电视接口

// 实现电视
type tv struct {
	projection Projection // 投影设备
	name       string
}

func NewTV() TV {
	return &tv{}
}

func (t *tv) Name() string {
	return t.name
}

func (t *tv) Play() string {
	return "播放电视"
}

func (t *tv) Projection() string {
	return t.projection.Project()
}

func (t *tv) SetProjection(projection Projection) {
	t.projection = projection
}

4.设备一:投影到电脑

// 投影到电脑
type computerProjection struct {
}

func NewComputerProjection() Projection {
	return &computerProjection{}
}

func (c *computerProjection) Project() string {
	return "this is computer projection"
}

5.设备二:投影到墙壁

// 投影到墙壁
type wallProjection struct {
}

func NewWallProjection() Projection {
	return &wallProjection{}
}

func (c *wallProjection) Project() string {
	return "this is wall projection"
}

至此,适配器模式定义完成,下面是单测

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestInterfaceAdapter(t *testing.T) {
	tv := NewTV()
	// 1.投影到电脑
	computerProject := NewComputerProjection()
	tv.SetProjection(computerProject)
	assert.Equal(t, "this is computer projection", tv.Projection())

	// 2.投影到墙壁
	wallProject := NewWallProjection()
	tv.SetProjection(wallProject)
	assert.Equal(t, "this is wall projection", tv.Projection())
}

结果

=== RUN   TestInterfaceAdapter
--- PASS: TestInterfaceAdapter (0.00s)
PASS

装饰器模式

通过额外的装饰来给对象添加新的行为

下面通过小狗这个类来实现一只生病的小狗
1.定义动物接口

type Animal interface {
	Walk() string
}

2.定义一个类:一只小狗

type dog struct {
	name string
}

func NewDog(name string) Animal {
	return &dog{
		name: name,
	}
}

func (d *dog) Walk() string {
	return fmt.Sprintf("name:%s, i walk with four legs", d.name)
}

3.定义一个装饰类:一只生病的小狗

// 装饰器: 一只生病的狗
type decoratorDog struct {
	d Animal
}

func (d *decoratorDog) Walk() string {
	return "i am sick. " + d.d.Walk()
}

func NewDecoratorDay(d Animal) Animal {
	return &decoratorDog{
		d: d,
	}
}

注意,装饰类和原始类都实现了Animal接口,即他们要实现相同的接口

下面是单测

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestDecorator(t *testing.T) {
	d := NewDog("dagname")
	assert.Equal(t, "name:dagname, i walk with four legs", d.Walk())
	d = NewDecoratorDay(d) // 将d装饰一下
	assert.Equal(t, "i am sick. name:dagname, i walk with four legs", d.Walk())
}

结果

=== RUN   TestDecorator
--- PASS: TestDecorator (0.00s)
PASS

装饰者模式和继承非常像,它是在调用被装饰者相关函数之前做一些其他的事情,对于调用者来说是无感知的。

代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

代理模式和装饰者模式很像,但是他们之间又有区别。

  • 代理模式主要是控制对象的访问,它是在客户端和目标对象之间的中介,在客户端访问目标对象前后增加一些额外的操作,让客户端访问目标对象更容易、更专注于自己需要做的事情,而忽略无关紧要的事情。例如客户买房子,对于客户来说,只需要专注于选房子、付款、收房子。中介要做的就是验房子、签合同、过户等事情,这里的中介就是代理。对目标对象来说,它只知道中介的存在,不用知道客户端是谁。
  • 装饰者主要是用于对原始对象的扩展。还是用客户买房子为例,在客户付款之前,客户可能需要验房子、签合同,付款之后,客户需要过户。这里客户做了中介需要做的事情。对目标用户来说,它的对象是客户,只是这个客户被装饰了一下,但是客户对目标对象提供的访问方法是不变的。目标客户是不需要知道这个装饰过程的,甚至它不知道客户被装饰了。

1.定义代理接口,即代理

// 假设买房子有3步骤:1.验房;2.付款;3.过户
type ProxyBuyer interface {
	Check() string
	Buy() string
	ChangeOwner() string
}

2.定义用户,即客户端

// 对于用户来说,只有一个步骤:付款
type Buyer interface {
	Buy() string
	Name() string
}

3.实现代理

// 实现proxy
type proxyBuyer struct {
	buyer Buyer
}

func NewProxyBuyer(b Buyer) ProxyBuyer {
	return &proxyBuyer{buyer: b}
}

func (p *proxyBuyer) Check() string {
	return "check"
}

func (p *proxyBuyer) Buy() string {
	return fmt.Sprintf("i am proxy. %s", p.buyer.Buy())
}

func (p *proxyBuyer) ChangeOwner() string {
	return fmt.Sprintf("i am proxy. the house owner is:%s", p.buyer.Name())
}

4.实现用户

// 实现购房者
type buyer struct {
	name string
}

func NewBuyer(name string) Buyer {
	return &buyer{
		name: name,
	}
}

func (b *buyer) Buy() string {
	return fmt.Sprintf("i am:%s, i buy the house", b.name)
}

func (b *buyer) Name() string {
	return b.name
}

至此,代理模式定义完成,下面是单测

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestProxy(t *testing.T) {
	// 1.实例化一个用户
	buyer := NewBuyer("张三")
	proxy := NewProxyBuyer(buyer)
	assert.Equal(t, "i am proxy. i am:张三, i buy the house", proxy.Buy())
	assert.Equal(t, "i am proxy. the house owner is:张三", proxy.ChangeOwner())
}

结果

=== RUN   TestProxy
--- PASS: TestProxy (0.00s)
PASS

外观模式

也叫门面模式,隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。

例如,电脑开机,用户只需要按下开关,然后等几分钟就行了。实际电脑内部需要启动很多模块,包括主机、键盘、鼠标,电脑内部都封装好了,对外只暴露一个接口:开机。这就是外观模式。

1.定义电脑、主机、键盘、鼠标启动接口

type Computer interface {
	Start() string
}

type Master interface {
	Start() string
}

type Keyboard interface {
	Start() string
}

type Mouse interface {
	Start() string
}

2.实现电脑、主机、键盘、鼠标启动接口

type computer struct {
	master   *master
	keyboard *keyboard
	mouse    *mouse
}

func NewComputer(m, k, mo string) Computer {
	return &computer{
		master:   &master{name: m},
		keyboard: &keyboard{name: k},
		mouse:    &mouse{name: mo},
	}
}

func (c *computer) Start() string {
	return fmt.Sprintf("computer start. %s,%s,%s", c.master.Start(), c.keyboard.Start(), c.mouse.Start())
}

type master struct {
	name string
}

func (m *master) Start() string {
	return fmt.Sprintf("master:%s start", m.name)
}

type keyboard struct {
	name string
}

func (m *keyboard) Start() string {
	return fmt.Sprintf("keyboard:%s start", m.name)
}

type mouse struct {
	name string
}

func (m *mouse) Start() string {
	return fmt.Sprintf("mouse:%s start", m.name)
}

测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestFacade(t *testing.T) {
	// 1.实例化一个用户
	computer := NewComputer("mastername", "keyboardname", "mousename")
	assert.Equal(t, "computer start. master:mastername start,keyboard:keyboardname start,mouse:mousename start", computer.Start())
}

结果

=== RUN   TestFacade
--- PASS: TestFacade (0.00s)
PASS

桥接模式

桥接模式就是把包含2组信息的类拆分成2个类,通过聚合的形式桥接到一起。

这样,就只需要独立定义每一组信息的不同的类,而不需要交叉定义所有的类。

例如如果要定义红色的苹果、红色的香蕉、绿色的苹果、绿色的香蕉,定义4个类肯定不合适,如果颜色颜色和水果种类更多呢?

这里,就可以把颜色和水果单独拿出来定义成独立的类,然后不同的水果通过组合的形式组装在一起,就可以实现任意水果的任意颜色。这个组装过程就是桥接。

1.定义颜色接口和水果种类接口

// 定义颜色接口
type Color interface {
	Name() string
}

// 定义水果接口
type Fruit interface {
	Name() string
}

2.实现颜色接口和水果种类接口

// 实现颜色接口
type color struct {
	name string
}

func NewColor(name string) Color {
	return &color{name: name}
}

func (c *color) Name() string {
	return c.name
}

// 实现水果接口
type fruit struct {
	name string
}

func NewFruit(name string) Fruit {
	return &fruit{name: name}
}

func (c *fruit) Name() string {
	return c.name
}

3.定义一个带颜色的水果接口

// 实现带颜色的水果
type ColorFruit interface {
	SetColor(c Color)
	SetFruit(f Fruit)
	Eat() string
}

这个接口把颜色和水果种类组合在一起,这就是桥接模式

4.实现带颜色的水果接口

// 实现带颜色的水果
type colorFruit struct {
	c Color
	f Fruit
}

func NewColorFruit() ColorFruit {
	return &colorFruit{}
}

func (c *colorFruit) SetColor(color Color) {
	c.c = color
}

func (c *colorFruit) SetFruit(f Fruit) {
	c.f = f
}

func (c *colorFruit) Eat() string {
	return fmt.Sprintf("eat:%s, color is:%s", c.f.Name(), c.c.Name())
}

下面是测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestBridge(t *testing.T) {
	// 1.定义一个红色、绿色
	red := NewColor("red")
	green := NewColor("green")
	// 2.定义一个苹果、香蕉
	apple := NewFruit("apple")
	banana := NewFruit("banana")
	// 3.定义一个水果
	f := NewColorFruit()

	// 接下来可以任意设置f这个水果的颜色以及具体的水果
	// 4.一个红色的苹果
	f.SetColor(red)
	f.SetFruit(apple)
	assert.Equal(t, "eat:apple, color is:red", f.Eat())
	// 5.一个绿色的香蕉
	f.SetColor(green)
	f.SetFruit(banana)
	assert.Equal(t, "eat:banana, color is:green", f.Eat())
}

结果

=== RUN   TestBridge
--- PASS: TestBridge (0.00s)
PASS

组合模式

把一些相同对象放在一起,组合成树形结构,递归调用

例如:一棵树有树枝和树叶,树枝是嵌套结构,从顶往下,一层一层,树叶在最底层。

可以抽象出一个组件接口,树叶、树枝都继承这个接口

1.定义组件接口

// 组件
type Component interface {
	Add(component Component)
	Remove(component Component)
	Visit() string
	Name() string
}

2.定义树叶接口和树枝接口

// 树叶接口,继承Component组件
type Leaf interface {
	Component
}

// 树枝接口,继承Component组件
type Branch interface {
	Component
}

3.实现树叶

// 实现树叶
type leaf struct {
	name string
}

func NewLeaf(name string) Leaf {
	return &leaf{name: name}
}

func (l *leaf) Add(component Component) {
	// 树叶不能再添加
}

func (l *leaf) Remove(component Component) {
	// 树叶不能移除
}

func (l *leaf) Visit() string {
	// 树叶可被访问
	return fmt.Sprintf("leaf:%s;", l.name)
}

func (l *leaf) Name() string {
	return l.name
}

4.实现树枝接口

// 实现树枝
type branch struct {
	name string
	list []Component
}

func NewBranch(name string) Branch {
	return &branch{
		name: name,
		list: make([]Component, 0),
	}
}

func (l *branch) Add(component Component) {
	l.list = append(l.list, component)
}

func (l *branch) Remove(component Component) {
	idx := -1
	for i := range l.list {
		if l.list[i].Name() == component.Name() {
			idx = i
			break
		}
	}
	newList := l.list[:idx]
	newList = append(newList, l.list[idx+1:]...)
	l.list = newList
}

func (l *branch) Visit() string {
	result := ""
	for i := range l.list {
		result += l.list[i].Visit()
	}
	if result == "" {
		return l.name
	}
	return l.name + ";" + result
}

func (l *branch) Name() string {
	return l.name
}

下面是测试用例


import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestComposite(t *testing.T) {
	// 1.定义一棵树
	root := NewBranch("root")
	// 2.添加树枝
	c1 := NewBranch("c1")
	root.Add(c1)
	c2 := NewBranch("c2")
	// 3.定义一个带叶子的树枝
	c3 := NewBranch("c3")
	l1 := NewLeaf("leaf1")
	l2 := NewLeaf("leaf2")
	c3.Add(l1)
	c3.Add(l2)
	c2.Add(c3)
	root.Add(c2)
	assert.Equal(t, "root;c1c2;c3;leaf:leaf1;leaf:leaf2;", root.Visit())
	// 4.移除c1
	root.Remove(c1)
	assert.Equal(t, "root;c2;c3;leaf:leaf1;leaf:leaf2;", root.Visit())
}

结果

=== RUN   TestComposite
--- PASS: TestComposite (0.00s)
PASS

享元模式

享元模式就是共享对象模式,常见于池子。

先定义一个池子(比如大小为100),然后初始化(初始化100个对象),对外暴露Get接口,需要用的时候,Get函数从池子中获取,用完再放回。

主要用于数据库连接池、并发任务定义的缓冲池等等。

行为型模式(十一种)

策略模式

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户

模板方法模式

定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

迭代子模式

提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示

责任链模式

如果有多个对象有机会处理请求,责任链可使请求的发送者和接受者解耦,请求沿着责任链传递,直到有一个对象处理了它为止

一般中间件可以使用这种模式,例如gin框架的middleware

命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式

状态模式

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象

访问者模式

访问者模式是一种行为型设计模式,它可以让你在不修改现有类的情况下,向现有类添加新的行为。该模式适用于对象结构相对稳定但需要频繁添加新功能的场景。访问者模式将数据结构与操作分离,使得操作可以独立变化而不影响数据结构,同时还可以在不修改数据结构的情况下增加新的操作。

1.定义访问者接口

// 定义访问者接口
type Visitor interface {
	Visit(Element) string
}

2.定义元素的接口

// 定义元素
type Element interface {
	// 接受访问者
	Accept(visitor Visitor) string
	Name() string
}

3.实现访问者接口和元素接口

// 定义一个人,实现访问者
type people struct {
	name string
}

func NewPerson(name string) Visitor {
	return &people{name: name}
}

func (p *people) Visit(element Element) string {
	return fmt.Sprintf("i am visitor:%s, visit element:%s", p.name, element.Name())
}

// 定义一个车,实现元素
type car struct {
	name string
}

func NewCar(name string) Element {
	return &car{name: name}
}

func (c *car) Accept(visitor Visitor) string {
	return visitor.Visit(c)
}

func (c *car) Name() string {
	return c.name
}

下面是测试用例

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestVisitor(t *testing.T) {
	// 1.定义一个车
	car := NewCar("carname")
	// 2.定义一个人
	p := NewPerson("张三")
	assert.Equal(t, "i am visitor:张三, visit element:carname", car.Accept(p))
}

中介者模式

中介者模式是一种行为型设计模式,它通过引入一个中介者对象来封装一系列对象之间的交互,从而解耦这些对象之间的直接关联关系。中介者模式有助于减少对象之间的耦合度,使系统更易于维护和扩展,并且有利于降低系统的复杂性

中介者模式的核心思想是通过引入一个中介者对象,来将系统中各个对象之间的交互行为集中处理。这个中介者对象拥有对系统中所有相关对象的引用,并负责协调这些对象之间的交互。

1.定义中介者接口

// 中介者接口
type Mediator interface {
	Register(object Object)
	Send(from, to Object, message string) string
}

2.定义访问对象的接口

// 对象
type Object interface {
	Name() string
	Receive(from Object, message string) string
}

3.实现中介者接口

// 实现中介者的接口
type mediator struct {
	objects map[string]Object
}

func NewMediator() Mediator {
	return &mediator{
		objects: make(map[string]Object),
	}
}

func (m *mediator) Register(object Object) {
	if _, ok := m.objects[object.Name()]; ok {
		// 已经存在,则报错,或者直接返回
		return
	}
	m.objects[object.Name()] = object
}

func (m *mediator) Send(from, to Object, message string) string {
	fromObj := m.objects[from.Name()]
	toObj := m.objects[to.Name()]
	return toObj.Receive(fromObj, message)
}

4.实现访问对象的接口

// 实现对象的接口
type object struct {
	name string
}

func NewObject(name string) Object {
	return &object{name: name}
}

func (o *object) Name() string {
	return o.name
}

func (o *object) Receive(from Object, message string) string {
	return fmt.Sprintf("%s receive %s from %s", o.name, message, from.Name())
}

下面是测试用例


import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMediator(t *testing.T) {
	// 1.定义一个中介者
	mediator := NewMediator()
	// 2.定义2个对象
	o1 := NewObject("object1")
	o2 := NewObject("object2")
	// 3.注册
	mediator.Register(o1)
	mediator.Register(o2)
	assert.Equal(t, "object2 receive 你好 from object1", mediator.Send(o1, o2, "你好"))
}

结果

=== RUN   TestMediator
--- PASS: TestMediator (0.00s)
PASS

解释器模式

总结

本文实现了23重设计模式,show me code,代码比语言更句说服力、更具体、更清晰易懂,希望能帮助到大家。

写这篇文章的目的,一个是为了输出自己所学,帮助大家理解设计模式。另外一个目的也是为了检验自己的学习成果,记录学习成果。随着时间的推移,肯定会忘记。但是有了这篇文章,将来在任何时候看一遍就能快速捡起来。

写一篇高质量技术文章真心不容易,这篇文章花了大半天实际,而且质量并不好,为那些输出高质量文章的作者点赞。

文章中的源代码:https://github.com/ZBIGBEAR/design_pattern

参考

[1]23 种设计模式详解(全23种)

[2]23种设计模式


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录