享元模式
是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
模型说明
- 享元模式只是一种优化。在应用该模式之前,你要确定程序中存在与大量类似对象同时占用内存相关的内存消耗问题,并且确保该问题无法使用其他更好的方式来解决。
- 享元(Flyweight)类包含原始对象中部分能在多个对象中共享的状态。同一享元对象可在许多不同情景中使用。享元中存储的状态被称为“内在状态”。传递给享元方法的状态被称为“外在状态”。
- 情景(Context)类包含原始对象中各不相同的外在状态。情景与享元对象组合在一起就能表示原始对象的全部状态。
- 通常情况下,原始对象的行为会保留在享元类中。因此调用享元方法必须提供部分外在状态作为参数。但你也可将行为移动到情景类中,然后将连入的享元作为单纯的数据对象。
- 客户端(Client)负责计算或存储享元的外在状态。在客户端看来,享元是一种可在运行时进行配置的模板对象,具体的配置方式为向其方法中传入一些情景数据参数。
- 享元工厂(Flyweight Factory)会对已有享元的缓存池进行管理。有了工厂后,客户端就无需直接创建享元,它们只需调用工厂并向其传递目标享元的一些内在状态即可。工厂会根据参数在之前已创建的享元中进行查找,如果找到满足条件的享元就将其返回;如果没有找到就根据参数新建享元。
优缺点
1.优点
如果程序中有很多相似对象, 那么你将可以节省大量内存。
2.缺点
你可能需要牺牲执行速度来换取内存,因为他人每次调用享元方法时都需要重新计算部分情景数据。
代码会变得更加复杂。团队中的新成员总是会问:“为什么要像这样拆分一个实体的状态?”。
使用场景
- 仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式。
参考代码
假设目前有 5 名恐怖分子和 5 名反恐精英, 一共是 10 名玩家。 那么关于服装, 我们就有两个选项了。
- 10 个玩家对象各自创建不同的服装对象, 并将其嵌入。 总共会创建 10 个服装对象。
- 我们创建两个服装对象:
- 单一恐怖分子服装对象: 其将在 5 名恐怖分子之间共享。
- 单一反恐精英服装对象: 其将在 5 名反恐精英之间共享。
状态划分:
- 内部状态:内部状态的服装可在多个恐怖分子和反恐精英对象间共享。
- 外部状态:玩家位置和玩家所使用的武器就是外部状态, 因为其在每个对象中都是不同的。
dressFactory.go: 享元工厂
package mainimport "fmt"const (//TerroristDressType terrorist dress typeTerroristDressType = "tDress"//CounterTerrroristDressType terrorist dress typeCounterTerrroristDressType = "ctDress")var (dressFactorySingleInstance = &DressFactory{dressMap: make(map[string]Dress),})type DressFactory struct {dressMap map[string]Dress}func (d *DressFactory) getDressByType(dressType string) (Dress, error) {if d.dressMap[dressType] != nil {return d.dressMap[dressType], nil}if dressType == TerroristDressType {d.dressMap[dressType] = newTerroristDress()return d.dressMap[dressType], nil}if dressType == CounterTerrroristDressType {d.dressMap[dressType] = newCounterTerroristDress()return d.dressMap[dressType], nil}return nil, fmt.Errorf("Wrong dress type passed")}func getDressFactorySingleInstance() *DressFactory {return dressFactorySingleInstance}
dress.go: 享元接口以及具体享元实现类
package maintype Dress interface {getColor() string}type TerroristDress struct {color string}func (t *TerroristDress) getColor() string {return t.color}func newTerroristDress() *TerroristDress {return &TerroristDress{color: "red"}}type CounterTerroristDress struct {color string}func (c *CounterTerroristDress) getColor() string {return c.color}func newCounterTerroristDress() *CounterTerroristDress {return &CounterTerroristDress{color: "green"}}
player.go: 背景
package maintype Player struct {dressDressplayerType stringlatintlong int}func newPlayer(playerType, dressType string) *Player {dress, _ := getDressFactorySingleInstance().getDressByType(dressType)return &Player{playerType: playerType,dress:dress,}}func (p *Player) newLocation(lat, long int) {p.lat = latp.long = long}
game.go: 客户端代码
package maintype game struct {terrorists[]*PlayercounterTerrorists []*Player}func newGame() *game {return &game{terrorists:make([]*Player, 1),counterTerrorists: make([]*Player, 1),}}func (c *game) addTerrorist(dressType string) {player := newPlayer("T", dressType)c.terrorists = append(c.terrorists, player)return}func (c *game) addCounterTerrorist(dressType string) {player := newPlayer("CT", dressType)c.counterTerrorists = append(c.counterTerrorists, player)return}
main.go: 客户端代码
package mainimport "fmt"func main() {game := newGame()//Add Terroristgame.addTerrorist(TerroristDressType)game.addTerrorist(TerroristDressType)game.addTerrorist(TerroristDressType)game.addTerrorist(TerroristDressType)//Add CounterTerroristgame.addCounterTerrorist(CounterTerrroristDressType)game.addCounterTerrorist(CounterTerrroristDressType)game.addCounterTerrorist(CounterTerrroristDressType)dressFactoryInstance := getDressFactorySingleInstance()for dressType, dress := range dressFactoryInstance.dressMap {fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())}}
output:
DressColorType: tDressDressColor: redDressColorType: ctDressDressColor: green