什么是设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。本文将介绍七种最常见的设计模式:工厂模式、构造函数模式、模块模式、单例模式、混合模式、观察者模式、职责链模式。
常见的设计模式
1.工厂模式
顾名思义,工厂模式就是像工厂流水线一样生产对象,生产的每个对象都是完全相同的。工厂模式抽象了对象创建的过程,将其封装在函数内部,对外只提供创建对象的接口。具体过程为定义一个对象,为其设置属性和方法后返回该对象。下面给出一个demo:
function factory1(){
let obj=new Object();
obj.name='xxx';
obj.number='xxx';
obj.f=function(){
alert('function!');
}
return obj;
}
也可以直接用对象字面量的方式创建,如下:
function factory2(){
let obj={
name:'xxx',
number:'xxx',
f:function(){
alert('function!');
}
}
return obj;
}
可以看到在使用工厂模式的时候,只需要调用工厂函数创建而不需要关注内部实现,并且创建出来的所有对象都是完全相同的。
2.构造函数模式
构造函数模式是最常规的模式,在该模式下,我们将属性和方法绑定到this上,使用new来创建实例。下面给出一个demo:
function myConstructor(){
this.name='xxx';
this.number='xxx';
this.f=function(){
alert('function!');
}
}
也可以把属性和方法绑定到原型prototype上,如下:
function myConstructor(){
}
myConstructor.prototype.name='xxx';
myConstructor.prototype.number='xxx';
myConstructor.prototype.f=function(){
alert('function!');
}
3.模块模式
通过闭包,对外界提供接口,外界只能通过接口“受限制地”访问属性和方法。模块模式通过函数作用域解决了属性和方法的封装问题。Demo如下:
let myModule=(function(){
let name='xxx';
let number='xxx';
function f(){
alert('function!');
}
return{
name:name,
number:number,
f:f
}
})();
这样一来,外部只能通过访问myModule.name和myModule.number以及myModule.f访问属性和方法。模块模式的一般形式是:一个定义了私有变量和函数的函数,利用闭包创建合一访问私有变量和函数的特权函数,最后返回这个特权函数,或者把它们保存到一个可访问到的地方。
4.单例模式
单例模式保证一个类只有一个实例,并且提供一个访问它的全局访问点。有些时候一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。看下面的demo:
let Singleton=(function(){
let instance;
function init(){
return{
name:'xxx',
number:'xxx',
f:function(){
alert('function!');
}
}
}
return{
function getInstance(){
if(!instance){
instance=init();
}
return instance;
}
}
})();
上面的代码创建了一个单例对象,当该对象已经实例化时返回该对象实例。如果没被实例化则新建一个对象实例并返回。
5.混合模式
混合模式就是结合原型模式和构造函数模式。通过构造函数继承属性,通过原型继承方法。
function Mixin(){
this.name='xxx';
this.number='xxx';
};
Mixin.prototype.f=function(){
alert('function!');
}
function subMixin(){
//继承Mixin的属性
Mixin.call(this);
}
function subCreate(prototype){
function F(){};
F.prototype = prototype;
return new F();
}
//让subMixin的原型指向一个对象,该对象的原型指向了Mixin.prototype,通过这种方式继承Mixin的方法。
subMixin.prototype=subCreate(Mixin.prototype);
6.观察者模式
观察者模式,又叫订阅-发布者模式。为了便于理解,通过微信公众号的例子来看。我们订阅(关注)某微信公众号,当公众号发布消息时,每个订阅该公众号的人都能收到。观察者模式就是将这个过程抽象成一种设计模式。看一个demo:
let EventCenter = (function(){
//事件数组(二维)
var events = {};
//给事件绑定事件处理程序(回调函数)
function on(event, handler){
events[event] = events[event] || [];
events[event].push({
handler: handler
});
}
//执行某事件的事件处理程序
function fire(event, args){
if (!events[event]) {return}
for (let i = 0; i < events[event].length; i++) {
events[event][i].handler(args);
}
}
//删除某事件的事件处理程序
function off(event){
delete events[event];
}
//以模块模式的方式,向外界提供绑定事件处理程序、触发事件和删除事件处理程序的接口。
return {
on: on,
fire: fire,
off: off
}
})();
//具体应用
EventCenter.on('change', function(val){
console.log('change... now val is ' + val);
});
EventCenter.on('click', function(val){
console.log('click.... now val is '+ val);
})
EventCenter.fire('change', 'xin');
EventCenter.fire('click', 'xin');
EventCenter.off('change');
7.职责链模式
职责链模式使多个函数对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些函数对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。这样看比较抽象,考虑这样一个场景:如果满足条件1则xxx,如果不满足条件1但满足条件2则xxx,如果不满足条件1和2而满足条件3则xxx。写成代码则是:
let process=function(args){
if(condition1){
someHandle;
}else if(condition2){
someHandle;
}else if(condition3){
someHandle;
}
}
process(args);//调用执行
按照职责链模式改写后将会变成如下代码:
function p1(args){
if(condition1){
someHandle;
}
else{
p2(args);
}
}
function p2(args){
if(condition2){
someHandle;
}
else{
p3(args);
}
}
function p3(args){
someHandle;
}
p1(args);//调用执行
执行结果和前面那个大的process函数完全一样,但是代码的结构已经清晰了很多,我们把一个大函数拆分了三个小函数,去掉了许多嵌套的条件分支语句,这也就是职责链模式的优点所在。
如有错误还请大牛指正,如有不同意见也欢迎质疑,希望对大家有所帮助,也希望同样在准备春招的朋友一切顺利。