设计模式
创建型模式
原型模式
是用于创建重复的对象,同时又能保证性能。
1 2 3 4 5 6 7 8 9 10 11 12
| function Person () { Person.prototype.name = "marry"; Person.prototype.sayName = function(){ console.log(this.name); } } const person1 = new Person(); const person2 = new Person(); person1.sayName(); person2.sayName(); console.log(person1.sayName === person2.sayName);
|
单例模式
单例模式(Singleton Pattern)涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。
特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| let box; const createBox = (_a, _b) => { if(!box){ box = {}; } box.a = _a; box.b = _b; return box; }; const obj1 = createBox(3, 6); obj1; const obj2 = createBox(10, 20); obj1; obj2;
|
工厂模式
根据不同的输入返回不同类的实例,一般用来创建同一类对象
优点:
- 良好的封装,代码结构清晰,访问者无需知道对象的创建流程,特别是创建比较复杂的情况下;
- 扩展性优良,通过工厂方法隔离了用户和创建流程隔离,符合开放封闭原则;
- 解耦了高层逻辑和底层产品类,符合最少知识原则,不需要的就不要去交流;
缺点:
  带来了额外的系统复杂度,增加了抽象性;
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
| class Restaurant { static getMenu(menu) { switch (menu) { case '鱼香肉丝': return new YuXiangRouSi(); case '宫保鸡丁': return new GongBaoJiDin(); default: throw new Error('这个菜本店没有'); } } };
class YuXiangRouSi { constructor() { this.type = '鱼香肉丝' } eat() { console.log(this.type + ' 真香') } };
class GongBaoJiDin { constructor() { this.type = '宫保鸡丁' } eat() { console.log(this.type + ' 让我想起了外婆做的菜'); } }; const dish1 = Restaurant.getMenu('鱼香肉丝'); dish1.eat();
const dish2 = Restaurant.getMenu('红烧排骨');
|
抽象工厂模式
  通过对类的工厂抽象使其业务用于对产品类簇的创建,而不是负责创建某一类产品的实例。关键在于使用抽象类制定了实例的结构,调用者直接面向实例的结构编程,从实例的具体实现中解耦。
优点:
  抽象产品类将产品的结构抽象出来,访问者不需要知道产品的具体实现,只需要面向产品的结构编程即可,从产品的具体实现中解耦;
缺点:
- 扩展新类簇的产品类比较困难,因为需要创建新的抽象产品类,并且还要修改工厂类,违反开放封闭原则;
- 带来了系统复杂度,增加了新的类,和新的继承关系;
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| class AbstractRestaurant { constructor() { if (new.target === AbstractRestaurant) { throw new Error('抽象类不能直接实例化!') } this.signborad = '饭店' } createDish() { throw new Error('抽象方法不能调用!') } createSoup() { throw new Error('抽象方法不能调用!') } }
class Restaurant extends AbstractRestaurant { constructor() { super() } createDish(type) { switch (type) { case '鱼香肉丝': return new YuXiangRouSi(); case '宫保鸡丁': return new GongBaoJiDing(); default: throw new Error('本店没这个菜'); } } createSoup(type) { switch (type) { case '紫菜蛋汤': return new ZiCaiDanTang(); default: throw new Error('本店没这个汤'); } } }
class AbstractDish { constructor() { if (new.target === AbstractDish) { throw new Error('抽象类不能直接实例化!') } this.kind = '菜' } eat() { throw new Error('抽象方法不能调用!') } }
class YuXiangRouSi extends AbstractDish { constructor() { super() this.type = '鱼香肉丝' } eat() { console.log(this.kind + ' - ' + this.type + ' 真香~') } }
class GongBaoJiDing extends AbstractDish { constructor() { super() this.type = '宫保鸡丁' } eat() { console.log(this.kind + ' - ' + this.type + ' 让我想起了外婆做的菜') } }
class AbstractSoup { constructor() { if (new.target === AbstractDish) { throw new Error('抽象类不能直接实例化!') } this.kind = '汤' } drink() { throw new Error('抽象方法不能调用!') } }
class ZiCaiDanTang extends AbstractSoup { constructor() { super() this.type = '紫菜蛋汤' } drink() { console.log(this.kind + ' - ' + this.type + ' 我从小喝到大') } } const restaurant = new Restaurant(); const soup1 = restaurant.createSoup('紫菜蛋汤'); soup1.drink();
const dish1 = restaurant.createDish('鱼香肉丝'); dish1.eat();
const dish2 = restaurant.createDish('红烧排骨');
|
建造者模式
用于分步构建一个复杂的对象,将一个复杂对象的 构建层与其表示层分离。若不是极其复杂的对象,应选择使用对象字面或工厂模式等方式创建对象。
优点:
- 封装性好,创建和使用分离;
- 扩展性好,建造类之间独立、一定程度上解耦。
缺点:
- 产生多余的Builder对象;
- 产品内部发生变化,建造者都要修改,成本较大。
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
| class CarBuilder { constructor(param) { this.param = param; } buildPart1() { this.part1 = 'part1'; return this } buildPart2() { this.part2 = 'part2'; return this; } }
const benchi1 = new CarBuilder('param') .buildPart1() .buildPart2(); console.log(benchi1);
|
结构性模式
桥架模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。使用组合关系代替继承关系,降低抽象和实现两个可变维度的耦合度。
优点:
- 分离了抽象和实现部分,将实现层(DOM 元素事件触发并执行具体修改逻辑)和抽象层( 元素外观、尺寸部分的修改函数)解耦,有利于分层;
- 提高了可扩展性,多个维度的部件自由组合,避免了类继承带来的强耦合关系,也减少了部件类的数量;
- 使用者不用关心细节的实现,可以方便快捷地进行使用;
缺点:
- 桥接模式要求两个部件没有耦合关系,否则无法独立地变化,因此要求正确的对系统变化的维度进行识别,使用范围存在局限性;
- 桥接模式的引入增加了系统复杂度;
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
| function Boy(instrument) { this.sayHi = function() { console.log('hi, 我是男生') } this.playInstrument = function() { instrument.play() } } function Girl(instrument) { this.sayHi = function() { console.log('hi, 我是女生') } this.playInstrument = function() { instrument.play() } } function Piano() { this.play = function() { console.log('钢琴开始演奏') } } function Guitar() { this.play = function() { console.log('吉他开始演奏') } } let piano = new Piano() let guitar = new Guitar() let pianoBoy = new Boy(piano) pianoBoy.playInstrument() let guitarGirl = new Girl(guitar) guitarGirl.playInstrument()
|
外观模式
外观模式为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易。
优点:
- 访问者不需要再了解子系统内部模块的功能,而只需和外观交互即可,使得访问者对子系统的 使用变得简单 ,符合最少知识原则,增强了可移植性和可读性;
- 减少了与子系统模块的直接引用,实现了访问者与子系统中模块之间的松耦合,增加了可维护性和可扩展性;
- 通过合理使用外观模式,可以帮助我们更好地划分系统访问层次,比如把需要暴露给外部的功能集中到外观中,这样既方便访问者使用,也很好地隐藏了内部的细节,提升了安全性;
缺点:
- 不符合开闭原则,对修改关闭,对扩展开放,如果外观模块出错,那么只能通过修改的方式来解决问题,因为外观模块是子系统的唯一出口;
- 不需要或不合理的使用外观会让人迷惑,过犹不及;
1 2 3 4 5 6 7 8 9 10 11
| function setBox(){ var getId = document.getElementById('isShow'); return { show : function(){ getId.style.display = 'block'; }, hide : function(){ getId.style.display = 'none'; } } }
|
享元模式
运用共享技术来有效地支持大量细粒度对象的复用,以减少创建的对象的数量。通俗来讲,享元就是共享单元,比如现在流行的共享单车、共享充电宝等,他们的核心理念都是享元模式。
优点:
- 由于减少了系统中的对象数量,提高了程序运行效率和性能,精简了内存占用,加快运行速度;
- 外部状态相对独立,不会影响到内部状态,所以享元对象能够在不同的环境被共享;
缺点:
- 引入了共享对象,使对象结构变得复杂;
- 共享对象的创建、销毁等需要维护,带来额外的复杂度(如果需要把共享对象维护起来的话);
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| var Upload = function(uploadType) { this.uploadType = uploadType; }
Upload.prototype.delFile = function(id) { uploadManger.setExternalState(id, this); if(this.fileSize < 3000) { return this.dom.parentNode.removeChild(this.dom); } if(window.confirm("确定要删除文件吗?" + this.fileName)) { return this.dom.parentNode.removeChild(this.dom); } }
var UploadFactory = (function() { var createdFlyWeightObjs = {}; return { create: function(uploadType) { if(createdFlyWeightObjs[uploadType]) { return createdFlyWeightObjs[uploadType]; } return createdFlyWeightObjs[uploadType] = new Upload(uploadType); } }; })();
var uploadManger = (function() { var uploadDatabase = {}; return { add: function(id, uploadType, fileName, fileSize) { var flyWeightObj = UploadFactory.create(uploadType); var dom = document.createElement('div'); dom.innerHTML = "<span>文件名称:" + fileName + ",文件大小:" + fileSize +"</span>" + "<button class='delFile'>删除</button>"; dom.querySelector(".delFile").onclick = function() { flyWeightObj.delFile(id); }; document.body.appendChild(dom); uploadDatabase[id] = { fileName: fileName, fileSize: fileSize, dom: dom }; return flyWeightObj; }, setExternalState: function(id, flyWeightObj) { var uploadData = uploadDatabase[id]; for(var i in uploadData) { flyWeightObj[i] = uploadData[i]; } } }; })();
var id = 0; window.startUpload = function(uploadType, files) { for(var i=0,file; file = files[i++];) { var uploadObj = uploadManger.add(++id, uploadType, file.fileName, file.fileSize); } };
startUpload("plugin", [ { fileName: '1.txt', fileSize: 1000 },{ fileName: '2.txt', fileSize: 3000 },{ fileName: '3.txt', fileSize: 5000 } ]); startUpload("flash", [ { fileName: '4.txt', fileSize: 1000 },{ fileName: '5.txt', fileSize: 3000 },{ fileName: '6.txt', fileSize: 5000 } ]);
|
适配器模式
代理模式
组合模式
装饰器模式
组合模式
装饰器模式
行为型模式
观察者模式
迭代器模式
策略模式
模板方法模式
状态模式
命令模式
访问者模式