享元模式

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. BZOJ 3884: 上帝与集合的正确用法 [欧拉降幂]

    PoPoQQQ大爷太神了 只要用欧拉定理递归下去就好了.... 然而还是有些细节没考虑好: $(P,2) \neq 1$时分解$P=2^k*q$的形式,然后变成$2^k(2^{(2^{2^{...}} ...

  2. win2012 配置wamp的若干错误

    转自群友 VC2015.VC14 在 Windows 2012 R2 安装失败,0x80240017 - 未指定的错误,解决办法据朋友反应VC2013一样存在这个问题--查资料说是没有安装 KB299 ...

  3. jQuery源码研究——解决命名冲突

    在项目中难免不去使用多个插件,如此一来这些插件就有可能出现一样的名称,当出现同名变量时后一个将会覆盖上一个,这样的话我们就无法同时使用多个插件了. 当遇到这种情况我们可以手动去修改插件源码把它的名字改 ...

  4. bzoj2876 [NOI2012]骑行川藏(拉格朗日乘数法)

    题目描述 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因此在每天的骑行 ...

  5. windows FileZilla Server 开启FTP over TLS

    FileZilla Server官方下载地址: https://filezilla-project.org/download.php?type=server FileZilla Server 开启FT ...

  6. ASP.NET Core的身份认证框架IdentityServer4--(4)添加第三方快捷登录

    添加对外部认证的支持 接下来我们将添加对外部认证的支持.这非常简单,因为你真正需要的是一个兼容ASP.NET Core的认证处理程序. ASP.NET Core本身也支持Google,Facebook ...

  7. centos/linux下的安装Nginx

    1.安装gcc编译器 先查看gcc编译器是否安装 在shell控制台输入gcc-v 如果没有安装请看下一步 使用yuma安装gcc yum intsall gcc 看到如下视图则说明安装成功 2.安装 ...

  8. 剑指offer第一天

    15.反转链表 输入一个链表,反转链表后,输出链表的所有元素. 解法一:(使用栈) /* public class ListNode { int val; ListNode next = null; ...

  9. 编写React组件的最佳实践

    此文翻译自这里. 当我刚开始写React的时候,我看过很多写组件的方法.一百篇教程就有一百种写法.虽然React本身已经成熟了,但是如何使用它似乎还没有一个"正确"的方法.所以我( ...

  10. Spring MVC处理(下周完善)

    http://www.cnblogs.com/xiepeixing/p/4244574.html http://blog.csdn.net/kobejayandy/article/details/12 ...