再次通过结构型模式视角回顾Go
形而上 Lv5

结构型模式主要关注如何将类或者对象组合成更大的结构,以便在不改变原有类或者对象的情况下,实现新的功能或者优化结构。

结构型模式核心思想是通过组合(Composition)而不是继承(Inheritance)来实现代码的复用和扩展

代理,桥接,组合,装饰器,适配器,外观,享元,七种模式。

结构型模式-代理模式

代理模式:通过代理对象来控制另一个对象,核心是不改变原对象的情况下,通过代理对象来增强或者限制对原对象的访问。

常见场景:

  • 延迟初始化
  • 访问控制
  • 日志记录
  • 缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package structural_pattern

import "fmt"

type Log struct {
}

func (Log) Info(content string) {
fmt.Println(content)
}

type ProxyLog struct {
log *Log
}

func (p *ProxyLog) Info(content string) {
//延迟初始化
if p.log == nil {
p.log = &Log{}
}

//访问前
p.log.Info(content)
//访问后
}
1
2
3
4
5
6
7
8
package structural_pattern

import "testing"

func TestProxyLog_Info(t *testing.T) {
pl := ProxyLog{}
pl.Info("hello world")
}

结构型模式-桥接模式

核心思想是将抽象部分和实现部分分离,是他们可以独立变化。避免类的数量爆炸,提高可扩展可维护性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package bridge
import "fmt"
// 电脑
type Computer interface {
Print(file string) //打印文件
SetPrinter(Printer) //设置打印机
}
// 打印机
type Printer interface {
PrintFile(file string) // 打印文件
}
type Epson struct{}
func (Epson) PrintFile(file string) {
fmt.Println("使用爱普生打印机打印文件", file)
}
type Hp struct{}
func (hp Hp) PrintFile(file string) {
fmt.Println("使用惠普打印机打印文件", file)
}
type Mac struct {
printer Printer
}
func (m *Mac) Print(file string) {
//桥接到打印机的打印方法上
m.printer.PrintFile("mac:" + file)
}
func (m *Mac) SetPrinter(p Printer) {
m.printer = p
}
type Windows struct {
printer Printer
}
func (ws *Windows) Print(file string) {
ws.printer.PrintFile("windows:" + file)
}
func (ws *Windows) SetPrinter(p Printer) {
ws.printer = p
}
1
2
3
4
5
6
7
8
9
10
11
12
package bridge

import "testing"

func TestBridge(t *testing.T) {
w := Windows{}
hp := Hp{}

w.SetPrinter(hp)
w.Print("xxx")
}

结构型模式-组合模式

将对象组合成树形层次关系, “部分-整体”, 组合模式让客户端可以统一的处理单个对象和对象的组合。

应用场景:

  • 文件系统:文件,文件夹,文档树
  • 组织结构:部门员工关系

为什么使用组合模式:

  • 简化客户端代码
  • 增强灵活性
  • 提高可扩展性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package composite

import "fmt"

type Node interface {
Display(ident string)
}

type File struct {
Name string
}

type Dir struct {
Name string
Children []Node
}

func (f File) Display(ident string) {
fmt.Println(ident + f.Name)
}

func (d Dir) Display(ident string) {
fmt.Println(ident + d.Name)
for _, child := range d.Children {
child.Display(ident + " ")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package composite

import "testing"

func TestComposite(t *testing.T) {
root := Dir{
Name: "CreateProjects",
Children: []Node{
Dir{
Name: "cmd",
Children: []Node{
File{
Name: "main.go",
},
},
},
File{
Name: "go.mod",
},
},
}
root.Display("")
}

结构型模式-装饰器模式

动态的为对象添加行为和职责,不需要修改原始类。或者引入装饰者类, 运行时灵活的组合不通功能,而不需要创建大量子类。

装饰者的核心是为对象包装在一个或多个装饰者中,每个装饰者可以在调用被装饰方法前后添加额为的行为。

例如:

  • web服务添加,日志记录
  • web服务添加性能监控

很像代理模式,代理模式除了增强还可以限制, 装饰器模式一般增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package decorator

import (
"fmt"
"time"
)

type ReqI interface {
Handler(url string) string
}

type Req struct {
}

func (r Req) Handler(url string) string {
fmt.Println("请求" + url)
return ""
}

// LogDecorator 日志装饰器
type LogDecorator struct {
req ReqI
}

func (l LogDecorator) Handler(url string) string {
fmt.Println("日志记录前")
res := l.req.Handler(url)
fmt.Println("日志记录后")
return res
}

type MonitorDecorator struct {
req ReqI
}

func (m MonitorDecorator) Handler(url string) string {
t1 := time.Now()
res := m.req.Handler(url)
sub := time.Since(t1)
fmt.Println("耗时:", sub)
return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package decorator

import (
"testing"
)

func TestReq_Handler(t *testing.T) {
req := Req{}
logReq := LogDecorator{
req: req,
}
mReq := MonitorDecorator{
req: logReq,
}
mReq.Handler("baidu.com")
}

结构型模式-适配器模式

结构型模式最简单的一种

将一个类的接口转换成客户端期望的另一个接口,从而使不兼容的类能够一起工作

假如你的系统需要继承一个第三方的支付功能,它的功能与你的接口不兼容,转换接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package adapter

type AliPay struct {
}

func (a AliPay) GetPayPage(price int64) string {
return "支付宝连接"
}

type WeixinPay struct {
}

func (w WeixinPay) PayPage(price int64) string {
return "微信支付连接"
}

type PayI interface {
PayPage(price int64) string
}

func PayPage(pi PayI, price int64) string {
return pi.PayPage(price)
}

type AliPayAdapter struct {
aliPay *AliPay
}

func (a AliPayAdapter) PayPage(price int64) string {
return a.aliPay.GetPayPage(price)
}
1
2
3
4
5
6
7
8
9
10
11
package adapter

import (
"fmt"
"testing"
)

func TestPayPage(t *testing.T) {
fmt.Println(PayPage(WeixinPay{}, 1))
fmt.Println(PayPage(AliPayAdapter{aliPay: &AliPay{}}, 1))
}

结构型模式-外观模式

提供了一个统一的接口,用于访问子系统中的一组接口

外观模式的核心思想是简化复杂系统的使用,通过提供一个高层接口,隐藏系统的复杂性,使客户端更容易使用

比如:前后端解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package facade

import "fmt"

type Inventory struct {
}

func (k Inventory) Deduction() {
fmt.Println("扣库存")
}

type Pay struct {
}

func (p Pay) Pay() {
fmt.Println("下单")
}

type Logistics struct{}

func (l Logistics) SendOutGoods() {
fmt.Println("发货")
}

type Order struct {
i *Inventory
p *Pay
l *Logistics
}

func NewOrder() *Order {
return &Order{
i: &Inventory{},
p: &Pay{},
l: &Logistics{},
}
}

func (o Order) place() {
o.i.Deduction()
o.p.Pay()
o.l.SendOutGoods()
}
1
2
3
4
5
6
package facade
import "testing"
func TestLogistics_SendOutGoods(t *testing.T) {
o := NewOrder()
o.place()
}

结构型模式-享元模式

通过共享对象来减少内存使用和提高性能

核心思想是将对象共享部分(内部状态)与不可共享部分(外部状态)分离,从而减少重复对象的创建

举个例子:共享单车和百度网盘中的文件

核心思想:

  • 共享对象
  • 分离状态
  • 工厂管理

各种池技术,连接池,对象池等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package flyweight

import (
"fmt"
)

type DB struct {
id int
}

func NewDB(id int) *DB {
return &DB{id}
}

func (db DB) Query(str string) {
fmt.Printf("使用 %d 连接对象 进行查询操作\n", db.id)
}

type DBPool struct {
pool map[int]*DB
nextId int
}

func NewDBPool(num int) *DBPool {
var pool = map[int]*DB{} //sync.Map{} 这里线程不安全
for i := 0; i < num; i++ {
pool[i] = NewDB(i)
}
return &DBPool{
pool: pool,
nextId: num - 1,
}
}

func (p *DBPool) GetDB() *DB {
if len(p.pool) > 0 {
for id, db := range p.pool {
delete(p.pool, id)
return db
}
}
db := NewDB(p.nextId)
return db
}

func (p *DBPool) ReleaseDB(db *DB) {
p.pool[db.id] = db
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package flyweight

import "testing"

func TestNewDBPool(t *testing.T) {
pool := NewDBPool(10)
db1 := pool.GetDB()
db1.Query("select * from xxx")
db2 := pool.GetDB()
db2.Query("select * from xxx")
pool.ReleaseDB(db2)
db3 := pool.GetDB()
db3.Query("select * from xxx")
pool.ReleaseDB(db3)
}

  • 本文标题:再次通过结构型模式视角回顾Go
  • 本文作者:形而上
  • 创建时间:2022-07-02 00:00:00
  • 本文链接:https://deepter.gitee.io/2022_07_02_go_design_pattern/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!