结构型 享元模式

为什么需要享元模式

享元模式就是共享类单元,当一个系统存在大量的重复对象,而这些对象是immutable(不可变)对象的时候,我们可以想办法把对象设计成享元,内存里面只保存一份对象,当其他代码调用的时候只有一个对象,起到节省内存的作用。所以要求创建对象以后不能再被修改了。

一个通常用的例子是棋牌游戏,棋子的类型在不同的棋局中是一样的,我们没有必要给不同棋局的棋子都单独创建类的对象。

Java中常用的Integer,Double和String中其实也是应用了享元模式的设计。

实现

实现起来一般是要写两个类,一个共享元类(数据),一个共享元创建工厂类(用于创建和获取数据)。

使用的时候统一从factory的接口获取对象创建内容。其实有点像原型模式个人感觉。

go.mod

module flyweight

go 1.13

flyweight.go

package flyweight

var factory *dressFlyweightFactory

func init() {
	// hungry man init
	factory = &dressFlyweightFactory{
		cached: make(map[string]*DressFlyweight),
	}
}

// DressFlyweightFactory can produce and obtain shared object.
type dressFlyweightFactory struct {
	cached map[string]*DressFlyweight
}

func Get(brand string) *DressFlyweight {
	var (
		dress *DressFlyweight
		exist bool
	)
	if dress, exist = factory.cached[brand]; !exist {
		dress = NewDressFlyweight(brand)
		factory.cached[brand] = dress
	}

	return dress
}

// DressFlyweight defines shared object class.
type DressFlyweight struct {
	brand string // unexported such that not changeable
}

func NewDressFlyweight(brand string) *DressFlyweight {
	return &DressFlyweight{
		brand: brand,
	}
}

// Folowing are usages example

type DressViewer struct {
	dress *DressFlyweight
}

func NewShopViewer(brand string) *DressViewer {
	return &DressViewer{
		dress: Get(brand),
	}
}

func (d *DressViewer) Equal(other *DressViewer) bool {
	return d.dress == other.dress
}

flyweight_test.go

package flyweight_test

import (
	"flyweight"
	"testing"
)

func TestFlyweight(t *testing.T) {
	viewer1 := flyweight.NewShopViewer("GoGoDoge")
	viewer2 := flyweight.NewShopViewer("GoGoDoge")
	if !viewer1.Equal(viewer2) {
		t.Fatal("Not the same object!")
	}
}

执行: go test flyweight -v

Previous
Next