设计模式

创建型模式

原型模式

是用于创建重复的对象,同时又能保证性能。

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(); // marry
person2.sayName(); // marry
console.log(person1.sayName === person2.sayName); // true

单例模式

单例模式(Singleton Pattern)涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。

特点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。
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; // {a: 3, b: 6}

const obj2 = createBox(10, 20);
obj1; // {a: 10, b: 20}
obj2; // {a: 10, b: 20}

工厂模式

根据不同的输入返回不同类的实例,一般用来创建同一类对象

优点:

  1. 良好的封装,代码结构清晰,访问者无需知道对象的创建流程,特别是创建比较复杂的情况下;
  2. 扩展性优良,通过工厂方法隔离了用户和创建流程隔离,符合开放封闭原则;
  3. 解耦了高层逻辑和底层产品类,符合最少知识原则,不需要的就不要去交流;

缺点:
  带来了额外的系统复杂度,增加了抽象性;

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('红烧排骨');
// Error 这个菜本店没有

抽象工厂模式

  通过对类的工厂抽象使其业务用于对产品类簇的创建,而不是负责创建某一类产品的实例。关键在于使用抽象类制定了实例的结构,调用者直接面向实例的结构编程,从实例的具体实现中解耦。

优点:
  抽象产品类将产品的结构抽象出来,访问者不需要知道产品的具体实现,只需要面向产品的结构编程即可,从产品的具体实现中解耦;

缺点:

  1. 扩展新类簇的产品类比较困难,因为需要创建新的抽象产品类,并且还要修改工厂类,违反开放封闭原则;
  2. 带来了系统复杂度,增加了新的类,和新的继承关系;
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('红烧排骨');
// Error 本店没有这个

建造者模式

用于分步构建一个复杂的对象,将一个复杂对象的 构建层与其表示层分离。若不是极其复杂的对象,应选择使用对象字面或工厂模式等方式创建对象。

优点:

  1. 封装性好,创建和使用分离;
  2. 扩展性好,建造类之间独立、一定程度上解耦。

缺点:

  1. 产生多余的Builder对象;
  2. 产品内部发生变化,建造者都要修改,成本较大。
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;
}

// 生产部件,part1
buildPart1() {
this.part1 = 'part1';
return this
}

// 生产部件,part2
buildPart2() {
this.part2 = 'part2';
return this;
}
}

// 汽车装配,获得产品实例
const benchi1 = new CarBuilder('param')
.buildPart1()
.buildPart2();

console.log(benchi1);

// {
// param: "param"
// part1: "part1"
// part2: "part2"
// }

结构性模式

桥架模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化。使用组合关系代替继承关系,降低抽象和实现两个可变维度的耦合度。

优点:

  1. 分离了抽象和实现部分,将实现层(DOM 元素事件触发并执行具体修改逻辑)和抽象层( 元素外观、尺寸部分的修改函数)解耦,有利于分层;
  2. 提高了可扩展性,多个维度的部件自由组合,避免了类继承带来的强耦合关系,也减少了部件类的数量;
  3. 使用者不用关心细节的实现,可以方便快捷地进行使用;

缺点:

  1. 桥接模式要求两个部件没有耦合关系,否则无法独立地变化,因此要求正确的对系统变化的维度进行识别,使用范围存在局限性;
  2. 桥接模式的引入增加了系统复杂度;
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, 我是男生')
}

// 有一个功能叫playInstrument, 没有具体乐器
this.playInstrument = function() {
instrument.play()
}
}

function Girl(instrument) {
this.sayHi = function() {
console.log('hi, 我是女生')
}

// 有一个功能叫playInstrument, 没有具体乐器
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. 通过合理使用外观模式,可以帮助我们更好地划分系统访问层次,比如把需要暴露给外部的功能集中到外观中,这样既方便访问者使用,也很好地隐藏了内部的细节,提升了安全性;

缺点:

  1. 不符合开闭原则,对修改关闭,对扩展开放,如果外观模块出错,那么只能通过修改的方式来解决问题,因为外观模块是子系统的唯一出口;
  2. 不需要或不合理的使用外观会让人迷惑,过犹不及;
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. 外部状态相对独立,不会影响到内部状态,所以享元对象能够在不同的环境被共享;

缺点:

  1. 引入了共享对象,使对象结构变得复杂;
  2. 共享对象的创建、销毁等需要维护,带来额外的复杂度(如果需要把共享对象维护起来的话);
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); // 把当前id对应的外部状态都组装到共享对象中
// 大于3000k提示
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
}
]);

适配器模式

代理模式

组合模式

装饰器模式

组合模式

装饰器模式

行为型模式

观察者模式

迭代器模式

策略模式

模板方法模式

状态模式

命令模式

访问者模式