享元模式

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. 2018/1/15 JAVA多线程相关

    本文不说synchronized相关,它就是JAVA的一个保留关键字,jdk自己实现了它,但说真的,可应用场景真的少,相比lock接口,它还是被淘汰好吧; 首先,说说lock接口,lock接口是一个工 ...

  2. 通过Log4net来配置我们需要的日志文件格式

      我们先来看看配置写入txt文件是如何 的,当然不止可以配置txt格式还有其它格式. <?xml version="1.0" encoding="utf-8&qu ...

  3. [HAOI2009]毛毛虫

    题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格 ...

  4. Chrome浏览器的自动安装下载工具

    链接 https://www.google.com/chrome/browser/desktop/index.html?brand=CHWL&utm_campaign=en&utm_s ...

  5. Git hook实现自动部署

    Git Hook 是 Git 提供的一个钩子,能被特定的事件触发后调用.其实,更通俗的讲,当你设置了 Git Hook 后,只要你的远程仓库收到一次 push 之后,Git Hook 就能帮你执行一次 ...

  6. Java经典编程题50道之四

    将一个正整数分解质因数.例如:输入90,打印出90=2*3*3*5. public class Example04 {    public static void main(String[] args ...

  7. 换行符\r \n LF

    前言:在对照PSR-2规范时,看到文件结尾必须要以Unix LF(linefeed)结尾,不懂查~ 来源于:http://www.cppblog.com/prayer/archive/2009/08/ ...

  8. Visual Studio 2017 Enterprise 发布 15.4 版本,离线安装包百度网盘下载。

    Visual Studio 2017 于2017年10月13日发布 15.4 版本.该版本包含多项生产力改进,支持 .NET Standard 2.0 ,并且可以开启 Xamarin Live Pla ...

  9. LeetCode第六天

    第六天 30.(219) Contains Duplicate II JAVA class Solution { public boolean containsNearbyDuplicate(int[ ...

  10. Qt msvc 乱码如何解决?

    #ifdef Q_OS_WIN #pragma execution_character_set("UTF-8") #endif