前端常用的设计模式

169 阅读3分钟

什么是设计模式

在我理解中,设计模式就是将一些良好的代码习惯总结起来,形成一套便于开发和维护的代码格式。没什么高大上的东西,大部分开发者日常都会用到各种各样的设计模式,只是自己没发现,例如单例模式、适配器模式、工厂模式等

  1. 单例模式
    单例模式简单来说,就是保证全局中只有一个实例,例如弹窗、不想暴露到全局的变量等。

        //1.单例模式
        class SingleCase{
          constructor() {
            if (!SingleCase.instance) {
              SingleCase.instance = this;
            }
            return SingleCase.instance;
          }
          getSingleCase(){
            return SingleCase.instance;
          }
          setAttr(attr,value){
            SingleCase.instance[attr] = value
          }
        }
    
  2. 策略模式
    如果你不想写一堆if/else这个模式是很适合你的,这个模式主要是解决if/else太多,导致代码太乱,不好阅读和维护的问题。

        //2.策略模式
        const strategy = {
          'a':  a(),
          'b':  b(),
          'c':  c()
        }
        function a(){console.log("这是策略a")}
        function b(){console.log("这是策略b")}
        function c(){console.log("这是策略c") }
        let condition = 'a'
        strategy[condition]
    

     对比if/else

        //对比if/else
        function fun1(type){
          if(type=='a'){
            console.log("这是策略a")
          }else if(type=='b'){
            console.log("这是策略b")
          }else if(type=='c'){
            console.log("这是策略c")
          }
        }
    

    其实现在分支还不算多,用if/else也问题不大,但是当分支很多的时候,假如你要改策略n的代码,你就要找到fun1里面n的逻辑进行修改,而策略模式只要修改方法n里面的内容,从编程来说,改的地方越小越好,出错的概率会越低。

  3. 适配器模式
    当你要用到多种引擎或者功能逻辑一样,但是实现不同的时候,例如点击A按钮会生成一个特定的图形并且发起请求,点击B按钮会生成一个特定的图形并且发起请求,这两个按钮实现的功能只是图形不一样,之前开发的某个系统的时候,需要能支持百度和高德地图,需要随时切换,当时就用了适配器模式。

        let aButton = {
          click:function(){
            //生成A图形
          },
          pushData:function(){
            //提交A数据
          }
        }
        let bButton = {
          click: function () {
            //生成B图形
          },
          pushData: function () {
            //生成B数据
          }
        }
        //3.适配器模式
        const funAdapter=(type)=>{
          let funObject = {
            a:aButton,
            b:bButton
          }
          return funObject[type]
        }
        //如果是A环境
        let controller = funAdapter("a")
        controller.click()
    

    当aButton和bButton的规模越大的时候,这个模式的优势越明显,而且逻辑判断的地方就只有适配器那里,逻辑和实现分开,修改起来会很容易

  4. 观察者模式
    前端常用的监听事件,就是用的观察者模式,其原理就是把监听时候的方法存起来,当触发事件的时候,调用那些方法。

        //观察者模式
        class Subject{
          constructor(){
            this.eventObject = {}
          }
          addEvent(type,func){
            this.eventObject[type]?this.eventObject[type].push(func):eventObject[type] = [func]
          }
          removeEvent(type, func) {
            this.eventObject[type] ? this.eventObject[type].forEach((item,index)=>{
              if(item===func){
                eventObject[type].splice(index,1)
              }
            }) : void(0)
          }
          triggerEvent(type){
             this.eventObject[type] ? this.eventObject[type].forEach((item, index) => {
              item()
            }) : void (0)
          }
        }
    
  5. 责任链模式
    责任链模式是指将处理的内容从一条链上往下传,直到有方法处理,才结束传播。我在使用责任链模式的时候,感觉是策略模式的改版,只不过是把对象改成数组。

        //策略模式
        function responsibilityModel(type,params){
          let funcList = [{
            condition:type==='a'&&params,
            func:()=>{
              console.log("type==='a'&&params",params)
            },
          }, {
              condition: type === 'a' && !params,
              func: () => {
                console.log("type==='a'&&!params")
              },
            }, {
              condition: type === 'b' && params,
              func: () => {
                console.log("type === 'b' && params", params)
              },
            }, {
              condition: type === 'b' && !params,
              func: () => {
                console.log("type === 'b' && !params")
              },
          }]
          for(let i =0;i<funcList.length;i++){
            if(funcList[i].condition){
              funcList[i].func();
            }
          }
        }
    

总结

当然还有很多其他的设计模式,例如原型链模式,工厂模式等,但是除了上面六个模式,别的我比较少用到,就不班门弄斧了。总的来说,这些模式只是帮我们更好的开发出可维护性和可阅读性强的代码,至于使不使用模式,使用什么模式就见仁见智了,目标在一百米不用非得交通工具,杀鸡也不一定用牛刀。