设计模式 享元模式flyweight
本文主要介绍享元模式基础概念、实现原理和通过一个棋牌游戏例子简述实现过程。
享元模式
所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。 具体来讲,当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。
享元的实现并不复杂,通过工厂模式,通过一个 Map 来缓存已经创建过的享元对象,来达到复用的目的。 其中:
- Flyweight : 享元类包含原始对象中部分能在多个对象中共享的状态。
- FlyweightFactory:享元工厂,会对已有享元的缓存池进行管理。
接着,我们通过一个棋牌游戏的案例来演示。
案例-棋牌游戏
这里以象棋为例,游戏大厅中有N个房间,那么每个房间会有一个棋局对象,和X个(将、相、士、炮)棋子对象。以下是go的具体实现flyweight.go
package design_mode
type ChessPiece struct {
Name string
Color string
PositionX int
PositionY int
}
type ChessBoard struct {
Cards map[int]*ChessPiece
}
func NewChessBoard() *ChessBoard {
return &ChessBoard{
Cards: map[int]*ChessPiece{
1: {
Name: "車",
Color: "紅",
PositionX: 1,
PositionY: 11,
},
2: {
Name: "馬",
Color: "黑",
PositionX: 2,
PositionY: 2,
},
},
// 其他棋子
}
}
开N个房间flyweight_test.go
package design_mode
import (
"testing"
)
func TestNewChessBoard(t *testing.T) {
game1 := NewChessBoard()
game2 := NewChessBoard()
t.Log(game1.Cards[1])
t.Log(game2.Cards[1])
t.Log(game1.Cards[1] == game2.Cards[1])
}
OutPut:
=== RUN TestNewChessBoard
flyweight_test.go:15: &{車 紅 1 11}
flyweight_test.go:16: &{車 紅 1 11}
flyweight_test.go:17: false
--- PASS: TestNewChessBoard (0.00s)
例中,每创建一个棋局就需要初始化对应的棋子,如果游戏大厅有百万人同时在线 ,那保存这么多棋局对象就会消耗大量的内存。
使用享元模式重构
如何对上例进行优化呢,从例子中,每个大厅存在大量重复对象棋牌,利用享元模式把棋牌设计成共享的(map)详细如下。
package design_mode
import "fmt"
var chessPieceUnit = map[int]*ChessPiece{
1: {
Name: "車",
Color: "紅",
PositionX: 1,
PositionY: 11,
},
2: {
Name: "馬",
Color: "黑",
PositionX: 2,
PositionY: 2,
},
// 其他棋子
}
func NewChessPieceUnitFactory() *ChessBoard {
board := &ChessBoard{Cards: map[int]*ChessPiece{}}
for id := range chessPieceUnit {
board.Cards[id] = chessPieceUnit[id]
}
return board
}
output:
=== RUN TestNewChessPieceUnitFactory
flyweight2_test.go:13: &{車 紅 1 11}
flyweight2_test.go:14: &{車 紅 1 11}
flyweight2_test.go:15: true
--- PASS: TestNewChessPieceUnitFactory (0.00s)
例中,通过一个ChessPieceUnit来缓存一个ChessBoard棋局的30+个ChessPiece棋子对象。原本如果是1万个ChessBoard棋局,需要创建【1万乘30】约30万个棋子对象。现在通过共享同一个ChessPieceUnit,那么需要创建约【30】个对象,大大节省了内存。