深入理解JavaScript系列(37):设计模式之享元模

介绍

享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。
享 元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的 话,就可以大幅度较少需要实例化的类的数量。如果能把那些参数移动到类实例的外面,在方法调用的时候将他们传递进来,就可以通过共享大幅度第减少单个实例 的数目。
那么如果在JavaScript中应用享元模式呢?有两种方式,第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。

享元与数据层

Flyweight中有两个重要概念--内部状态intrinsic和外部状态extrinsic之分,内部状态就是在对象里通过内部方法管理,而外部信息可以在通过外部删除或者保存。
说 白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中 常出现Factory模式,Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象。

使用享元模式

让我们来演示一下如果通过一个类库让系统来管理所有的书籍,每个书籍的元数据暂定为如下内容:

双击代码全选

1

2

3

4

5

6

7

ID

Title

Author

Genre

Page count

Publisher ID

ISBN

 

我们还需要定义每本书被借出去的时间和借书人,以及退书日期和是否可用状态:

双击代码全选

1

2

3

4

checkoutDate

checkoutMember

dueReturnDate

availability

 

因为book对象设置成如下代码,注意该代码还未被优化:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){

this.id = id;

this.title = title;

this.author = author;

this.genre = genre;

this.pageCount = pageCount;

this.publisherID = publisherID;

this.ISBN = ISBN;

this.checkoutDate = checkoutDate;

this.checkoutMember = checkoutMember;

this.dueReturnDate = dueReturnDate;

this.availability = availability;

};

Book.prototype = {

   getTitle:function(){

returnthis.title;

   },

   getAuthor: function(){

returnthis.author;

   },

   getISBN: function(){

returnthis.ISBN;

   },

/*其它get方法在这里就不显示了*/

 

// 更新借出状态

updateCheckoutStatus: function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){

this.id  = bookID;

this.availability = newStatus;

this.checkoutDate = checkoutDate;

this.checkoutMember = checkoutMember;

this.dueReturnDate = newReturnDate;

},

//续借

extendCheckoutPeriod: function(bookID, newReturnDate){

this.id =  bookID;

this.dueReturnDate = newReturnDate;

},

//是否到期

isPastDue: function(bookID){

var currentDate = new Date();

return currentDate.getTime() > Date.parse(this.dueReturnDate);

 }

};

 

程序刚开始可能没问题,但是随着时间的增加,图书可能大批量增加,并且每种图书都有不同的版本和数量,你将会发现系统变得越来越慢。几千个book对象在内存里可想而知,我们需要用享元模式来优化。
我 们可以将数据分成内部和外部两种数据,和book对象相关的数据(title, author 等)可以归结为内部属性,而(checkoutMember, dueReturnDate等)可以归结为外部属性。这样,如下代码就可以在同一本书里共享同一个对象了,因为不管谁借的书,只要书是同一本书,基本信息 是一样的:

双击代码全选

1

2

3

4

5

6

7

8

9

/*享元模式优化代码*/

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的对象,如果有就返回,没有就重新创建并存储以便后面可以继续访问,这确保我们为每一种书只创建一个对象:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/* 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,其它都需要在这里管理:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

/*BookRecordManager 借书管理类 单例*/

var BookRecordManager = (function(){

var bookRecordDatabase = {};

return{

/*添加借书记录*/

       addBookRecord: function(id, title, author, genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember, dueReturnDate, availability){

varbook = 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);

   }

 };

});

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Grow your business fast with

Suku