享元模式

1. 介绍

  • 一种优化模式
  • 适合解决因创建大量类似对象而累积性能问题
  • javaScript 代码可能很快就用光浏览器的内容,通过把大量独立对象转化为少量共享对象,可以降低运行 Web 应用所需的资源数量。
  • 对一连用上几天也不会重新加载的大型应用系统比较有用,对打开时间短的小型网页作用不大
  • JavaScript 对象需要创建 HTML 内容的情况下,享元模式特别有用。

2. 享元的结构

  1. 通过将对象内部状态划分为 内在数据外在数据 来实现,将内在状态相同的所有对象替换为同一个共享对象。
  2. 需要使用工厂,这样可以跟踪已经实例化的各个对象,从而仅当所需对象的内在状态不同于已有对象时才创建一个新对象,对象外在状态被保存在一个管理器对象中,在调用对象的方法时,管理器会把外在状态作为参数传入。
  3. 关于内部状态和外部状态的划分具有一定的随机性和主观性,既要维持每个对象的模块性,又想把尽量多的数据作为外在数据处理
  4. 管理享元对象的外在数据有许多方法
    • 管理器对象
    • 组合模式

3. 例子:

这里以一个车子管理的例子说明享元模式的使用,我们假设车子有下列属性:品牌、型号、出厂时间、车主姓名、车牌号、最近登记时间。

那么可以把相对固定,重复率较高的这些属性:品牌、型号、出厂时间作为内部数据,把其他属性作为外部数据

var Car = function(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
};
Car.prototype = {
getMake: function() {
return this.make;
},
getModel: function() {
return this.model;
},
getYear: function() {
return this.year;
}
} var CarFactory = (function() {
var createdCars = {}; // 【静态私有变量】
return {
// 品牌,型号,出厂时间。这些是相对固定的,所以可以在工厂模式中先存好,这就是内在数据,可以被共享的数据
createCar: function(make, model, year) {
if(createdCars[make + '-' + model + '-' + year]) {
return createdCars[make + '-' + model + '-' + year];
} else {
var car = new Car(make, model, year);
createdCars[make + '-' + model + '-' + year] = car;
return car;
}
},
}
})() // 使用 单体 封装这些数据的管理器对象,用于管理外在数据
var CarRecordManager = (function() {
var carRecordDatabase = {}; // 【静态私有变量】
return {
/**
* [addCarRecord description]
* @param {[type]} make [品牌]
* @param {[type]} model [型号]
* @param {[type]} year [出厂时间]
* @param {[type]} owner [车主姓名]
* @param {[type]} tag [车牌号]
* @param {[type]} renewDate [最近登记时间]
*/
addCarRecord : function(make, model, year, owner, tag, renewDate) { // 这里不是每次都 new 一个新的 car 实例,只有太浪费内存了。如果工厂中已有这种类型的车,则会直接通过引用获取,这样能减少所需对象的数量
var car = CarFactory.createCar(make, model, year); // owner, tag, renewDate 是外在数据,作为外在数据由参赛传入
carRecordDatabase[tag] = {
owner: owner,
renewDate: renewDate,
car: car
}
},
transferOwnership: function(tag, newOwner, newTag, newRenewDate) {
var record = carRecordDatabase[tag];
record.owner = newOwner;
record.tag = newTag;
record.renewDate = newRenewDate
},
renewRegistration: function(tag, newRenewDate) {
carRecordDatabase[tag].renewDate = newRenewDate
},
isRegistrationCurrent: function(tag) {
var today = new Date();
return today.getTime() < Date.parse(carRecordDatabase[tag].renewDate)
},
getCarInfo: function(tag) {
// 切断该输出对象与单体里面的对象的引用关系
return JSON.parse(JSON.stringify(carRecordDatabase[tag]))
}
}
})()

4. 日历例子:

4.1 不使用享元

/**
* 日历年类
* @param {[type]} year [description]
* @param {[type]} parent [description]
*/
var CalendarYear = function(year, parent) {
this.year = year;
this.element = document.createElement('div')
this.element.style.display = 'none'
parent.appendChild(this.element); function isLeapYear(y) {
return (y>0) && !(y % 4) && ((y % 100) || !(y % 400));
} this.months = []; this.numDays = [31, isLeapYear(this.year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30,31, 30, 31];
for(var i=0, len =12; i<len; i++) {
this.months[i] = new CalendarMonth(i, this.numDays[i], this.element);
}
};
CalendarYear.prototype = {
display: function() {
for(var i=0; i<this.months.length; i++) {
this.months[i].display();
}
this.element.style.display = 'block';
}
} /**
* 日历月类
* @param {[type]} monthNum [description]
* @param {[type]} numDays [description]
* @param {[type]} parent [description]
*/
var CalendarMonth = function(monthNum, numDays, parent) {
this.monthNum = monthNum;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element); this.days = [];
for(var i=0; i<numDays; i++) {
this.days[i] = new CalendarDay(i+1, this.element);
}
}
CalendarMonth.prototype = {
display: function() {
for(var i=0, len = this.days.length; i<len; i++) {
this.days[i].display();
}
this.element.style.display = 'block'
}
} /**
* 日历日类
* @param {[type]} data [description]
* @param {[type]} parent [description]
*/
var CalendarDay = function(data, parent) {
this.data = data;
this.element = document.createElement('span');
this.element.style.display = 'none';
parent.appendChild(this.element);
};
CalendarDay.prototype = {
display: function() {
this.element.style.display = '';
this.element.innerHTML = this.data + ' ';
}
} // 这样需要创建很多个 CalenderDay 实例,我们可以使用享元模式来解决这个问题

4.2 使用享元

现在的日历的日类只有一个实例,然后用它来生成所有的 DOM 节点,就不在需要为每个日期的 DOM 节点生成一个实例,可以节省大量内存

/**
* 日历年类
* @param {[type]} year [description]
* @param {[type]} parent [description]
*/
var CalendarYear = function(year, parent) {
this.year = year;
this.element = document.createElement('div')
this.element.style.display = 'none'
parent.appendChild(this.element); function isLeapYear(y) {
return (y>0) && !(y % 4) && ((y % 100) || !(y % 400));
} this.months = []; this.numDays = [31, isLeapYear(this.year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30,31, 30, 31];
for(var i=0, len =12; i<len; i++) {
this.months[i] = new CalendarMonth(i, this.numDays[i], this.element);
}
};
CalendarYear.prototype = {
display: function() {
for(var i=0; i<this.months.length; i++) {
this.months[i].display();
}
this.element.style.display = 'block';
}
} // 使用享元修改日期对象
var CalendarDay = function() {};
CalendarDay.prototype = {
display: function(date, parent) {
var element = document.createElement('span');
parent.appendChild(element);
element.innerHTML = date;
}
} // display方法的参数不再是类的构造函数的参数,所有的CalendarMonth对象都是使用这个实例
// 这里本来应该像上一个例子一样使用工厂模式来攒机该类的实例,不过这个类只需要创建一个实例,所以直接实例化它
var calendarDay = new CalendarDay(); // 改写 CalendarMonth
var CalendarMonth = function(monthNum, numDays, parent) {
this.monthNum = monthNum;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element); this.days = [];
for(var i=0; i<numDays; i++) {
this.days[i] = calendarDay // 这里改写了
}
}
CalendarMonth.prototype = {
display: function() {
for(var i=0, len = this.days.length; i<len; i++) {
this.days[i].display(i+1, this.element); // 这里改写了
}
this.element.style.display = 'block'
}
}

这个例子中不需要像前面汽车例子一样使用一个中心数据库来保存所有从享元对象剥离的数据(owneer,tag, ...)

组合模式一般跟享元莫忽视配合得很完美,因为组合对象通常拥有大量叶对象,而且它还保存着许多可以作为外在数据处理的数据,也对象通常只包含极少的内在数据,所以很容易被转化为共享资源


注意

转载、引用,但请标明作者和原文地址

JavaScript设计模式(9)-享元模式的更多相关文章

  1. 再起航,我的学习笔记之JavaScript设计模式16(享元模式)

    ### 享元模式 **享元模式(Flyweight):** 运用共享技术有效地支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销. 上回我们在组合模式中创建了文章列表类,这次我们要向不同的文 ...

  2. JavaScript设计模式-18.享元模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 深入理解JavaScript系列(37):设计模式之享元模式

    介绍 享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类). 享元模式可以避免大量非常相似类的开销,在程序设 ...

  4. 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)

    原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...

  5. python设计模式之享元模式

    python设计模式之享元模式 由于对象创建的开销,面向对象的系统可能会面临性能问题.性能问题通常在资源受限的嵌入式系统中出现,比如智能手机和平板电脑.大型复杂系统中也可能会出现同样的问题,因为要在其 ...

  6. 【GOF23设计模式】享元模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_享元模式.享元池.内部状态.外部状态.线程池.连接池 package com.test.flyweight; /** * ...

  7. JS常用的设计模式(16)—— 享元模式

    享元模式主要用来减少程序所需的对象个数. 有一个例子, 我们这边的前端同学几乎人手一本<JavaScript权威指南>. 从省钱的角度讲, 大约三本就够了. 放在部门的书柜里, 谁需要看的 ...

  8. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  9. Head First设计模式之享元模式(蝇量模式)

    一.定义 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式. ...

随机推荐

  1. ES6 学习笔记之二 块作用域与闭包

    "闭包是函数和声明该函数的词法环境的组合." 这是MDN上对闭包的定义. <JavaScript高级程序设计>中则是这样定义的:闭包是指有权访问另一个函数作用域中的变量 ...

  2. python中的randint,引入模块

    引入模块的方法: from 模块名 import 方法名 范例: from random import randint#使用randint需要加上这句 while True: answer=randi ...

  3. 【linux之shell脚本】

    一.简介 机器语言汇编语言高级语言 面向过程 C Shell Perl 面向对象 java python c++ 强语言:先编译再执行 java c++ 弱语言:边编译边执行 shell python ...

  4. zabbix 网络模板自动发现端口时,过滤掉某些特定规则的端口,减少item的方法

    1.需求描述        默认情况下Zabbix 模板 中网络接口自动发现接口时,会产生很多item,有时候会有我们不需要的一些接口,这时候需要过滤掉他们.        比如我有一台运行kvm的服 ...

  5. dedecms data文件夹外迁

    出于网站安全考虑,我们一般要把data文件夹迁移到网站根目录外面. dedecms data文件夹外迁方法: 1. 修改首页文件中配置文件路径 打开/index.php,把代码 if(!file_ex ...

  6. C# 快速高效率复制对象的几种方式

    http://www.cnblogs.com/emrys5/p/expression_trans_model.html 这篇较具体. 本文基于上文略加改动,暂记 using Newtonsoft.Js ...

  7. 五子棋的斜对角方向上的规则 -- java编程(简单粗暴版)

    五子棋判断输赢规则 --- 斜对角线方向上 一.左上右下方向上 1.分析图 2.代码 /**判断左上右下方向上是否有连续五颗相同颜色的棋子 * 全部遍历法 */ int loop = 0; boole ...

  8. nyoj888 取石子(九) 反Nimm博弈

    这题就是反Nimm博弈--分析见反Nimm博弈 AC代码 #include <cstdio> #include <cmath> #include <algorithm&g ...

  9. HDU - 3567 IDA* + 曼哈顿距离 + 康托 [kuangbin带你飞]专题二

    这题难度颇大啊,TLE一天了,测试数据组数太多了.双向广度优先搜索不能得到字典序最小的,一直WA. 思路:利用IDA*算法,当前状态到达目标状态的可能最小步数就是曼哈顿距离,用于搜索中的剪枝.下次搜索 ...

  10. 科普 TLS 1.3—新特性与开启方式

    TLS 1.3 协议针对安全强化及效率提升等方面进行了大量修改,相继推出 20 多个草案版本,即将完成最终的标准化.标准完成后,OpenSSL 组织将推出 OpenSSL 1.1.1 版本,对 TLS ...