前端开发中常用的几种设计模式
设计模式概览
设计模式是对软件设计开发过程中反复出现的某类问题的通用解决方案。设计模式更多的是指导思想和方法论,而不是现成的代码,当然每种设计模式都有每种语言中的具体实现方式。学习设计模式更多的是理解各种模式的内在思想和解决的问题,毕竟这是前人无数经验总结成的最佳实践,而代码实现则是对加深理解的辅助。
设计模式可以分为三大类:
- 结构型模式(Structural Patterns): 通过识别系统中组件间的简单关系来简化系统的设计。
- 创建型模式(Creational Patterns): 处理对象的创建,根据实际情况使用合适的方式创建对象。常规的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
- 行为型模式(Behavioral Patterns):用于识别对象之间常见的交互模式并加以实现,如此,增加了这些交互的灵活性。
上述中一共有23种设计模式,但我们作为前端开发人员,需要了解的大概有以下10种。
前端需要了解的设计模式(10种)
创建型模式
故名思意,这些模式都是用来创建实例对象的。
1. 工厂模式
我们从简单的开始。 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
上图为例,我们构造一个简单的汽车工厂来生产汽车:
使用一下我们的工厂:
输出结果:
使用工厂模式之后,不再需要重复引入一个个构造函数,只需要引入工厂对象就可以方便的创建各类对象。
2. 单例模式
首先我们需要理解什么是单例?
单:指的是一个。
例:指的是创建的实例。
单例:指的是创建的总是同一个实例。也就是使用类创建的实例始终是相同的。
先看下面的一段代码:
上面这段代码,定义了一个Person类,通过这个类创建了两个实例,我们可以看到最终这两个实例是不相等的。也就是说,通过同一个类得到的实例不是同一个(这本就是理所应当),但是如果我们想始终得到的是同一个实例,那么这就是单例模式。那么下面就该介绍如何实现单例模式了:
想要实现单例模式,我们需要注意两点:
- 需要使用return。使用new的时候如果没有手动设置return,那么会默认返回this。但是,我们这里要使得每次返回的实例相同,也就是需要手动控制创建的对象,因此这里需要使用return。
- 我们需要每次return的是同一个对象。也就是说实际上在第一次实例的时候,需要把这个实例保存起来。再下一个实例的时候,直接return这个保存的实例。因此,这里需要用到闭包了。
从上面的代码中,我们可以看到在闭包中,使用instance变量来保存创建的实例,每次返回的都是第一次创建的实例。这样的话就实现了无论创建多少次,创建的都是同一个实例,这就是单例模式。
3. 原型模式
通俗点讲就是创建一个共享的原型,并通过拷贝这些原型创建新的对象。
在我看来,其实原型模式就是指定新创建对象的模型,更通俗一点来说就是我想要新创建的对象的原型是我指定的对象。
最简单的原型模式的实现就是通过Object.create()。Object.create(),会使用现有的对象来提供新创建的对象的__proto__。例如下方代码:
另外,如果我们想要自己实现原型模式,而不是使用封装好的Object.create()函数,那么可以使用原型继承来实现:
原型模式就是创建一个指定原型的对象。如果我们需要重复创建某个对象,那么就可以使用原型模式来实现。
结构型模式
1. 装饰器模式
装饰器模式:为对象添加新功能,不改变其原有的结构和功能。
适配器模式是原有的不能用了,要重新封装接口。装饰器模式是原有的还能用,但是需要新增一些东西来完善这个功能。
比如手机壳,手机本身的功能不受影响,手机壳就是手机的装饰器模式。
输出结果:
如今都2021了,es7也应用广泛,我们在es7中这么写(ES7装饰器):
1、安装 yarn add babel-plugin-transform-decorators-legacy
2、新建.babelrc文件,进行下面的配置
3、上代码
打印出来了true,说明@testDec这个装饰器已经成功了,函数是个装饰器,用@testDec给Demo装饰了一遍。这个target其实就是class Demo,然后给她加一个isDec。
拆解后就是下面的内容:
装饰器参数的形式
验证是否是一个真正的装饰器模式需要验证以下几点:
2. 适配器模式
适配器模式:旧接口格式和使用者不兼容,中间加一个适配转换接口。
比如国外的插座跟国内的插座不一样,我们需要买个转换器去兼容。
上代码:
结果:
场景上可封装旧接口:
这个时候需要一个适配器:
3. 代理模式
代理模式:使用者无权访问目标对象,中间加代理,通过代理做授权和控制。
明星经纪人:比如有个演出,要请明星,要先联系经纪人。
或者理解为:为一个对象提供一个代用品或者占位符,以便控制对它的访问。例如图片懒加载、中介等。
虚拟代理用于图片的预加载
图片很大,页面加载时会空白,体验不好,所以我们需要个占位符,来短暂替代这个图片,等图片加载好了放上去。
ES6 Proxy
其实在ES6中,已经有了Proxy,这个内置的函数。我们来用一个例子来演示一下他的用法。这是一个明星代理的问题。
设计原则验证
代理类和目标类分离,隔离开目标类和使用者
符合开放封闭原则
行为型模式
1. 策略模式
策略模式是一种简单却常用的设计模式,它的应用场景非常广泛。我们先了解下策略模式的概念,再通过代码示例来更清晰的认识它。
策略模式由两部分构成:一部分是封装不同策略的策略组,另一部分是 Context。通过组合和委托来让 Context 拥有执行策略的能力,从而实现可复用、可扩展和可维护,并且避免大量复制粘贴的工作。
策略模式的典型应用场景是表单校验中,对于校验规则的封装。接下来我们就通过一个简单的例子具体了解一下:
从以上示例可以得出使用策略模式有以下优势:
- 方便在运行时切换算法和策略
- 代码更简洁,避免使用大量的条件判断
- 关注分离,每个strategy类控制自己的算法逻辑,strategy和其使用者之间也相互独立
2. 观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一或一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。典型代表vue/react等。
使用观察者模式的好处:
- 支持简单的广播通信,自动通知所有已经订阅过的对象。
- 目标对象与观察者存在的是动态关联,增加了灵活性。
- 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
addEventListener()
也是一种:
Target就是被观察对象Subject,listener就是观察者Observer。
观察者模式中Subject对象一般需要实现以下API:
subscribe()
: 接收一个观察者observer对象,使其订阅自己unsubscribe()
: 接收一个观察者observer对象,使其取消订阅自己fire()
: 触发事件,通知到所有观察者
用JavaScript手动实现观察者模式:
验证一下订阅是否成功:
输出:
验证一下取消订阅是否成功:
输出:
3. 迭代器模式
ES6中的迭代器
相信大家都不陌生,迭代器用于遍历容器(集合)并访问容器中的元素,而且无论容器的数据结构是什么(Array、Set、Map等),迭代器的接口都应该是一样的,都需要遵循 。迭代器模式解决了以下问题:
- 提供一致的遍历各种数据结构的方式,而不用了解数据的内部结构
- 提供遍历容器(集合)的能力而无需改变容器的接口
一个迭代器通常需要实现以下接口:
hasNext()
:判断迭代是否结束,返回Booleannext()
:查找并返回下一个元素
为Javascript的数组实现一个迭代器可以这么写:
验证一下迭代器:
输出:
ES6提供了更简单的迭代循环语法 for...of
,使用该语法的前提是操作对象需要实现 ,简单说就是该对象有个Key为 Symbol.iterator
的方法,该方法返回一个iterator对象。
比如我们实现一个 Range
类用于在某个数字区间进行迭代:
验证:
结果:
4. 状态模式
状态模式:一个对象有状态变化,每次状态变化都会触发一个逻辑,不能总是用if...else来控制。
比如红绿灯:
设计原则验证
将状态对象和主体对象分离,状态的变化逻辑单独处理
符合开放封闭原则
- 点赞
- 收藏
- 关注作者
评论(0)