结构型模式主要关注如何将类或者对象组合成更大的结构,以便在不改变原有类或者对象的情况下,实现新的功能或者优化结构。
结构型模式核心思想是通过组合(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_patternimport "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_patternimport "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 bridgeimport "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 bridgeimport "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 compositeimport "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 compositeimport "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("" ) }
结构型模式-装饰器模式 动态的为对象添加行为和职责,不需要修改原始类。或者引入装饰者类, 运行时灵活的组合不通功能,而不需要创建大量子类。
装饰者的核心是为对象包装在一个或多个装饰者中,每个装饰者可以在调用被装饰方法前后添加额为的行为。
例如:
很像代理模式,代理模式除了增强还可以限制, 装饰器模式一般增强
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 decoratorimport ( "fmt" "time" ) type ReqI interface { Handler(url string ) string } type Req struct {} func (r Req) Handler(url string ) string { fmt.Println("请求" + url) return "" } 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 decoratorimport ( "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 adaptertype 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 adapterimport ( "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 facadeimport "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 facadeimport "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 flyweightimport ( "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{} 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 flyweightimport "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) }