设计模式

103 阅读2分钟

单例模式

优势和特点:单例模式主要解决的问题就是节约资源,保持访问一致性。保证一个类只有一个实例,并提供一个访问它的全局访问点。

简单实现(闭包)


const Person = (() => {
    class Person {
        constructor(name, age) {
            this.name = name
            this.age = age
        }
        intro() {
            console.log(`my name is ${this.name}`);
        }
    }
    let instance = null
    // 有实例就返回,无则创建
    return function singleTon(...args) {
        if (!instance) instance = new Person(...args)
        return instance
    }
})()
 const p1 = new Person("zs", 19)
 const p2 = new Person("ls", 20)
 console.log(p1 === p2); //true
 p1.intro() //my name is zs
 p2.intro() //my name is zs

应用举例

  • element-ui中Loading,当以服务的方式调用的全屏 Loading 是单例的:若在前一个全屏 Loading 关闭前再次调用全屏 Loading,并不会创建一个新的 Loading 实例,而是返回现有全屏 Loading 的实例:
let loadingInstance1 = Loading.service({ fullscreen: true }); 
let loadingInstance2 = Loading.service({ fullscreen: true }); 
console.log(loadingInstance1 === loadingInstance2); // true
  • vuex 使用单一状态树,实现了一个全局的 Store 用于存储应用的所有状态,
import Vue from 'vue'
import Vuex from 'vuex'

// Vuex在内部实现了一个 install 方法,这个方法会在插件安装时被调用, 可以保证一个 Vue 实例(即一个 Vue 应用)只会被 install 一次 Vuex 插件,所以每个 Vue 实例只会拥有一个全局的 Store。
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
new Vue({
  el: '#app',
  store: store,
})

工厂模式

优势和特点:外部不许关心内部构造器是怎么生成的,只需调用一个工厂方法生成一个实例即可。传入不同的参数,获取对应的功能。

简单实现(switch case)

class User {
  constructor(name = '', viewPage = []) {
    this.name = name;
    this.viewPage = viewPage;
  }
}

class UserFactory extends User {
  constructor(name, viewPage) {
    super(name, viewPage)
  }
  create(role) {
    switch (role) {
      case 'superAdmin': 
        return new UserFactory( '超级管理员', ['首页', '通讯录', '发现页', '应用数据', '权限管理'] );
        break;
      case 'admin':
        return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] );
        break;
      case 'user':
        return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] );
        break;
      default:
        throw new Error('参数错误, 可选参数:superAdmin、admin、user')
    }
  }
}

let userFactory = new UserFactory();
let superAdmin = userFactory.create('superAdmin');
let admin = userFactory.create('admin');
let user = userFactory.create('user');

应用举例

  • vue-router
export default class VueRouter {
    constructor({mode, base} = {}) {
        switch (mode) {
            case 'history':
                this.history = new HTML5History(this, base)
                break
            case 'hash':
                this.history = new HashHistory(this, base, this.fallback)
                break
            default:
        }
    }
}

策略模式

优势和特点:策略模式根据传入的参数,来调整采用对应的算法。

应用举例

  • element-ui中的表单项校验
// 1.src/utils/validates.js  
// 姓名校验 由2-10位汉字组成   
export function validateUsername(str) {  
    const reg = /^[\u4e00-\u9fa5]{2,10}$/  
    return reg.test(str)  
}  
// 手机号校验 由以1开头的11位数字组成    
export function validateMobile(str) {  
    const reg = /^1\d{10}$/  
    return reg.test(str)  
}  
// 邮箱校验   
export function validateEmail(str) {  
    const reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/  
    return reg.test(str)  
}


// 2.src/utils/index.js  
import * as Validates from './validates.js'  
// 生成表格自定义校验函数   
export const formValidateGene = (key, msg) => (rule, value, cb) => {  
    if (Validates[key](value)) {  
        cb()  
    } else {  
        cb(new Error(msg))  
    }  
}

// 3.组件中使用
<template>  
    <el-form ref="ruleForm"   
             :rules="rules"  
             :model="ruleForm">  
          
        <el-form-item label="用户名" prop="username">  
            <el-input v-model="ruleForm.username"></el-input>  
        </el-form-item>  
          
        <el-form-item label="手机号" prop="mobile">  
            <el-input v-model="ruleForm.mobile"></el-input>  
        </el-form-item>  
          
        <el-form-item label="邮箱" prop="email">  
            <el-input v-model="ruleForm.email"></el-input>  
        </el-form-item>  
    </el-form>  
</template>  
<script type='text/javascript'>  
import * as Utils from '../utils'  

export default {  
name'ElTableDemo',  
data() {  
    return {  
        ruleForm: { pass''checkPass''age'' },  
        rules: {  
            username: [{  
                validatorUtils.formValidateGene('validateUsername''姓名由2-10位汉字组成'),  
                trigger'blur'  
            }],  
            mobile: [{  
                validatorUtils.formValidateGene('validateMobile''手机号由以1开头的11位数字组成'),  
                trigger'blur'  
            }],  
            email: [{  
                validatorUtils.formValidateGene('validateEmail''不是正确的邮箱格式'),  
                trigger'blur'  
            }]  
        }  
    }  
}  
}  
</script>

发布订阅模式

优势和特点:一个地方触发,能通知多个订阅者

简单实现, juejin.cn/post/713768…

应用举例

  • EventBus
// event-bus.js  
import Vue from 'vue'  
export const EventBus = new Vue()

// 监听方
import { EventBus } from './event-bus.js'
EventBus.$on('addition'param => { })
 
 // 发送方
 import {EventBusfrom './event-bus.js' // 引入事件中心
 EventBus.$emit('addition', { num:123 })

拦截器模式

优势和特点:入口或出口地方进行拦截,集中式处理

应用举例

  • axios请求响应拦截器
  • Vue Router 导航守卫

装饰器模式

优势和特点:辅助功能和业务功能分离。

应用举例

  • 防抖节流装饰器
// @/decorator/index.js
import { throttle, debounce } from 'lodash'

export const Throttle =  function(wait, options = {}) {
  return function(target, name, descriptor) {
    descriptor.value = throttle(descriptor.value, wait, options)
  }
}
export const Debounce = function(wait, options = {}) {
  return function(target, name, descriptor) {
    descriptor.value = debounce(descriptor.value, wait, options)
  }
}

//在组件中使用
import { Debounce } from '@/decorator' 
export default { 
  methods:{ 
    // 这样resize方法就自带有防抖功能了
    @Debounce(100) 
    resize(){} 
  }
}
  • form表单的校验
// @/utils/decorator
export function Validate(refName) {
  return function (target, name, descriptor) {
    const fn = target[name]; // 被装饰的方法
    descriptor.value = function (...args) {
      // 将触发校验的代码封装在此
      this.$refs[refName].validate((valid) => {
        if (valid) {
          fn.call(this, ...args); // 在这里调用“被装饰的方法”
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    };
  };
}

//在组件中使用
import { Validate } from '@/utils/decorator'
export default { 
 methods:{ 
  // 这样就会自动校验表单,校验成功才会执行submitForm中的代码了
  @Validate('formName')
  async submitForm() {
    await this.saveRequest();
  }
  
 }
}
  • 删除业务的确认框(点击删除按钮的时候,一般都需要弹出一个提示框让用户确认是否删除)