We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
享元模式在中文表面上不是很好理解,但是英文fly的原意是苍蝇,可以理解成粒度跟苍蝇一般大小。举个生活例子:围棋的棋子,如果用编程去实现的话,势必要new出很多实例,整盘棋结束时估计内存也快爆了。如果使用享元模式,把围棋的共性共性出来,一些外部状态则外部动态控制,那么这个效率才是最优的。比如共享的应该是这个棋子的形状、大小、图片,外部动态修改的应该是棋子的位置。那么,只要定义两个类,黑棋和白棋,而且工厂中或说整个系统中也就只维护两个对象,一个是黑棋一个是白棋。
享元模式主要用于性能优化。在以下几种场景中可以使用该模式:
内部状态:在享元对象内部不随外界环境改变而改变的共享部分。 外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。
区分两者之间有一些指引:
花了这么多功夫介绍外部和内部状态,这也是使用享元模式的关键所在。区分出了外部和内部状态,接下来的工作就简单了。
假设有个内衣工厂,目前的产品有50种男式内衣和50种女士内衣,为了推销产品,工厂生产一些塑料模特来穿上他们的内衣拍成广告照片。正常情况下50个男模特和50个女模特,然后让他们每人分别穿一件内衣来拍照。不使用享元模式的情况下,在程序里也许会这样写:
var Model =function(sec,underware){ this.sex= sex; this.underware= underware; } Model.prototype.takePhoto=function(){ console.log('sex='+this.sex+'underwear='+this.underware); } for(vari=1;i<=50;i++){ var maleModel=newModel('male','underwear'+i); maleModel.takePhoto(); } for(vari=1;i<=50;i++){ var femaleModel=newModel('female','underwear'+i); femaleModel.takePhoto(); }
上面是利用工厂模式实现的效果,如果将来要生产更多的内衣,那么就有多少个模型。这迟早会导致溢栈。其实想想,模特我们就只需要一个男模型和一个女模型就足够了。下面改写代码:
var Model = function(sex){ this.sex = sex; } Model.prototype.takePhoto = function(){ console.log(‘sex=’ + this.sex + ’underware=’ + this.underwear); } var maleModel = new Model(‘male’), femaleModel= new Model(‘female’); //给男模特拍照 for(var i=1;i<=50;i++){ maleModel.underware = ‘underware’+i; maleModel.takePhoto(); } //同理女模特 for(var i=1;i<=50;i++){ femaleModel.underware = ‘underware’+i; femaleModel.takePhoto(); }
上面只使用了两个对象即完成了一样的功能。还有开发中经常用到的连接池,比如对象池——维护一个装载对象的池子。当我们需要访问对象的时候,不是直接new,而是先去判断连接池有没对应的实例,有的话直接使用,没有再去new;new了之后存入连接池,以备之后使用。
在JavaScript中应用享元模式有两种方式,第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。
把一个对象的数据保存在两个不同的对象中(共享对象、管理器对象)
比如图书馆中的一本书可以用一个对象来表示,他有很多属性:
var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){ ...//初始化代码 } Book.prototype = { getTitle:function(){ return this.title; }, ... // 更新借出状态方法 updateCheckoutStatus:function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){...}, //续借 extendCheckoutPeriod: function(bookID, newReturnDate){...}, //是否到期 isPastDue: function(bookID){...} }
程序刚开始可能没问题,但是随着时间的增加,图书可能大批量增加,并且每种图书都有不同的版本和数量,你将会发现系统变得越来越慢。几千个book对象在内存里可想而知,我们需要用享元模式来优化。
我们可以将数据分成内部和外部两种数据,同一本书中,和book对象相关的数据(title,author等)可以归结为内部属性,而(checkoutMember,dueReturnDate等)可以归结为外部属性。这样,如下代码就可以在同一本书里共享同一个对象了,因为不管谁借的书,只要书是同一本书,基本信息是一样的:
//共享对象 var Book = function(title, author, genre, pageCount, publisherID, ISBN){ this.title = title; this.author = author; this.genre = genre; this.pageCount = pageCount; this.publisherID = publisherID; this.ISBN = ISBN; };
让我们来定义一个基本工厂,用来检查之前是否创建该book的对象,如果有就返回,没有就重新创建并存储以便后面可以继续访问,这确保我们为每一种书只创建一个对象:
/* Book工厂 单例 */ var BookFactory = (function(){ var existingBooks = {}; return{ createBook: function(title, author, genre,pageCount,publisherID,ISBN){ /*查找之前是否创建*/ var existingBook = existingBooks[ISBN]; if(existingBook){ return existingBook; }else{ /* 如果没有,就创建一个,然后保存*/ var book = new Book(title, author, genre,pageCount,publisherID,ISBN); existingBooks[ISBN] = book; return book; } } } });
外部状态,相对就简单了,除了我们封装好的book,其它都需要在这里管理:
/*BookRecordManager 借书管理类 单例*/ var BookRecordManager = (function(){ var bookRecordDatabase = {}; return{ /*添加借书记录*/ addBookRecord: function(id, title, author, genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember, dueReturnDate, availability){ var book = bookFactory.createBook(title, author, genre,pageCount,publisherID,ISBN); bookRecordDatabase[id] ={ checkoutMember: checkoutMember, checkoutDate: checkoutDate, dueReturnDate: dueReturnDate, availability: availability, book: book; }; }, updateCheckoutStatus: function(bookID, newStatus, checkoutDate, checkoutMember, newReturnDate){ var record = bookRecordDatabase[bookID]; record.availability = newStatus; record.checkoutDate = checkoutDate; record.checkoutMember = checkoutMember; record.dueReturnDate = newReturnDate; }, extendCheckoutPeriod: function(bookID, newReturnDate){ bookRecordDatabase[bookID].dueReturnDate = newReturnDate; }, isPastDue: function(bookID){ var currentDate = new Date(); return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate); } }; });
通过这种方式,我们做到了将同一种图书的相同信息保存在一个bookmanager对象里,而且只保存一份;相比之前的代码,就可以发现节约了很多内存。
开发中存在性能瓶颈的时候,就可以考虑下享元模式。使用享元模式的关键就是区分对象的内部状态和外部状态。
享元模式 《JavaScript设计模式与开发实践》
The text was updated successfully, but these errors were encountered:
No branches or pull requests
定义
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
享元模式在中文表面上不是很好理解,但是英文fly的原意是苍蝇,可以理解成粒度跟苍蝇一般大小。举个生活例子:围棋的棋子,如果用编程去实现的话,势必要new出很多实例,整盘棋结束时估计内存也快爆了。如果使用享元模式,把围棋的共性共性出来,一些外部状态则外部动态控制,那么这个效率才是最优的。比如共享的应该是这个棋子的形状、大小、图片,外部动态修改的应该是棋子的位置。那么,只要定义两个类,黑棋和白棋,而且工厂中或说整个系统中也就只维护两个对象,一个是黑棋一个是白棋。
作用
享元模式主要用于性能优化。在以下几种场景中可以使用该模式:
内部状态和外部状态
内部状态:在享元对象内部不随外界环境改变而改变的共享部分。
外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。
区分两者之间有一些指引:
花了这么多功夫介绍外部和内部状态,这也是使用享元模式的关键所在。区分出了外部和内部状态,接下来的工作就简单了。
举例
假设有个内衣工厂,目前的产品有50种男式内衣和50种女士内衣,为了推销产品,工厂生产一些塑料模特来穿上他们的内衣拍成广告照片。正常情况下50个男模特和50个女模特,然后让他们每人分别穿一件内衣来拍照。不使用享元模式的情况下,在程序里也许会这样写:
上面是利用工厂模式实现的效果,如果将来要生产更多的内衣,那么就有多少个模型。这迟早会导致溢栈。其实想想,模特我们就只需要一个男模型和一个女模型就足够了。下面改写代码:
上面只使用了两个对象即完成了一样的功能。还有开发中经常用到的连接池,比如对象池——维护一个装载对象的池子。当我们需要访问对象的时候,不是直接new,而是先去判断连接池有没对应的实例,有的话直接使用,没有再去new;new了之后存入连接池,以备之后使用。
在JavaScript中应用享元模式有两种方式,第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。
把一个对象的数据保存在两个不同的对象中(共享对象、管理器对象)
比如图书馆中的一本书可以用一个对象来表示,他有很多属性:
程序刚开始可能没问题,但是随着时间的增加,图书可能大批量增加,并且每种图书都有不同的版本和数量,你将会发现系统变得越来越慢。几千个book对象在内存里可想而知,我们需要用享元模式来优化。
我们可以将数据分成内部和外部两种数据,同一本书中,和book对象相关的数据(title,author等)可以归结为内部属性,而(checkoutMember,dueReturnDate等)可以归结为外部属性。这样,如下代码就可以在同一本书里共享同一个对象了,因为不管谁借的书,只要书是同一本书,基本信息是一样的:
让我们来定义一个基本工厂,用来检查之前是否创建该book的对象,如果有就返回,没有就重新创建并存储以便后面可以继续访问,这确保我们为每一种书只创建一个对象:
外部状态,相对就简单了,除了我们封装好的book,其它都需要在这里管理:
通过这种方式,我们做到了将同一种图书的相同信息保存在一个bookmanager对象里,而且只保存一份;相比之前的代码,就可以发现节约了很多内存。
优点
缺点
小结
开发中存在性能瓶颈的时候,就可以考虑下享元模式。使用享元模式的关键就是区分对象的内部状态和外部状态。
参考资料
享元模式
《JavaScript设计模式与开发实践》
The text was updated successfully, but these errors were encountered: