共享元模式主要是为了重用对象和节省内存。使用共享模式需要两个前提。
共享元对象不会更改。创建共享元模式后,不会修改其变量和属性。系统中有很多重复的对象。这些重复对象可以使用相同的共享元素,内存中只有一个,从而节省了大量空间。
当然这也是为什么享元对象不可变的原因,因为有很多引用,变更的话会引起很多问题。UML类图位置:
本文代码链接为:
1.定义
1.1享元模式
享元模式:运用共享技术有效的支持大量细粒度的对象。
UML:
1.2分析
享元模式主要是把系统中共同的、不变的对象抽象出来,达到共用一份的效果。
抽象出的对象接口为Flyweight,ConcreteFlyweight为实际被共享的对象。UnsharedConcreteFlyweight是否存在,主要看是否有对象是无需共享的。
享元模式里有工厂FlyweightFactory,主要是因为系统中需要的享元结构虽然确定了,但是享元的属性不同,所以需要管理多个对象,此处使用了工厂模式。关于工厂模式可以参看这篇文章Go设计模式(7)-工厂模式。
2.使用场景
享元模式还是有很多具体使用场景的,如很多联网类棋牌游戏。假设有100w场象棋游戏在同时进行,不使用享元模式的话,系统需要维护32*100w个象棋对象。但象棋的文案、颜色、规则是不变的,变的只是持有人和位置。所以将32个象棋对象抽象出来,当做享元,可以极大的节省空间,而且不会带来成本提升。
享元模式与其说是一种设计模式,不如说是一种设计理念,主要讲的是抽象的能力,将相同模块提取出来,供不同模块使用。从这个维度来说,代码重构中提取相同功能、单例模式等,何尝不是另一种享元。
3.代码实现
写一下象棋游戏中对于象棋的管理吧。
package main
import "fmt"
/**
* @Author: Jason Pang
* @Description: 棋子类,有文案、颜色、规则,这三种不变属性
*/
type Piece struct {
text string
color string
rule string
}
/**
* @Author: Jason Pang
* @Description: 棋子信息说明
* @receiver p
* @return string
*/
func (p *Piece) String() string {
return ("%s,颜色为%s,规则为%s", p.text, p.color, p.rule)
}
/**
* @Author: Jason Pang
* @Description: 棋子在棋盘位置
*/
type Pos struct {
x int64
y int64
}
/**
* @Author: Jason Pang
* @Description: 游戏中的棋子
*/
type GamePiece struct {
piece *Piece //棋子指针
pos Pos //棋子位置
ownerId int64 //玩家ID
roomId int64 //房间ID
}
/**
* @Author: Jason Pang
* @Description: 游戏中的棋子说明
* @receiver g
* @return string
*/
func (g *GamePiece) String() string {
return ("%s位置为(%d,%d)", g.piece, g., g.)
}
/**
* @Author: Jason Pang
* @Description: 棋子工厂,包含32颗棋子信息
*/
type PieceFactory struct {
pieces []*Piece
}
/**
* @Author: Jason Pang
* @Description: 创建棋子。棋子的信息都是不变的
* @receiver f
*/
func (f *PieceFactory) CreatePieces() {
f.pieces = make([]*Piece, 32)
f.pieces[0] = &Piece{
text: "兵",
color: "红",
rule: "过河前只能一步一步前进,过河后只能一步一步前进或者左右移",
}
f.pieces[1] = &Piece{
text: "兵",
color: "黑",
rule: "过河前只能一步一步前进,过河后只能一步一步前进或者左右移",
}
//todo 创建其它棋子。此处可以使用配置文件创建,能方便一些。系统中可以设置一个规则引擎,控制棋子运动。
}
/**
* @Author: Jason Pang
* @Description: 获取棋子信息
* @receiver f
* @param id
* @return *Piece
*/
func (f *PieceFactory) GetPiece(id int64) *Piece {
return f.pieces[id]
}
/**
* @Author: Jason Pang
* @Description: 初始化棋盘
* @param roomId
* @param u1
* @param u2
*/
func InitBoard(roomId int64, u1 int64, u2 int64, factory *PieceFactory) {
("创建房间%d,玩家为%d和%d \n", roomId, u1, u2)
("初始化棋盘")
("玩家%d的棋子为 \n", u1)
piece := &GamePiece{
piece: (0),
pos: Pos{1, 1},
roomId: roomId,
ownerId: u1,
}
(piece)
("玩家%d的棋子为 \n", u2)
piece2 := &GamePiece{
piece: (1),
pos: Pos{16, 1},
roomId: roomId,
ownerId: u2,
}
(piece2)
}
func main() {
factory := &PieceFactory{}
()
InitBoard(1, 66, 88, factory)
}
输出:
➜ myproject go run main.go
创建房间1,玩家为66和88
初始化棋盘
玩家66的棋子为
兵,颜色为红,规则为过河前只能一步一步前进,过河后只能一步一步前进或者左右移位置为(1,1)
玩家88的棋子为
兵,颜色为黑,规则为过河前只能一步一步前进,过河后只能一步一步前进或者左右移位置为(16,1)
3总结
享元模式充分说明了抽象的重要性,希望大家能够善用这种模式,优化系统。
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:
往期文章回顾:
招聘
- 字节跳动|内推大放送
- 字节跳动|今日头条广州服务端研发工程师内推
- 字节跳动|抖音电商急招上海前端开发工程
- 字节跳动|抖音电商上海资深服务端开发工程师-交易
- 字节跳动|抖音电商武汉服务端(高级)开发工程师
- 字节跳动|飞书大客户产品经理内推咯
- 字节跳动|抖音电商服务端技术岗位虚位以待
- 字节跳动招聘专题
设计模式
- Go设计模式(16)-组合模式
- Go设计模式(15)-门面模式
- Go设计模式(14)-适配器模式
- Go设计模式(13)-装饰器模式
- Go设计模式(12)-桥接模式
- Go设计模式(11)-代理模式
- Go设计模式(10)-原型模式
- Go设计模式(9)-建造者模式
- Go设计模式(8)-抽象工厂
- Go设计模式(7)-工厂模式
- Go设计模式(6)-单例模式
- Go设计模式(5)-类图符号表示法
- Go设计模式(4)-代码编写优化
- Go设计模式(4)-代码编写
- Go设计模式(3)-设计原则
- Go设计模式(2)-面向对象分析与设计
- Go设计模式(1)-语法
语言
- 再也不怕获取不到Gin请求数据了
- 一文搞懂pprof
- Go工具之generate
- Go单例实现方案
- Go通道实现原理
- Go定时器实现原理
- Beego框架使用
- Golang源码BUG追查
- Gin框架简洁版
- Gin源码剖析
架构
- 分页复选设计的坑
- 支付接入常规问题
- 限流实现2
- 秒杀系统
- 分布式系统与一致性协议
- 微服务之服务框架和注册中心
- 浅谈微服务
- 限流实现1
- CDN请求过程详解
- 常用缓存技巧
- 如何高效对接第三方支付
- 算法总结
存储
- 一文搞懂MySQL数据库分库分表
- MySQL开发规范
- Redis实现分布式锁
- 事务原子性、一致性、持久性的实现原理
- InnoDB锁与事务简析
网络
- HTTP2.0基础教程
- HTTPS配置实战
- HTTPS连接过程
- TCP性能优化
工具
- GoLand实用技巧
- 根据mysql表自动生成go struct
- Markdown编辑器推荐-typora
读书笔记
- 《毛选》推荐
- 原则
- 资治通鉴
- 敏捷革命
- 如何锻炼自己的记忆力
- 简单的逻辑学-读后感
- 热风-读后感
- 论语-读后感
- 孙子兵法-读后感
思考
- 对过去的一点回顾
- 对写博客的一些思考
- 晚上打119的经历
- 为动员一切力量争取胜利而斗争
- 反对自由主义
- 实践论
- 评价自己的标准
- 2020博客总结
- 服务端团队假期值班方案
- 项目流程管理
- 对项目管理的一些看法
- 对产品经理的一些思考
- 关于程序员职业发展的思考
- 关于代码review的思考