天天看点

浅谈前端当中常见的设计模式

一、什么是设计模式

定义:设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。在面向对象软件设计过程中针对特定问题的优雅而简洁的解决方案。

设计模式遵循一下几种原则:

1、开闭原则:对扩展开放,对修改关闭

2、单一职责原则:一个类只负责一个功能领域中的相应职责

3、里氏代换原则:所有引用基类的地方必须能透明地使用其子类的对象

4、依赖倒转原则:依赖于抽象,不能依赖于具体实现

5、接口隔离原则:类之间的依赖关系应该建立在最小的接口上

6、合成/聚合复用原则:尽量使用合成/聚合,而不是通过继承达到复用的目的

7、最少知道原则:一个软件实体应当尽可能少的与其他实体发生相互作用

二、为什么要了解设计模式

设计模式说白了就是前辈们对自己代码经验的总结,目的是为了让我们在遇到类似或相同的问题的时候有经验可以参考或借鉴使用,使得代码可复用、更容易被他人理解。

1、设计模式可以让你知道在某些场景下如何设计出适合该场景的架子。如果只追求去实现一个功能,没有做其他的考虑,以后需要增加新功能或者做一些改动的时候,可能就会将代码改的面目全非,难以维护,不熟悉代码的人接手干活的话,会出现根本无从下手的问题;

2、如果想要提高自己的能力,去学习一些框架或者库的源码的时候,了解设计模式也有助于去理解源码当中应用到的一些设计思想,帮助自己更好的成长;

三、前端中常见的设计模式

设计模式按照分类分为三大类:创建型模式、结构型模式、行为型模式;总共是有23种设计模式。在这里我只想根据我做过的项目经验和自己学习设计模式过程中的一些理解来谈一谈前端中常见的几种设计模式。通过介绍主要思想结合案例的代码去和大家一起分享以下的几种设计模式。

1、工厂模式

什么是工厂模式?工厂模式是用来创建对象的一种最常用的设计模式。我们不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。说白了就是像工厂一样重复的产生类似的产品,工厂模式只需要我们传入正确的参数,就能生产类似的产品;

生活中的实例:我们去饭店吃饭,只需要知道菜名,饭店后厨就能够做出相应的菜,而不需要关注具体每道菜的材料、调料以及烹饪方法。

工厂模式的分类:工厂模式根据抽象程度的不同可以分为:

a、简单工厂

b、工厂方法

c、抽象工厂

工厂模式的优点:我们可以在不动之前原逻辑的基础上,继承和拓展新的功能,这样我们就可以提高效率,之前写好的功能可以复用,而且可以站在前人的肩膀上,不断拓展。

在实际的项目中,我们常常需要根据用户的权限来渲染不同的页面,高级权限的用户所拥有的页面有些是无法被低级权限的用户所查看。所以我们可以在不同权限等级用户的构造函数中保存该用户能够看到的页面,再根据权限实例化用户。

超级管理员:首页、活动页面、数据管理、权限管理
   管理员:首页、活动页面、数据管理
   普通用户:首页、活动页面           

function UserFactory(role) {

return new this[role]();

}

//工厂方法函数的原型中设置所有对象的构造函数

UserFactory.prototype = {

superAdmin: function () {

this.name = "超级管理员";
this.auth = ["首页", "活动", "数据管理", "权限管理"];           

},

admin: function () {

this.name = "管理员";
this.auth = ["首页", "活动", "数据管理"];           

user: function () {

this.name = "普通用户";
this.auth = ["首页", "活动"];           

vipUser: function () {

this.name = "超级心悦会员";
this.auth = ["首页", "活动", "数据管理", "权限管理", "心悦会员专享福利"];           

};

根据获取到的用户角色信息,直接通过工厂模式,new 一个UserFactory的实例出来,获取到对应的页面权限直接渲染展示出来。

2、单例模式

什么是单例模式?限制类实例化次数只能一次,一个类只有一个实例,并且提供可全局访问的点。

单例模式的实现:保证一个类只有一个实例, 一般先判断实例是否存在,如果存在直接返回, 不存在则先创建再返回,这样就可以保证一个类只有一个实例对象。

1、我们前端应用在使用websocket通信的时候,首先会创建一个websocket连接但是如果过多的websocket连接会占用大量的服务端的资源,因此一个页面不可能也没必要初始化多个相同接口的websocket连接 ,一个应用当中不管有多少页面,都可以共享同一个连接;

2、项目中引入第三方库时,重复多次加载库文件时,全局只会实例化一个库对象,如 jQuery,lodash,moment ..., 其实它们的实现理念也是单例模式;

3、当我们在逛购物网站的时候,我们可以在不同的产品页面,看到自己喜欢的商品后加入到购物车当中,此时购物车不会是多个重复的实例,只会生成一个实例;

3、发布-订阅模式

什么是发布-订阅模式?它定义了对象间的一种一对多的关系,让多个订阅者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。

生活中的案例:我想要买房子,我去我看中的楼盘的售楼处去询问,但是楼盘还没有开盘,销售人员让我关注他们楼盘的一个公众号,当楼盘开盘以后,这个公众号会发布一个通知,我们接收到这个通知以后,就可以去买房了。

这里我就是订阅者,销售就是发布者,当楼盘开盘的时候,销售人员在公众号上发布了一条消息,我接受到了这个消息,我就可以过去买房。有很多人和我一样都想要买这个楼盘的房子,都关注了这个公众号,但是每个人接收到楼盘开盘的消息以后具体做什么,有的人去买房了,有的人不想买了,销售人员管不了,他只管在公众号上发布消息;

什么时候使用发布-订阅模式:

a、各模块相互独立

b、存在一对多的依赖关系

c、依赖模块不稳定、依赖关系不稳定

d、各模块由不同的人员、团队开发

发布-订阅模式的优点

a、发布-订阅模式广泛应用于异步编程之中,是一种替代回调函数的方案.多个事件处理函数可以订阅同一个事件,当该事件发生后,与其相对应的多个事件处理函数都会运行。

b、一个对象不用再显示的调用另外一个对象的某个接口,降低模块之间的耦合程度,虽然不清楚彼此的细节,但是不影响他们之间相互通信

1、dom事件

对一个dom节点的事件进行监听,当操作dom节点时,触发相应的事件,相应的函数执     行。事件函数对dom节点完全未知,不用去理会是什么事件,如何触发,执行就好。
    document.body.addEventListener(“click”,function() {…})
   2、自定义事件
    假如小明想去看周杰伦的演唱会,但是周杰伦演唱会的门票太火爆,一开始买票瞬间就被抢光了。但是有些人可能只是抢到票了,最终没有去付款,这样的话在一定时间没有付款的话,订单失效,票就会释放出来供大家去抢。所以我在抢票的网站订阅了演唱会余票提醒,只要一有余票,就会通知小明去抢。           

4、装饰器模式

什么是装饰器模式?是为了给一个函数赋能,增强它的某种能力,它能动态的添加对象的行为,对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。

什么是装饰器模式:是为了给一个函数赋能,增强它的某种能力,它能动态的添加对象的行为,对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。

react当中的高阶函数就是装饰器模式的一个最常见的应用;

5、适配器模式

什么是适配器模式?适配器用来解决两个已有接口之间不匹配的问题,它并不需要考虑接口是如何实现,也不用考虑将来该如何修改;适配器不需要修改已有接口,就可以使他们协同工作;

适配器模式的优点:
   a、已有的功能如果只是接口不兼容,使用适配器适配已有功能,可以使原有逻辑得到更好的复用,有助于避免大规模改写现有代码;
   b、可扩展性良好,在实现适配器功能的时候,可以调用自己开发的功能,从而方便地扩展系统的功能;
   c、灵活性好,因为适配器并没有对原有对象的功能有所影响,如果不想使用适配器了,那么直接删掉即可,不会对使用原有对象的代码有影响;
   1、地图组件
  2、我们都知道很多UI组件或者工具库会按指定的数据格式进行渲染,但是这个时候    后端是不知道的;   所以可能接口出来的数据我们是不能直接正常的在页面上渲染的,    而此时老板催促我们赶紧上线,而后端坚持认为数据格式没问题,坚决不修改;这个时候我们可以通过适配器模式来前端格式化数据;           

四、总结和感想

设计模式并不是单一的某种方法也没有固定的适用场景。很多时候可能我们自己在写代码的时候可能已经在使用某种设计模式的思想但是我们自己却不知道。我觉得学习设计模式只是单纯的去看相关的书籍和博客并不能够深入的去理解,我觉得在学习的过程中结合自己的项目经验能够感悟到其中的精髓才能够掌握这个思想。设计的再厉害,性能很差,也是一个很差的设计。所以最适合的就是最好的。我希望大家都能够在实际的工作过程中不断的去思考和总结怎样写出更好的代码,在前端的道路上能够走的更远。

继续阅读