qiuhongbingo / blog

Writing something in Issues.
https://github.com/qiuhongbingo/blog/issues
3 stars 0 forks source link

享元模式在前端中的应用 #27

Open qiuhongbingo opened 4 years ago

qiuhongbingo commented 4 years ago
/**
 * 享元模式是用于性能优化的一种常见模式,它依靠:
 * 减少创建对象实例的数量
 * 运用共享技术来有效支持大量细粒度的对象
 * 这两种方式减少内存占用,以提高性能
 */

/**
 * 例子:在图书管理系统中,每本书都有以下特性:
 * ID
 * Title
 * Author
 * Genre
 * Page count
 * Publisher ID
 * ISBN
 */

/**
 * 同时我们需要以下属性来追踪每一本书时,记录它是否可用、归还时间等:
 * checkoutDate
 * checkoutMember
 * dueReturnDate
 * availability
 */

// 那么 Book 类看上去像
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() {
    return this.title
  },

  getAuthor: function() {
    return this.author
  },

  getISBN: function() {
    return this.ISBN
  },

  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() {
    var currentDate = new Date()
    return currentDate.getTime() > Date.parse(this.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
}

/**
 * 我们将外在特性删去,check-outs 等信息将会被移动到一个新的类中,一个新的工厂函数也将出现
 * 在这个工厂函数中,我们将会检查当前需要创建的书籍是否已经存在,如果存在直接返回书实例
 * 否则进行调用 Book 构造函数进行创建
 * 这保证了所有的书都是唯一的,而不存在重复
 */
var BookFactory = (function() {
  var existingBooks = {}
  var existingBook
  return {
    createBook: function(title, author, genre, pageCount, publisherID, ISBN) {
      existingBook = existingBooks[ISBN]
      if (!!existingBook) {
        return existingBook
      } else {
        var book = new Book(title, author, genre, pageCount, publisherID, ISBN)
        existingBooks[ISBN] = book
        return book
      }
    }
  }
})()

/**
 * 书目所有的外在特性都被从书本身的特性中抽离
 * 现在被移动到 BookManager 的 BookDatabase 当中
 * 对于书借入/借出的操作也移动到了 BookRecordManager 当中,因为这些方法需要直接操作书的外在特性
 * 如此一来,比一本书拥有多项属性的大 object 模式更加高效,也更利于维护
 * 如果有 30 本同样一本书的 copy,现有的模式下只存储了一个实例
 * 同时对于书状态转移的函数,我们维护在 BookManager 当中,而不再出现在对象(原型)上
 * 如果这些函数出现在每一个书实例当中,将会是更大的开销
 */
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)
    }
  }
})()

/**
 * 享元模式在前端还有更多的应用,比如事件代理就是一个很典型的体现
 */
{
  /* <div id="container">
  <div class="toggle" href="#">
    <span class="info">1</span>
  </div>
  <div class="toggle" href="#">
    <span class="info">2</span>
  </div>
</div> */
}

// 我们集中将事件处理放到父容器上
var stateManager = {
  fly: function() {
    var self = this
    $('#container')
      .unbind()
      .on('click', 'div.toggle', function(e) {
        self.handleClick(e.target)
      })
  },

  handleClick: function(elem) {
    $(elem)
      .find('span')
      .toggle('slow')
  }
}