跳到主要內容
黯羽輕揚每天積累一點點

享元模式_JavaScript 設計模式 12

免費2015-08-14#JS#Design_Pattern#JavaScript享元模式#JavaScript蝇量模式#JavaScript Flyweight#JavaScript代码复用

享元(flyweight)模式是用來減少記憶體消耗的,從一堆相似對象中提取出可共享的屬性,以減少相似對象的數量。本文詳細介紹 JavaScript 實現的享元模式

一。享元模式的結構

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.如果項目中需要創建大量實例對象,就應該考慮一下享元模式是否適用

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論