JavaScript设计模式-享元模式

  • 概念
  • 例子
  • 内部状态与外部状态
  • 享元模式的通用结构
  • 例子
  • 总结
  • github仓库地址:点击 [设计模式例子](https://github.com/fanhualuoye/design) 查看

概念

  1. 享元(flyweight)模式是一种用于性能优化的模式,“fly”在这里是苍蝇的意思,意为蝇量
    级。享元模式的核心是运用共享技术来有效支持大量细粒度的对象。

  2. 如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就非常有用了。在
    JavaScript 中,浏览器特别是移动端的浏览器分配的内存并不算多,如何节省内存就成了一件非
    常有意义的事情。

例子

// 有一家模特儿公司(工厂)const modelFactory = function (type, name) {this.type = typethis.name = name}// 给穿上指定衣服的模特儿拍照,modelFactory.prototype.takePhoto = function () {console.log('给type= ' + this.type + ' name=' + this.name + '拍照')}// 有50件不同的西装,需要拍50张照片for (let i = 1; i <= 50; i++) {// 给每件西装都请一位模特儿,一共请了50个const suitModel = new modelFactory('西装', i)suitModel.takePhoto()}// 有50件不同的羽绒服,需要50张照片for (let j = 1; j <= 50; j++) {// 给每件羽绒服都请一位模特儿,一共请了50个const jacketModel = new modelFactory('羽绒服', j)jacketModel.takePhoto()}// 上面new了100个对象,这样是很占内存(资源),我们可以用享元模式优化//我们只需要两位模特就行了,一个西装模特,一个羽绒服模特const modelFactory = function (type) {this.type = type}modelFactory.prototype.takePhoto = function () {console.log('给type= ' + this.type + ' name=' + this.name + '拍照')}// 分别创建一个西装模特对象和一个羽绒服模特对象:const suitModel = new modelFactory('西装')const jacketModel = new modelFactory('羽绒服')// 给模特依次穿上西装,并进行拍照:for (let i = 1; i <= 50; i++) {suitModel.name = isuitModel.takePhoto()}// 给模特依次穿上羽绒服,并进行拍照:for (let j = 1; j <= 50; j++) {jacketModel.name = jjacketModel.takePhoto()}// 这个西装模特对象和一个羽绒服模特对象就是我们的享元了// 以上就是是享元模式的雏形

内部状态与外部状态

  • 享元模式要求将对象的属性划分为内部状态与外部 状态(状态在这里通常指属性)。享元模式的目标是尽量减少共享对象的数量,关于如何划分内 部状态和外部状态,下面的几条经验提供了一些指引。
  1. 内部状态存储于对象内部。
  2. 内部状态可以被一些对象共享。
  3. 内部状态独立于具体的场景,通常不会改变。
  4. 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。
  • 这样一来,我们便可以把所有内部状态相同的对象都指定为同一个共享的对象。而外部状态可以从对象身上剥离出来,并储存在外部。

  • 剥离了外部状态的对象成为共享对象,外部状态在必要时被传入共享对象来组装成一个完整的对象。虽然组装外部状态成为一个完整对象的过程需要花费一定的时间,但却可以大大减少系统中的对象数量,相比之下,这点时间或许是微不足道的。因此,享元模式是一种用时间换空间的优化模式。

  • 在上面的例子中,type是内部状态,name是外部状态,通过区分这两种状态,大大减少了系 统中的对象数量。

  • 总结:享元模式共同的属性是内部属性,在创建对象的时候决定;不同的属性是外部属性,在对象创建完后改变。

享元模式的通用结构

  • 示例初步展示了享元模式的威力,但这还不是一个完整的享元模式,在这个例子中 还存在以下两个问题。
  1. 我们通过构造函数显式new出了两个model对象,在其他系统中,也许并不是一开始就需要所有的共享对象。(不用就不创建)

  2. 给 model 对象手动设置了name外部状态,在更复杂的系统中,这不是一个最好的方式,因为外部状态可能会相当复杂,它们与共享对象的联系会变得困难。

  • 我们通过一个对象工厂来解决第一个问题,只有当某种共享对象被真正需要时,它才从工厂中被创建出来。对于第二个问题,可以用一个管理器来记录对象相关的外部状态,使这些外部状态通过某个钩子和共享对象联系起来。

例子

        // 例子// 工厂进行对象实例化const UploadFactory = (function () {const createdFlyWeightObjs = {}return {create: function (uploadType) {if (createdFlyWeightObjs [uploadType]) {return createdFlyWeightObjs [uploadType]}return createdFlyWeightObjs [uploadType] = new Upload(uploadType)}}})()// 管理器封装外部状态const uploadManager = (function () {// 定义上传文件的集合const uploadDatabase = {}return {// 上传文件处理逻辑add: function (id, uploadType, fileName, fileSize) {// 利用UploadFactory.create工厂进行对象实例化,得到实例,同时,uploadType相同的,只会有一个实例// uploadType就是内部状态了,其他属性就是外部状态了const flyWeightObj = UploadFactory.create(uploadType)const dom = document.createElement('div')dom.innerHTML ='<span>文件名称:' + fileName + ', 文件大小: ' + fileSize + '</span>' +'<button class="delFile">删除</button>'dom.querySelector('.delFile').onclick = function () {flyWeightObj.delFile(id)}document.body.appendChild(dom)// 向上传文件的集合里添加数据uploadDatabase[id] = {fileName: fileName,fileSize: fileSize,dom: dom}return flyWeightObj},// 用来获取当前id的文件对象setExternalState: function (id, flyWeightObj) {const uploadData = uploadDatabase[id]for (const i in uploadData) {flyWeightObj[i] = uploadData[i]}}}})()let id = 0// 开始上传const startUpload = function (uploadType, files) { // uploadType 区分是控件还是 flashfor (let i = 0; i < files.length; i++) {const file = files[i]uploadManager.add(++id, uploadType, file.fileName, file.fileSize)}}// 上传的对象const Upload = function (uploadType) {this.uploadType = uploadType}Upload.prototype.delFile = function (id) {// 这里把id和this传进setExternalState方法,该方法会根据id找到上传的文件对象,并把对象属性赋值给this// 那么下面的this.dom就可以找到相应的文件dom了uploadManager.setExternalState(id, this) // (1)if (this.fileSize < 3000) {return this.dom.parentNode.removeChild(this.dom)}if (window.confirm('确定要删除该文件吗? ' + this.fileName)) {return this.dom.parentNode.removeChild(this.dom)}}// 接下来分别创建 3 个插件上传对象和 3 个 Flash 上传对象:startUpload('plugin', [{fileName: '1.txt',fileSize: 1000},{fileName: '2.html',fileSize: 3000},{fileName: '3.txt',fileSize: 5000}])startUpload('flash', [{fileName: '4.txt',fileSize: 1000},{fileName: '5.html',fileSize: 3000},{fileName: '6.txt',fileSize: 5000}])

总结

  • 享元模式是为解决性能问题而生的模式,这跟大部分模式的诞生原因都不一样。在一个存在大量相似对象的系统中,享元模式可以很好地解决大量对象带来的性能问题。
  • 优缺点:享元模式是一种用时间换空间的优化模式。

github仓库地址:点击 设计模式例子 查看

JavaScript设计模式-享元模式相关推荐

  1. Python设计模式-享元模式

    Python设计模式-享元模式 基于Python3.5.2,代码如下 #coding:utf-8class Coffee:name = ""price = 0def __init_ ...

  2. 10-Python与设计模式--享元模式

    10-Python与设计模式--享元模式 一.网上咖啡选购平台 假设有一个网上咖啡选购平台,客户可以在该平台上下订单订购咖啡,平台会根据用户位置进行线下配送.假设其咖啡对象构造如下: class Co ...

  3. 设计模式--享元模式实现C++

    /********************************* *设计模式--享元模式实现 *C++语言 *Author:WangYong *Blog:http://www.cnblogs.co ...

  4. 【设计模式】Java设计模式 - 享元模式

    [设计模式]Java设计模式 - 享元模式

  5. Unity设计模式——享元模式(附代码)

    Unity设计模式--享元模式(附源码) 享元Flyweight模式是什么 享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的 ...

  6. 第二十二章 Caché 设计模式 享元模式

    文章目录 第二十二章 Caché 设计模式 享元模式 定义 优点 使用场景 结构图 描述 完整示例 实体类 抽象享元角色 实现享元角色 享元工厂 调用 思考 第二十二章 Caché 设计模式 享元模式 ...

  7. JAVA 设计模式 享元模式

    用途 享元模式 (Flyweight) 运用共享技术有效地支持大量细粒度的对象. 享元模式是一种结构型模式. 结构 图-享元模式结构图 Flyweight : 它是所有具体享元类的超类或接口,通过这个 ...

  8. java设计模式---享元模式

    Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己 对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广 泛,遵循一定的编程模式,才能使自己的 ...

  9. PHP设计模式——享元模式

    声明:本系列博客参考资料<大话设计模式>,作者程杰. 享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的 ...

最新文章

  1. 冠军揭晓!京东Alpha开发者大赛Pick谁上了C位
  2. 学好机器学习必备这12条经验 !(附资料)
  3. 现代谱估计:多窗口谱相关性
  4. C# Datagridview完整攻略
  5. python之路2.0_Python之路【第二十一篇】:JS基础
  6. LeetCode 1366. 通过投票对团队排名(自定义排序)
  7. matplotlib scale 刻度
  8. 一步一步SharePoint 2007之二十二:完美解决实现Form认证后无法再用SharePoint Designer编辑网站的问题...
  9. 对字符串进行加密解密
  10. 麦亡9什么时候能装鸿蒙系统,距断供不到10天 麒麟9000即将绝版 华为大招来了:不止鸿蒙2.0...
  11. java svg to png_如何用Image Magick将SVG转换为PNG?
  12. Android 的singleTask和singleInstance的一点思考
  13. 管理者必须要精通的六项管理技能
  14. 【Arduino】基础传感器使用
  15. js自执行函数(function(){})()前加个分号是什么意思?
  16. 罗马帝国开创了辉煌的人类文明,但他们的数字表示法的确有些繁琐,尤其在表示大数的时候,现在看起来简直不能忍受,所以在现代很少使用了。之所以这样,不是因为发明表示法的人的智力的问题,而是因为一个宗教的原因
  17. 面向需求编程才是常态,聊聊我的经历
  18. 数据库设计(理论实例)
  19. 绘画教程:女生伤心眼泪、哭泣表情的画法技巧
  20. 【深度学习】【积分梯度】深度网络的公理归因(Axiomatic Attribution for Deep Networks)

热门文章

  1. NAT模式下的虚拟机网络配置
  2. camera前置摄像头左右镜像问题
  3. admui3字体无法删除_迄今为止最满意的手写字体丨杨任东竹石体
  4. 通信工具箱之awgn
  5. SNTP原理讲解 客户端 C语言实现
  6. Android的WakeLock机制
  7. 【使用教程】nbr服务器端代码的编译和运行
  8. 『WX运动』想上封面?就你那点步数怎么能行?点进来!老铁,借一步说话
  9. 神舟的主板就是“牛”!
  10. C++程序设计课程师生互动(2012年春第10周)