享元模式是一种结构型的设计模式,通过共享细粒度对象实现对象的复用,从而达到减少对象创建与销毁,减少内存消耗,其本质是本质是缓存共享对象,降低内存消耗。
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。[1]
- 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
- 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
在使用享元模式时,我们需要注意与单例模式的区别:
- 1.如果请求对象都一样,可以使用单例模式
- 2.如果请求对象相似,可以共享多个共同属性
结构
- 1.抽象享元角色(Flyweight):即享元的接口,定义相关方法
- 2.具体享元(Concrete Flyweight)角色:实现享元接口
- 3.享元工厂(Flyweight Factory)角色:管理享元的创建销毁等
优缺点
-
优点
1.缩小对象的创立,升高内存中对象的数量,升高零碎的内存,提高效率。
2.缩小内存之外的其余资源占用。 -
缺点
1.为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
2.读取享元模式的外部状态会使得运行时间稍微变长。
适用场景
1.系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源,如需要缓冲池,数据库连接池。
2.大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
3.由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
代码实现
在 golang 实现中,我们可以用 map 结构,也可以用 sync.Pool 来实现,sync.Pool则封装更好,具体看需求。
package main
import (
"fmt"
"sync"
)
/*
业务场景描述:
- 租车场景,客户像车行租车,如果车行有车直接租给客户,没有则从车行的其他分部调车分配
场景角色:
- 1.抽象享元,接口,实现产品具有属性
- 2.具体享元,类,实现抽象享元
- 3.享元工厂,控制和管理享元
- 4.客户端,请求获取产品
*/
// 1.抽象享元-便于扩展
type iFlyweight interface {
drive()
}
// 2.具体享元
type flyweight struct {
name string
}
func NewFlyweight(name string) *flyweight {
return &flyweight{
name: name,
}
}
func (r *flyweight) drive() {
fmt.Printf("the car [%s] is driving on the road.\n", r.name)
}
// 3.享元工厂
type flyweightFactory struct {
pool map[string]*flyweight
}
func NewFlyweightFactory() *flyweightFactory {
return &flyweightFactory{
pool: make(map[string]*flyweight),
}
}
func (r *flyweightFactory) Get(name string) *flyweight {
object, ok := r.pool[name]
if !ok {
object = r.schedule(name)
r.pool[name] = object
}
return object
}
// 相当于新建对象
func (r *flyweightFactory) schedule(name string) *flyweight {
fmt.Printf("车行从其他分部调入: [%s]\n", name)
return &flyweight{name}
}
// 用完之后的还车
func (r *flyweightFactory ) Put(object *flyweight) error {
r.pool[object.name] = object
return nil
}
// sync.pool 实现,封装更好,直接get、put即可
type flyweightFactoryV2 struct {
pool sync.Pool
}
func NewFlyweightFactoryV2() *flyweightFactoryV2 {
factory := &flyweightFactoryV2{}
factory.pool.New = func() any {
return new(flyweight)
}
return factory
}
func (r *flyweightFactoryV2) Get(name string) *flyweight {
// 使用时需要断言
object := r.pool.Get().(*flyweight)
return r.warp(name, object)
}
func (r *flyweightFactoryV2) warp(name string, fly *flyweight) *flyweight {
fmt.Printf("正在租出一辆车: [%s]\n", name)
fly.name = name
return fly
}
// 用完之后的还车
func (r *flyweightFactoryV2) Put(object *flyweight) error {
r.pool.Put(object)
return nil
}
// 4.客户端
func main() {
// map实现共享池
fac := NewFlyweightFactory()
// 获取对象
car := fac.Get("BMW")
// 使用对象
car.drive()
// 用完还车
_ = fac.Put(car)
// 再次租车
car1 := fac.Get("BMW")
// 租其他车
car2 := fac.Get("Benz")
// 直接还车
fac.Put(car1)
fac.Put(car2)
/* result:
车行从其他分部调入: [BMW]
the car [BMW] can driving on the road.
车行从其他分部调入: [Benz]
*/
// sync.Pool实现共享池
facV2 := NewFlyweightFactoryV2()
// rent
car3 := facV2.Get("RR")
// use
car3.drive()
// return car
facV2.Put(car3)
}
参考:
标签:享元,return,name,object,golang,flyweight,func,设计模式 From: https://www.cnblogs.com/davis12/p/17451198.html