享元模式_JavaScript设计模式12

一.享元模式的结构

0.内部状态与外部状态

在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。

简单地说,内部状态是对象本身的属性,外部状态是管理这些对象所需的额外的属性,相同的对象内部状态相同,但外部状态可能不同(比如2本相同的书被2个人借走了)

1.享元

享元是相似对象(书的例子中指的是完全相同的书,而不是题材相似的书)间可共享的属性的集合,比如书的名字、作者、ISBN等等,完全相同的书这些信息都是相同的,没有必要把相同的属性在多个相似对象中保存多份,享元负责把这些属性分离出来,以便共享

如果可共享的属性比较复杂,还可以增加抽象享元,以及与之对应的具体享元,还可以有复合享元

2.享元工厂

享元工厂负责创建并管理享元,实现共享逻辑(创建时判断是否存在,已存在就返回现有对象,否则创建一个)

3.客户端(Client)

Client负责调用享元工厂,并存储管理相似对象所需的额外属性(比如书的id,借/还日期,是否在馆等等)

二.享元模式实例

// 图书管理

// 书的属性
// id
// title
// author
// genre
// page count
// publisher id
// isbn

// 管理所需的额外属性
// checkout date
// checkout member
// due return date
// availability

// 享元(存储内部状态)
function Book(title, author, genre, pageCount, publisherId, isbn) {
    this.title = title;
    this.author = author;
    this.genre = genre;
    this.pageCount = pageCount;
    this.publisherId = publisherId;
    this.isbn = isbn;
}

// 享元工厂(创建/管理享元)
var BookFactory = (function() {
    var existingBooks = {};
    var existingBook = null;

    return {
        createBook: function(title, author, genre, pageCount, publisherId, isbn) {
            // 如果书籍已经创建,,则找到并返回
            // !!强制返回bool类型
            existingBook = existingBooks[isbn];
            if (!!existingBook) {
                return existingBook;
            }
            else {
                // 如果不存在选择创建该书的新实例并保存
                var book = new Book(title, author, genre, pageCount, publisherId, isbn);
////
console.log('new book');
                existingBooks[isbn] = book;
                return book;
            }
        }
    }
})();

// 客户端(存储外部状态)
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
            }
        },
        updateCheckStatus: 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 currDate = new Date();

            return currDate.getTime() > Date.parse(bookRecordDatabase[bookId].dueReturnDate);
        }
    };
})();

// test
// isbn号是书籍的唯一标识,以下三条只会创建一个book对象
BookRecordManager.addBookRecord(1, 'x', 'x', 'xx', 300, 10001, '100-232-32');   // new book
BookRecordManager.addBookRecord(1, 'xx', 'xx', 'xx', 300, 10001, '100-232-32');
BookRecordManager.addBookRecord(1, 'xxx', 'xxx', 'xxx', 300, 10001, '100-232-32');

如果需要管理的书籍数量非常大,那么使用享元模式节省的内存将是一个可观的数目

三.jQuery与享元模式

例子是JAMES PADOLSEY的jQuery.single,如下:

jQuery.single = (function(o){

    var collection = jQuery([1]); // Fill with 1 item, to make sure length === 1

    return function(element) {

        // Give collection the element:
        collection[0] = element;

        // Return the collection:
        return collection;

    };

}());

// window.$_ = jQuery.single;   // 定义别名

// test
jQuery('a').click(function(){

    var html = jQuery.single(this).next().html(); // Method chaining works!

    alert(html);

    // etc. etc.

});

维护了一个单例collection,避免多次用$()包裹同一个DOM对象带来的内存消耗(会创建多个jQuery对象),使用jQuery.single永远都只会创建一个jQuery对象,节省了创建额外jQuery对象消耗的时间,还减少了内存开销,但这样做最大的问题可能是:jQuery.single返回的对象无法被缓存。因为内部是单例实现,缓存的对象在下一次调用jQuery.single后可能会被改变,所以无法像$()一样随时缓存。但据说直接使用jQuery.single获取单例要比缓存普通jQuery对象更快,但为了避免混乱,建议只在需要把DOM对象包裹成jQuery对象时才使用jQuery.single方法

四.享元模式的优缺点

优点

  • 减少内存开销

  • 提供了一种方便的管理大量相似对象的方法

缺点

  • 享元模式需要分离内部状态和外部状态,会使逻辑变得更加复杂

  • 外部状态被分离出去后,访问会产生轻微的额外时耗(时间换空间?)

P.S.如果项目中需要创建大量实例对象,就应该考虑一下享元模式是否适用

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code