享元模式

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. SpringMVC源码之参数解析绑定原理

    摘要 本文从源码层面简单讲解SpringMVC的参数绑定原理 SpringMVC参数绑定相关组件的初始化过程 在理解初始化之前,先来认识一个接口 HandlerMethodArgumentResolv ...

  2. SSE图像算法优化系列十六:经典USM锐化中的分支判断语句SSE实现的几种方法尝试。

    分支判断的语句一般来说是不太适合进行SSE优化的,因为他会破坏代码的并行性,但是也不是所有的都是这样的,在合适的场景中运用SSE还是能对分支预测进行一定的优化的,我们这里以某一个算法的部分代码为例进行 ...

  3. java处理json与对象的转化 递归

    整个类是一个case,总结了我在使用java处理json的时候遇到的问题,还有级联关系的对象如何遍历,json和对象之间的转换! 对于对象json转换中遇到的问题我参考了一篇博客,http://blo ...

  4. PLECS_直流电机基本系统模型

    1.模型图 2.模型仿真结果 (1)Step阶跃t=1s,R=20Ω,V_dc = 120V,那么此时 电源电压波形: 电机电枢电流波形: 电机电磁转矩: 电机转速波形: (2)其他参数不变将R=30 ...

  5. centos7 网桥的配置

    centos7下配置网桥,两个步骤:1.新建网桥配置2.修改网卡配置 新建br0 网桥配置 在/etc/sysconfig/network-scripts/目录下新建ifcfg-br0,添加如下配置信 ...

  6. linux 下yum使用技巧

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

  7. Java经典编程题50道之四十

    将几个字符串排序(按英文字母的顺序). public class Example40 {    public static void main(String[] args) {        Stri ...

  8. Maven中的pom.xml详解

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  9. ErrorKiller:Failed to decode response: zlib_decode(): data error

    先更新composer自己,composer self-update 然后再更新依赖关系 composer update

  10. 1 Python数据类型--

    常见的Python数据类型: (1)数值类型:就是平时处理的数字(整数.浮点数) (2)序列类型:有一系列的对象并排或者排列的情况.如字符串(str),列表(list),元组(tuple)等 (3)集 ...