享元模式

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. 用js脚本一键下载网页所有图片

    年前这两天稍微闲一点了,琢磨了一点js脚本,功能是把当前网页页面上的所有图片一次性保存到本地,免得每次都要对图片右键保存. 测试环境:Chrome开发者模式下(启动Chrome,按F12即可) 测试网 ...

  2. 谨慎升级到HTTPS

    我们的业务主要为两块,首先是h5商城,在商城里面会有很多很多的运营活动,点击进去是在后台配置的各种H5活动链接.而H5商城和运营活动是两个业务,两者的联系就是要在后台系统将运营活动的链接配置到商城中. ...

  3. wangeditor Demo

    <html> <head> <!--在这里字符集的设定很重要,如果设定不当将会出现乱码--> <meta charset="UTF-8"& ...

  4. 图论算法-最小费用最大流模板【EK;Dinic】

    图论算法-最小费用最大流模板[EK;Dinic] EK模板 const int inf=1000000000; int n,m,s,t; struct node{int v,w,c;}; vector ...

  5. linux 下yum使用技巧

    本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 经常会遇上一些linux系统允许你上外网,而一些是不允许的,这时我们 ...

  6. C/C++语言的特点

    一.支持数据封装和数据隐藏 在C++中,类是支持数据封装的工具,对象则是数据封装的实现.C++通过建立用户定义类支持数据封装和数据隐藏. 在面向对象的程序设计中,将数据和对该数据进行合法操作的函数封装 ...

  7. XP环境下的网络证书问题

    项目过程中,由于是收银系统需要从服务器获取支付二维码,会产生SSL连接的问题,在win7.win10上都没有问题,放到WIN XP上出现了The underlying connection was c ...

  8. Egret学习笔记 (Egret打飞机-5.实现子弹对象)

    上一章把飞机添加到屏幕上,但是飞机要发射子弹对吧?那么这一章我们就来实现一下发射子弹,并实现一个简单的子弹对象池 先来捋一捋思路 1.创建一个子弹对象 2.然后添加一个bitmap,显示子弹贴图 3. ...

  9. MYSQL,触发器,实现两个表共用ID不重复

    前后台没有分开,为了区分前后台用户,所以分表,但是ID不能重复,因为关联了权限表. 这里实现后台用户表使用奇数ID 前台用户表使用偶数ID MYSQL 没有sequence SET @@auto_in ...

  10. python爬虫框架scrapy问题的解决

    2016-09-24:今天的弄了一天的scrapy的环境的配置的,linux很多的学过的事情都忘记啦.理论和实践的结合还是非常的重要的,不光要学会思考,更要学会总结纪录.还要多多回忆的和复习.学习了不 ...