开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情
前言
设计模式是一种编程思想,并不局限于某种特定的语言,正确恰当的使用设计模式,能够是程序更加健壮和易维护。
如果文章存在错误的地方,请批评指正。期待评论讨论,一起学习
单例模式
定义: 保证一个类仅有一个实例,并提供一个访问他的全局访问点。
实现单例模式
要实现一个单例模式并不复杂,使用一个变量来标记某一个类是否已经实例化,如果已经实例化,则直接返回之前创建的对象
// es6 class 实现
class Singleton{
private static instance = null
public static getInstance(){
if(!instance){
instance = new Singleton()
}
return instance
}
}
// 使用 函数实现
const Singleton1 = function(){
//
}
Singleton1.instance = null
Singleton1.getInstance = function(){
if(!this.instance){
this.instance = new Singleton1
}
return this.instance
}
使用上面的方式存在一个问题,就是在使用这个类时,需要提前知道这是一个单例类,然后通过调用 getInstance 方法来获取单例。也就是说这个类是 不透明的
透明的单例模式
我们需要实现一个透明的单例类,在使用这个类时,可以和正常类一样使用 new 关键字来进行实例化
// 使用闭包的方式,在闭包中返回一个真正的构造方法
const Singleton = (function(){
let instance = null
let Singleton = function(){
if(instance){
// 返回已经存在的实例
return instance
}
instance = this
return instance
}
})
tips: 在使用 new 关键字创建对象时,如果构造函数中显示返回了一个对象,则最终创建的对象为显示返回的这个对象,否则为构造函数内部的 this 对象。
上面使用闭包的方式实现的单例模式,解决了不透明的问题,使得我们在使用时,可以想平常的类一样使用 new 关键字去实例化。在实际开发过程中,我们需要尽可能的将同步的部分抽离出来,对于上面的例子,我们可以将创建对象的部分和管理单例的部分进行拆分,来了解一下 使用代理实现单例模式
使用代理实现单例模式
我们将上面构造函数提取出来
function LoginLayout(){
// 登陆窗口构造方法
}
const ProxySingletonLoginLayout = function(){
// 对登陆窗口类进行代理,实现单例模式, 这样不会影响 LoginLayout 本身的功能
let instance = null
if {!instance}{
instance = new LoginLayout()
}
return instance
}
策略模式
定义一些列的算法,一个个进行封装,并且使它们可以相互替换
策略模式的使用
实用策略模式实现表单校验
将表单校验逻辑封装成策略对象
const strategies = {
isNonEmpty: function (value, errorMsg) { // 不为空的校验规则
if (value === '') {
return errorMsg
}
},
minLength: function (value, length, errorMsg) { // 最小程度校验规则
if (value.length < length) {
return errorMsg
}
},
isMobile: function (value, errorMsg) { // 手机号格式校验
if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
return errorMsg;
}
}
}
const Validator = function () {
this.cache = []
}
/**
* @Params dom 需要校验规则的 dom 元素
* @params rule {string} 校验规则名称和需要的参数,格式为 rule:arg:arg
* @params errorMsg {string} 错误信息
*/
Validator.prototype.add = function (dom, rule: string, errorMsg: string) {
let ruleArgs = rule.split(':') // 将校验规则和参数分开
this.cache.push(function () {
const strategy = ruleArgs.shift() // 获取校验规则名称
ruleArgs.unshift(dom.value) // 将 input 元素的 value 值添加到参数数组中
ruleArgs.push(errorMsg) // 将错误信息添加到参数数组中
return strategies[strategy].apply(dom, ruleArgs) // 执行校验规则
})
}
Validator.prototype.start = function () {
for (let i = 0, validatorFun; validatorFun = this.cache[i++];) {
const msg = validatorFun()
if (msg) {
return msg
}
}
}
上面实现了一个 Validator 类,该类作为 Context , 负责接收用户的请求并委托为 strategy 对象。接下来实用一下 Validator 类来进行表单校验
const validatorFun = function () {
const validator = new Validator()
/***** 为当前表单添加校验规则 */
validator.add(registerForm.username, 'isNonEmpty', '用户名不能为空')
validator.add(registerForm.passcode, 'minLength:8', '密码长度不能小于 8 位')
const errorMsg = validator.start() // 校验表单并获取结果
return errorMsg;
}
在表单提交之前,执行 validatorFun 方法,进行表单校验,如果 errorMsg 不为假,则显示相应的错误信息并阻止表单提交。
策略模式的优缺点
- 策略模式利用组合、委托和多态等技术和思想,可以有效的避免多重条件选择语句
- 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的 strategy 中,使的它们易于切换。易于理解,易于扩展。
- 策略模式中的算法可以复用在系统的其他地方,从而避免了许多重复工作
- 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力。