设计模式

目前我们常说的设计模式,来源于 Erich Gamma、 Richard Helm、 Ralph Johnson 和 John Vlissides(Gang of Four) 编著的《设计模式:可复用面向对象软件的基础》,该书提出了23种软件设计模式(Design Pattern),作为面向对象软件设计的最佳实践,广为人知。

23种设计模式如下:

创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”

– 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。

结构型模式:用于描述如何将类或对象按某种布局组成更大的结构

– 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责

– 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

面向对象设计原则

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据一些原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。

原则 定义 提出者 目的
开闭原则 软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification) 勃兰特·梅耶(Bertrand Meyer) 降低维护带来的新风险
里氏替换原则 继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects) 里斯科夫(Liskov) 防止继承泛滥
依赖倒置原则 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions) 罗伯特·马丁(Robert C.Martin) 更利于代码结构的升级扩展
单一职责原则 单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change) 罗伯特·C.马丁(Robert C. Martin) 便于理解,提高代码的可读性
接口隔离原则 一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface) 罗伯特·C.马丁(Robert C. Martin) 功能解耦,高聚合、低耦合
迪米特法则(最少知道原则) 只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers) 伊恩·荷兰(Ian Holland) 减少代码臃肿

单例模式

单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。

单例模式有 3 个特点:

  • 单例类只有一个实例对象;
  • 该单例对象必须由单例类自行创建;
  • 单例类对外提供一个访问该单例的全局访问点。
1
2
3
4
5
6
7
8
function Singleton() {}
Singleton.getInstance = function() {
if (!this.instance) {
this.instance = new Singleton();
}
return this.instance;
};
// let singleton = Singleton.getInstance();
1
2
3
4
5
6
7
8
9
10
11
function Singleton() {}
Singleton.getInstance = (function() {
let instance = null;
return function () {
if (!instance) {
instance = new Singleton();
}
return instance;
};
})();
// let singleton = Singleton.getInstance();
1
2
3
4
5
6
7
8
9
10
let Singleton = (function() {
let instance = null;
return function() {
if (!instance) {
instance = this;
}
return instance;
}
})();
// let singleton = new Singleton();

原型模式

原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。

1
2
3
function Prototype() {}
let instance = Object.create(Prototype);
// Object.getPrototypeOf(instance);
1
2
3
4
function Prototype() {}
let instance = {};
Object.setPrototypeOf(instance, Prototype);
// Object.getPrototypeOf(instance);
1
2
3
4
5
6
function Prototype() {}
function Instance() {}
Instance.prototype = Prototype;
Instance.prototype.constructor = Instance;
let instance = new Instance();
// Object.getPrototypeOf(instance);

工厂模式

工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。

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
function Instance1() {}
function Instance2() {}
function Instance1Factory() {
this.create = function() {
return new Instance1();
}
}
function Instance2Factory() {
this.create = function() {
return new Instance2();
}
}
function InstanceFactory(type) {
this.create = function() {
let factory = null;
switch(type) {
case 1:
factory = new Instance1Factory();
break;
case 2:
factory = new Instance2Factory();
break;
}
factory.create();
}
}
// let instance1Factory = new InstanceFactory(1);
// let instance1 = instance1Factory.create();

抽象工厂模式

抽象工厂(Abstract Factory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。

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
function Instance1() {}
function Instance2() {}
function Instance1Factory() {
this.create = function() {
return new Instance1();
}
}
function Instance2Factory() {
this.create = function() {
return new Instance2();
}
}
function InstanceFactory() {
this.create = function(type) {
switch(type) {
case 1:
return new Instance1Factory().create();
break;
case 2:
return new Instance2Factory().create();
break;
}
}
}
// let factory = new InstanceFactory();
// let instance1 = factory.create(1);

建造者模式

建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

代理模式

代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Instance() {
this.method = function() {}
}
function InstanceProxy() {
// proxy could do some extra things
this.instance = new Instance();
// proxy could do some extra things

this.method = function() {
// proxy could do some extra things
this.instance.method();
// proxy could do some extra things
}
}
// let proxy = new InstanceProxy();
// let result = proxy.method();

适配器模式

适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Instance1() {
this.method1 = function() {}
}
function Instance2() {
this.method2 = function() {}
}
function InstanceAdapter(type) {
this.instance = new type();
this.adapterMethod = function() {
(this.instance.method1 ? this.instance.method1 : this.instance.method2)(...arguments);
}
}
// let adapter1 = new InstanceAdapter(Instance1);
// let adapter2 = new InstanceAdapter(Instance2);
// let result1 = adapter1.adaperMethod();
// let result2 = adapter2.adaperMethod();

桥接模式

桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

装饰模式

装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Instance() {}
Instance.prototype.method = function() {
console.log('method1');
}
function Decorator(instance) {
this.instance = instance;
}
Decorator.prototype.method = function() {
this.instance.method();
console.log('method2');
}
var instance = new Instance();
var instanceDecorated = new Decorator(instance);

// instance.method();
// instanceDecorated.method();
使用 @decorator 语法
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
interface TypedPropertyDescriptor<T> {
enumerable?: boolean;
configurable?: boolean;
writable?: boolean;
value?: T;
get?: () => T;
set?: (value: T) => void;
}
function decorator(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor<T>) : TypedPropertyDescriptor<T> | void {
var fn = descriptor.value;
descriptor.value = function(...args: any[]) {
try {
return fn.apply(this, args);
} finally {
console.log('method2');
}
}
return descriptor;
}

class Instance {
@decorator
method() {
console.log('method1');
}
}

// var instance = new Instance();
// instance.method();

外观模式

外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。

1
2
3
4
5
6
7
8
9
function method1() {}
function method2() {}
function method3() {}
function facadeMethod() {
method1();
method2();
method3();
}
// facadeMethod();

享元模式

享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。

组合模式

组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。

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
function Composite(name) {
this.name = name;
this.parent = null;
this.children = [];
}
Composite.prototype = {
add: function(child) {
child.parent = this;
this.children.push(child);
return this;
},
action: function() {
if (this.children.length > 0) {
for (var i = 0; i < this.children.length; ++i) {
this.children[i].action();
}
} else {
console.log(this.name);
// do something
}
}
}
var root = new Composite('root');
var leaf1 = new Composite('leaf1');
var leaf2 = new Composite('leaf2');
var leaf3 = new Composite('leaf3');
var leaf4 = new Composite('leaf4');
var leaf5 = new Composite('leaf5');
var leaf6 = new Composite('leaf6');
var leaf7 = new Composite('leaf7');
root.add(leaf1).add(leaf2).add(leaf3).add(leaf4);
leaf3.add(leaf5).add(leaf6).add(leaf7);

// root.action();

模版方法模式

模板方法(Template Method)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

策略模式

策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var strategy1 = function(val) {
return val * 1
}

var strategy1 = function(val) {
return val * 2
}

var strategy1 = function (val) {
return val * 3
}

var executor = function(fn, val) {
return fn(val);
}

// var params = {}; // strategy parameters
// executor(strategy1, params);
// executor(strategy2, params);
// executor(strategy3, params);

命令模式

命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。

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
function Command() {
// 命令栈
this.stack = [];
// 栈指针位置
this.stackPosition = -1;
}
Command.prototype = {
// 执行
execute: function(fn) {
this._clearRedo();
var command = fn.bind(this);

// 执行并缓存起来
command();

this.stack.push(command);
this.stackPosition++;
},
canUndo: function() {
return this.stackPosition >= 0;
},
canRedo: function() {
return this.stackPosition < this.stack.length - 1;
},
// 撤销
undo: function(undoFn) {
if (!this.canUndo()) {
return;
}

this.stackPosition--;

// 命令的撤销,与执行的处理相反
undoFn && undoFn.call(this);
},
// 重做
redo: function() {
if (!this.canRedo()) {
return;
}

// 执行栈顶的命令
this.stack[++this.stackPosition]();
},
// 在执行时,已经撤销的部分不能再重做
_clearRedo: function() {
this.stack = this.stack.slice(0, this.stackPosition + 1);
}
}
// var commandar = new Command();
// commandar.execute(function step1(){console.log('command1')});
// commandar.execute(function step2(){console.log('command2')});
// commandar.undo(function undoStep2(){console.log('command3')});
// commandar.redo();

职责链模式

职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。

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
function ChainItem(fn) {
this.fn = fn;
this.next = null;
}
ChainItem.prototype = {

setNext: function(next) {
this.next = next;
return next;
},

start: function() {
this.fn.apply(this, arguments);
},

toNext: function() {
if (this.next) {
this.start.apply(this.next, arguments);
} else {
console.log('There is no more chain.');
}
}
};
function action1(input) {
if (/* match action conditions */) {
// do action
}
this.toNext(input);
}
function action2(input) {
if (/* match action conditions */) {
// do action
}
this.toNext(input);
}
function action3(input) {
if (/* match action conditions */) {
// do action
}
this.toNext(input);
}
var chain = new ChainItem(action1);
var action2ChainItem = new ChainItem(action2);
var action3ChainItem = new ChainItem(action3);
chain.setNext(action2ChainItem).setNext(action3ChainItem);

// chain.start();

状态模式

状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。

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
function Instance() {
Object.defineProperty(this, 'state', {
set: function(value) {
this._state = value;
let statusActionFunc = this['_' + value + 'Action'];
statusActionFunc && statusActionFunc.call(this);
},
get: function() {
return this._state;
},
enumerable: false
});
this.init();
}
Instance.prototype.init = function() {
this.state = 'status1';
}
Instance.prototype.changeState = function() {
this.state = this.state == 'status1' ? 'status2' : 'status1';
}
Instance.prototype._status1Action = function () {
console.log(this._state);
}
Instance.prototype._status2Action = function () {
console.log(this._state);
}
// var instance = new Instance();
// instance.changeState();
// instance.changeState();

观察者模式(发布-订阅模式)

观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Observer() {
this.eventTypeObj = {};
}
Observer.prototype.on = function(eventType, fn) {
if (!this.eventTypeObj[eventType]) {
// 按照不同的订阅事件类型,存储不同的订阅回调
this.eventTypeObj[eventType] = [];
}
this.eventTypeObj[eventType].push(fn);
}
Observer.prototype.emit = function() {
// 可以理解为arguments借用shift方法
var eventType = Array.prototype.shift.call(arguments);
var eventList = this.eventTypeObj[eventType];
for (var i = 0; i < eventList.length; i++) {
eventList[i].apply(eventList[i], arguments);
}
}

// var observer = new Observer();
// observer.on('event', function(params) {
// // handle function
// };
// observer.emit('event', {/* params */});

中介者模式

中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var instance1 = {
value: 1,
rank: rankMediator
}
var instance2 = {
value: 2,
rank: rankMediator
}
var instance3 = {
value: 3,
rank: rankMediator
}
function rankMediator() {
var ranks = [instance1.value, instance2.value, instance3.value].sort(function(a, b) {
return a - b;
});
return ranks.indexOf(this.value) + 1;
}

// instance1.rank(); // 1
// instance1.value = 100;
// isntance1.rank(); // 3

迭代器模式

迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

JavaScript中的有序数据集合有 Array,Map,Set,String,typeArray,arguments,NodeList

以上有序数据集合都部署了 Symbol.iterator 属性,属性值为一个函数,执行这个函数,返回一个迭代器,迭代器部署了 next 方法,调用迭代器的 next 方法可以按顺序访问子元素。同时,任何部署了 Symbol.iterator 接口的数据都可以使用 for…of 循环遍历

1
2
3
4
5
6
7
8
9
10
11
12
var arr = [1, 2, 3, 4];
var iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

for (item of arr) {
console.log(item);
}

访问者模式

访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。

备忘录模式

备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

解释器模式

解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。