Go设计模式(17)-享元模式

享元模式主要是为了复用对象,节省内存。使用享元模式需要有两个前提:

  1. 享元对象不可变:当享元模式创建出来后,它的变量和属性不会被修改
  2. 系统中存在大量重复对象:这些重复对象可以使用同一个享元,内存中只存在一份,这样会节省大量空间。当然这也是为什么享元对象不可变的原因,因为有很多引用,变更的话会引起很多问题。

UML类图位置:https://www.processon.com/diagraming/609b375407912943913a4c13

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/17flyweight.go

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 fmt.Sprintf("%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 fmt.Sprintf("%s位置为(%d,%d)", g.piece, g.pos.x, g.pos.y)
}

/**
 * @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) {
    
    
   fmt.Printf("创建房间%d,玩家为%d和%d \n", roomId, u1, u2)
   fmt.Println("初始化棋盘")

   fmt.Printf("玩家%d的棋子为 \n", u1)
   piece := &GamePiece{
    
    
      piece:   factory.GetPiece(0),
      pos:     Pos{
    
    1, 1},
      roomId:  roomId,
      ownerId: u1,
   }
   fmt.Println(piece)

   fmt.Printf("玩家%d的棋子为 \n", u2)
   piece2 := &GamePiece{
    
    
      piece:   factory.GetPiece(1),
      pos:     Pos{
    
    16, 1},
      roomId:  roomId,
      ownerId: u2,
   }
   fmt.Println(piece2)
}
func main() {
    
    
   factory := &PieceFactory{
    
    }
   factory.CreatePieces()
   InitBoard(1, 66, 88, factory)
}

输出:

➜ myproject go run main.go

创建房间1,玩家为66和88

初始化棋盘

玩家66的棋子为

兵,颜色为红,规则为过河前只能一步一步前进,过河后只能一步一步前进或者左右移位置为(1,1)

玩家88的棋子为

兵,颜色为黑,规则为过河前只能一步一步前进,过河后只能一步一步前进或者左右移位置为(16,1)

3总结

享元模式充分说明了抽象的重要性,希望大家能够善用这种模式,优化系统。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

招聘

  1. 字节跳动|内推大放送
  2. 字节跳动|今日头条广州服务端研发工程师内推
  3. 字节跳动|抖音电商急招上海前端开发工程
  4. 字节跳动|抖音电商上海资深服务端开发工程师-交易
  5. 字节跳动|抖音电商武汉服务端(高级)开发工程师
  6. 字节跳动|飞书大客户产品经理内推咯
  7. 字节跳动|抖音电商服务端技术岗位虚位以待
  8. 字节跳动招聘专题

设计模式

  1. Go设计模式(16)-组合模式
  2. Go设计模式(15)-门面模式
  3. Go设计模式(14)-适配器模式
  4. Go设计模式(13)-装饰器模式
  5. Go设计模式(12)-桥接模式
  6. Go设计模式(11)-代理模式
  7. Go设计模式(10)-原型模式
  8. Go设计模式(9)-建造者模式
  9. Go设计模式(8)-抽象工厂
  10. Go设计模式(7)-工厂模式
  11. Go设计模式(6)-单例模式
  12. Go设计模式(5)-类图符号表示法
  13. Go设计模式(4)-代码编写优化
  14. Go设计模式(4)-代码编写
  15. Go设计模式(3)-设计原则
  16. Go设计模式(2)-面向对象分析与设计
  17. Go设计模式(1)-语法

语言

  1. 再也不怕获取不到Gin请求数据了
  2. 一文搞懂pprof
  3. Go工具之generate
  4. Go单例实现方案
  5. Go通道实现原理
  6. Go定时器实现原理
  7. Beego框架使用
  8. Golang源码BUG追查
  9. Gin框架简洁版
  10. Gin源码剖析

架构

  1. 分页复选设计的坑
  2. 支付接入常规问题
  3. 限流实现2
  4. 秒杀系统
  5. 分布式系统与一致性协议
  6. 微服务之服务框架和注册中心
  7. 浅谈微服务
  8. 限流实现1
  9. CDN请求过程详解
  10. 常用缓存技巧
  11. 如何高效对接第三方支付
  12. 算法总结

存储

  1. 一文搞懂MySQL数据库分库分表
  2. MySQL开发规范
  3. Redis实现分布式锁
  4. 事务原子性、一致性、持久性的实现原理
  5. InnoDB锁与事务简析

网络

  1. HTTP2.0基础教程
  2. HTTPS配置实战
  3. HTTPS连接过程
  4. TCP性能优化

工具

  1. GoLand实用技巧
  2. 根据mysql表自动生成go struct
  3. Markdown编辑器推荐-typora

读书笔记

  1. 《毛选》推荐
  2. 原则
  3. 资治通鉴
  4. 敏捷革命
  5. 如何锻炼自己的记忆力
  6. 简单的逻辑学-读后感
  7. 热风-读后感
  8. 论语-读后感
  9. 孙子兵法-读后感

思考

  1. 对过去的一点回顾
  2. 对写博客的一些思考
  3. 晚上打119的经历
  4. 为动员一切力量争取胜利而斗争
  5. 反对自由主义
  6. 实践论
  7. 评价自己的标准
  8. 2020博客总结
  9. 服务端团队假期值班方案
  10. 项目流程管理
  11. 对项目管理的一些看法
  12. 对产品经理的一些思考
  13. 关于程序员职业发展的思考
  14. 关于代码review的思考

猜你喜欢

转载自blog.csdn.net/shida219/article/details/118209359