优秀的代码=优秀的数据结构+优秀的设计模式+优秀的算法
这里的设计模式指的是代码里多种基本设计模式的巧妙组合使用。
设计模式的出现,即是以提高代码质量为标准,实现高内聚,低耦合,以及稳定高效的扩展。
下面就日常开发中常用的设计模式进行说明:
- 单例模式
- 工厂模式
- 构造函数模式
- 观察者模式
- 代理模式Proxy
- 中介模式
- 访问者模式Vistor
- 微服务模式
单例模式
单例模式保证上下文只有唯一实例,一般放置于全局缓存,比如常见的全局变量window,document等。
采用闭包的方式实现如下:
const single=(function(){
let instance;
function getInstance(){
//如果该实例存在,则直接返回,否则就实例化
if(!instance){
instance=new Conf()
}
return instance
}
function Conf(){
//此处省略生成单例的对应构造函数代码
}
return instance
})()
工厂模式
工厂模式本质是一个负责生产对象实例的工厂,根据抽象程度的不同,可以分为:简单工厂、工厂方法和抽象工厂。
简单工厂:创建工厂类,根据传入的参数来决定创建具体的类实例。
工厂方法:比简单工厂多了一个角色————工厂接口(interface),它负责定义所有类实例的公共接口,然后每个具体的类实例分别实现对应的这些公共接口。
抽象工厂:是对工厂方法的升级,用于创建一组相互关联或相互依赖的类。
构造函数模式
构造函数模式通过new关键字来调用定义的构造函数,创建新的实例对象,这种方式属于继承式创建。而由于js本身采用原型继承的设计方式,所以构造函数一般采用组合式或寄生式等继承方式变相实现。
观察者模式
也称发布订阅模式,是众多框架中都会引入实现的最常见模式之一,例如Node中的Event模块,Vue中的依赖收集触发,Mq等等很多场景中都会涉及,它的优点在于可以分解多模块多层次间的耦合性,让模块间的通信变得更容易更简洁,让程序代码的实现以及扩展更容易。
这种模式下,一个订阅者订阅发布者,当该特定事件发生时,发布者会通知所有订阅者的触发执行。
还有我们传统的DOM事件绑定就是一种发布订阅模式。
// 订阅
document.body.addEventListener('click', function() {
console.log('click1');
}, false);
document.body.addEventListener('click', function() {
console.log('click2');
}, false);
// 发布
document.body.click(); // click1 click2
//IE下是dispatchEvent
代理模式Proxy
代理是一个对象,它用来控制对本地对象的访问,它与本体对象实现了同样的接口,代理对象会把所有的调用方法传递给本体对象。
代理模式最基本的形式是对访问进行控制,本地对象注重去执行自身的逻辑,代理对象则控制本地对象何时被实例化,何时被调用。
常见代理模式应用场景,如ES6的Proxy,HOC(高阶函数)反向代理,Nginx反向代理,Java出名的依赖注入(反射)思想等等。
代理的优点:
1.代理对象可以代替本体被实例化,并使其可以被远程访问;
2.代理还可以把本体实例化推迟执行,可以很好的控制访问以及实现对本体实例功能的兼容扩展。
下面来使用虚拟图片代理实际图片预加载(懒加载)来说明代理模式的使用:
在web开发中,图片预加载是比较常见的,一般我们采用在图片未加载完之前使用loading图来作为占位符,来提示用户图片正在加载,等图片加载完后再替换。
var myImage=(function(){
var imgNode=document.createElement('img')
document.body.appendChild(imgNode)
return {
setSrc(src){
imgNode.src=src
}
}
})()
//代理模式
var ProxyImage=(function(){
var img=new Image()
img.onload=function(){
myImage.setSrc(this.src)
}
return {
setSrc(src){
myImage.setSrc('loading.gif') //占位图
img.src=src
}
}
})()
//调用方式
ProxyImage.setSrc('real.png')
代理模式的设计可以很灵活,也可以尝试多层代理来将很多情况下很复杂的业务逻辑拆分成各个独立的业务逻辑块,然后自由组合成中间层的代理模式,对外暴露最顶层的代理,这样对业务的扩展以及防止外部侵入等都是很好的技术方案。
中介模式
中介模式一如房屋中介,它在租房者与房东间形成了一条关系链。租房者并不关心租谁的房,房东也不关心它租给谁,它们分别委托给中介即可。
中介模式的作用是解除对象与对象之间的耦合关系,所有相关对象都通过中介者对象来通信,中介者可以独立的改变它们之间的交互,染个所有相关对象除了本身操作外,只与中介者产生关系,从而可以是原本比较复杂的网状关系结构变成比较相对清晰的星型结构。
访问者模式Vistor
访问者模式是一种将数据操作和数据结构分离的设计模式。
使用场景:
1.对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
模式中的角色介绍:
Element:所有具体元素的抽象类(父类),其定义了一个可接受访问者(accept)的方法,目的是每一个元素都可以被访问者vistor访问。
ElementA、ElementB、ElementC...等为具体的元素类,继承自Element。
Vistor:抽象的访问类,对每个Element具体类定义访问行为,参数为被访问元素,理论上方法个数与具体类的个数一致。
VistorA、VistorB、VistorC...等为具体的访问类,继承自Vistor,然后对应具体方法里实现各自对应具体类的访问逻辑代码。
微服务模式
微服务架构是后端复杂架构常用的设计模式之一,大前端方向也基本会有相同的概念设计,微服务架构模式主要就是将整个应用分散为多层级多服务,每个层级,每个服务都只专注自己的业务逻辑,向上提供单例模式的单例访问,无法跨层级非法入侵访问,而且将业务拆分细化多节点,减少容灾的几率风险。
微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
由此带来的变化是,这些前端应用可以独立运行、独立开发、独立部署。以及,它们应该可以在共享组件的同时进行并行开发——这些组件可以通过 NPM 或者 Git Tag、Git Submodule 来管理。
目前业界最好的前端微服务设计是阿里的qiankun,感兴趣的童鞋可以去看看。