什么是设计模式
设计模式就是前人们总结出来的优秀的编程实践,应对不同场景为复杂的代码实现提供便捷的解决方案
常见的设计模式
- 创建型模式:处以创建对象为目标的设计模式
- 单例
- 工厂方法
- 抽象工厂
- 建造者
- 结构型模式:关注对象之间的组合结构的设计模式
- 装饰者
- 组合
- 外观
- 享元
- 代理
- 行为型模式:提高系统内相互独立的对象之间的交流通讯
- 命令
- 发布订阅
- 观察者
- 备忘录
单例模式
简单来说,单例模式中类的实例的个数只有一个,当需要一个对象去贯穿整个系统执行某些任务时,单例模式就很合适了。
实现单例模式需要解决这几个问题
- 怎么确定类只有一个实例(限制实例个数为 1)?
- 怎么简便的访问类的唯一实例?
实现:
- 将类的构造函数隐藏不直接暴露, 避免被多次实例化
- 通过暴露一个方法 getInstance() 来创建/获取唯一实例
//单例构造器 const singleton =(function(){ //隐藏的类的构造函数 function instanceConstructor(name){ this.name = name } //未初始化的单例对象 let instance return { //通过条件判断创建/获取单例对象 getInstance: function(name){ if(!instance){ instance = new instanceConstructor(name) } return instance } } })() const test = singleton.getInstance('woonsan') const test1 = singleton.getInstance('woon') console.log(test) // instanceConstructor { name: 'woonsan' } console.log(test.name) // woonsan console.log(test1)// instanceConstructor { name: 'woonsan' } console.log(test1.name) // woonsan console.log(test === test1) // true
作为命名空间使用(在es6 块级作用域出来之前常用现在比较少见 例如jQuery等)
const woonsan = (function(){ function instanceConstructor(){ this.name = 'woonsan' } instanceConstructor.prototype.calculate =function(a,b){ return a + b } instanceConstructor.prototype.say =function(){ console.log(`my name is ${this.name}`) } let instance return { getInstance: function(){ if(!instance){ instance = new instanceConstructor() } return instance } } })() const woon = (function(){ function instanceConstructor(){ this.name = 'woon' this.age = '23' } instanceConstructor.prototype.calculate =function(a,b){ return a * b } let instance return { getInstance: function(){ if(!instance){ instance = new instanceConstructor() } return instance } } })() console.log(woonsan.getInstance()) // instanceConstructor { name: 'woonsan' } console.log(woonsan.getInstance().caculate(2,5)) // 7 console.log(woon.getInstance()) // instanceConstructor { name: 'woon', age: '23' } console.log(woon.getInstance().caculate(2,5)) // 10
享元模式
定义:运用共享技术来减少创建对象的数量,避免大量拥有相同内容的小类的开销,从而减少内存占用、提高性能。
- 内部状态:可以被对象集合共享,通常不会改变的属性、方法
- 外部状态:根据应用场景经常改变的属性、方法
下面是一个例子,一个图书馆借书记录,在不使用享元模式优化时,每记录一条借书记录,都需要创建一个Book对象,假设同一本书被借了5次,Book对象会被实例化5次,其中书名、作者、出版id、isbn码等属性都会被创建5次,而其实这几个属性的值都是一样的,因为只是同一本书在不同时间点被借出了5次,重复创建相同的信息对系统造成不少的内存占用,样本小的时候也许看不出什么问题,当样本成千上万时就是比较明显了。
//不使用享元模式 let Book = function (id, title, author, publisherID, ISBN, outDate, checkoutMember, returnDate, status) { this.id = id //id this.title = title //书名 this.author = author //作者 this.publisherID = publisherID //出版id this.ISBN = ISBN //isbn码 this.outDate = outDate //借出日期 this.checkoutMember = checkoutMember //借书人 this.returnDate = returnDate || '暂无' //归还日期 this.status = status || '可借' //书的可借与否状态 } Book.prototype = { getTitle: function () { return this.title }, getAuthor: function () { return this.author }, getISBN: function () { return this.ISBN }, /*其它的get方法*/ // 更新借出状态 updateCheckoutStatus: function (bookID, newStatus, outDate, checkoutMember, newReturnDate) { this.id = bookID this.status = newStatus this.outDate = outDate this.checkoutMember = checkoutMember this.returnDate = newReturnDate }, //续借 extendCheckoutPeriod: function (newReturnDate) { this.returnDate = newReturnDate }, //是否到期 isOverdue: function () { var currentDate = new Date() return this.outDate } } let records = [] records.push(new Book(3, 'javascript', 'ryf', '123452', '123452', '2019-06-22', 'woonsan'))
//使用享元模式进行优化 /* 提取公共属性、方法(内部状态)对于一本书来说即使被借了多次这些属性也是不会变化的 */ let Book = function (title, author, publisherID, ISBN) { this.title = title //书名 this.author = author //作者 this.publisherID = publisherID //出版id this.ISBN = ISBN //isbn码 } /* Book工厂 统一管理书籍信息(管理内部状态) */ let BookFactory = (function () { //用一个对象来存储记录已创建的Book实例以便复用,假设同一本书被借了五次,也只是创建了一次 Book 类的实例 let existingBooks = {} return { createBook: function (title, author, publisherID, ISBN) { /* 查找之前是否已创建 */ let existingBook = existingBooks[ISBN] if (existingBook) { return existingBook } else { /* 如果没有,就创建一个,然后保存 */ let book = new Book(title, author, publisherID, ISBN) existingBooks[ISBN] = book return book } } } })() /*BookRecordManager 借书管理类(管理内部状态)*/ let BookRecordManager = (function () { //用一个对象来存储借书记录 let bookRecordDatabase = {} return { //新增一条记录 addBookRecord: function (id, title, author, publisherID, ISBN, outDate, checkoutMember, returnDate, status) { let book = BookFactory.createBook(title, author, publisherID, ISBN); bookRecordDatabase[id] = { checkoutMember: checkoutMember, outDate: outDate, returnDate: returnDate || '暂无', status: status || '可借', book: book } }, //根据id获取一条记录 getRecord: function (id) { return bookRecordDatabase[id] }, //更新记录信息 updateCheckoutStatus: function (bookID, outDate, checkoutMember, returnDate, status) { let record = bookRecordDatabase[bookID] record.status = status record.outDate = outDate record.checkoutMember = checkoutMember record.returnDate = returnDate }, //延长归还时间 extendCheckoutPeriod: function (bookID, returnDate) { bookRecordDatabase[bookID].returnDate = returnDate }, //查看是否超期 isOverdue: function (bookID) { return bookRecordDatabase[bookID].outDate } } })() //创建一条记录 BookRecordManager.addBookRecord(2, 'NBA', 'Kobe', '123456', '123', '2019-06-22', 'woonsan') //不使用享元模式下记录20条含多条已存在书籍的记录并调用isOverdue() 通过console.time()\console.timeEnd()记录消耗时间 default: 7.745ms 查看chrome F12下内存快照占用总内存1156字节 //使用享元模式下记录20条含多条已存在书籍的记录并调用isOverdue() 通过console.time()\console.timeEnd()记录消耗时间 default: 6.773ms 查看chrome F12下内存快照占用总内存860字节 //20条数据相差的消耗时间和内存占用不太明显,但是当样本数据量比较大时,两者相差会更加直观
单例vs享元
单例模式主要是为了让实例唯一,复用一个类生成的实例,减少重复创建的消耗。
享元模式在区分出不同种类的外部状态后,创建新 对象时如果需要选择不同种类的共享对象时可以使用工厂模式来提供共享对象,在共享对象的维护上,可以单例模式来提供单实例的共享对象。
心得
学习和了解经典的设计模式对于对于我们来说还是蛮有意义的,至少通过对这本书的阅读学习和查找资料过程中,了解到一些常常被运用到到日常开发中又被自己忽视优秀解决方案,这次对设计模式的学习目的也不是为了硬要去用哪种哪种设计模式,而是让我们在遇到某些场景时有一些思路去思考和解决问题。
let record = [ { id: 1, title: 'Lakers', author: 'Kobe', publisherID: '123450', ISBN: '123450', outDate: '2019-06-22', checkoutMember: 'woonsan', }, { id: 2, title: 'java', author: 'sun', publisherID: '123451', ISBN: '123451', outDate: '2019-06-22', checkoutMember: 'woonsan' }, { id: 3, title: 'Lakers', author: 'Kobe', publisherID: '123450', ISBN: '123450', outDate: '2019-06-26', checkoutMember: 'woon' }, { id: 4, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-22', checkoutMember: 'woonsan' }, { id: 5, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-25', checkoutMember: 'woon' }, { id: 6, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-23', checkoutMember: 'woonsan' }, { id: 7, title: 'NBA', author: 'sb', publisherID: '123453', ISBN: '123453', outDate: '2019-06-22', checkoutMember: 'woon' }, { id: 8, title: 'NBA', author: 'sb', publisherID: '123453', ISBN: '123453', outDate: '2019-06-26', checkoutMember: 'woonsan' }, { id: 9, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-28', checkoutMember: 'woon' }, { id: 10, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2020-06-25', checkoutMember: 'woonsan' }, { id: 11, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2021-06-23', checkoutMember: 'woon' }, { id: 12, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-28', checkoutMember: 'woon' }, { id: 13, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2020-06-25', checkoutMember: 'woon' }, { id: 14, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2021-06-23', checkoutMember: 'woonsan' }, { id: 15, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-06-28', checkoutMember: 'woon' }, { id: 16, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2020-06-25', checkoutMember: 'woonsan' }, { id: 17, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2021-06-23', checkoutMember: 'woon' }, { id: 18, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2021-06-23', checkoutMember: 'woon' }, { id: 19, title: 'NBA', author: 'sb', publisherID: '123453', ISBN: '123453', outDate: '2021-07-23', checkoutMember: 'woon' }, { id: 20, title: 'javascript', author: 'ryf', publisherID: '123452', ISBN: '123452', outDate: '2019-07-03', checkoutMember: 'woonsan' }, ]