前端常用的设计模式剖析——单例、观察者、工厂、策略模式

什么是设计模式

有人说设计模式是能被反复使用、多数人知道的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码,让代码更容易被他人理解、保证代码的可靠性。

个人认为,设计模式其实就是前端工程化编程的一种思想,将代码 能够理解成更贴近生活的一种经验。年纪大了,就知道做什么,怎么做,怎么能做的更好,编程也一样,时间长了,就更明白什么需求应该采用什么方式编写,这种一贯的编写方式于是被总结为设计模式,我们现在用的这些模式,其实就是前辈的经验总结,能避免我们绕弯路,更好的编程。

设计模式有很多

  • 单体/单例模式
  • 观察者模式
  • 工厂模式
  • 策略模式
  • 模板模式
  • 代理模式
  • 外观模式

….
我们常用的也就是 单体模式、工厂模式、单例模式、观察者模式、策略模式,其他的可能没用到,或者无形中用到了但没注意是什么模式,不管是什么模式,能够被重用的,可维护的代码,都是好的程序

一、单体/单例模式

单例模式在js中我们随处都见,一个类只能保证有一个实例,例如对象字面量的方式创建一个单例,他可以定义很多的属性和方法,这个类也只有一个实例对象。优点,能够单独划分一个命名空间,避免和别人的内部变量发生冲突,所以单例可以分为简单的单例和闭包单例

  • 简单单例

       //先判断实例是否存在,存在则返回,不存在则创建,这样可以保证一个类只有一个实例对象
       var test_simple = test_simple || {
       name: 'alice',
       age: 15,
       gender: '2',
       sayName: function(){
           console.log('my name is ' + this.name)
       },
       sayAge: function(){
           console.log('i am '+ this.age + ' years old')
       }
    }
  • 闭包单例

       闭包的作用是保护一些私有属性,不让外界访问,只有return将属性暴露才能被外界访问
       var test_Closure = test_Closure || {
       introduce = (function(){
           var _name = 'bob',          //定义私有属性
           var _age = 18,
           var _gender = '1',
           var _sayName = function(){
               console.log('my name is ' + _name)
           },
           var _sayAge = function(){
               console.log('i am '+ _age + ' years old')
           }
           
           //将属性暴露 让别人看看
           return {
               name: _name,
               age : _age,
               gender : _gender,
               sayName : function(){
                   return _sayName();
               },
               sayAge : function(){
                   return _sayAge();
               }
           }
       })()
    }
    
    //调用
    test_Closure.sayName();      =>'my name is bob' 
    test_Closure.sayAge();       =>'i am 18 years old'

二、观察者模式

观察者模式也是我们常用的设计模式,也叫”发布-订阅”模式,当一个对象的状态发生改变,依赖于他的对象都得到通知并自动刷新

例如vue的双向数据绑定的原理:

当我们在表单输入框中输入(发布)message的时候,依赖(订阅)他的地方都会被更改
一句话描述:一个页面在多处订阅使用了同一个数据,用Object.defineProperty监听其改变,并由发布者通知 订阅者 去更新它所持有的数据

具体实现请参照 https://segmentfault.com/a/11…

实现一个分蛋糕案例

1、onserver.js

var Observer = {}; //定义一个对象 包括三个方法 订阅 发布 退订
(function (_observer) {

    var subListObj = {}, // 回调函数存放的数组
        subId = -1; //订阅者id
    // 发布  传入两个参数 (订阅主题,具体内容)
    _observer.publish = function (subTip, args) {
        if (!subListObj[subTip]) {
            return false;      //判断是否有订阅者
        }

        setTimeout(function () {
            var subscribers = subListObj[subTip],   //定义一个数组用来存储所有订阅者
                len = subscribers ? subscribers.length : 0;

            while (len--) {                          //只要发布者一发布就会遍历所有订阅者,分发信息
                subscribers[len].func(subTip, args);  
            }
        }, 0);

        return true;

    };
    //订阅
    _observer.subscribe = function (subTip, func) {

        if (!subListObj[subTip]) {
            subListObj[subTip] = [];
        }

        var token = (++subId).toString();   //订阅者唯一标识
        subListObj[subTip].push({                //接收信息
            token: token,             
            func: func                      //func不仅是一个动作 数据更新的回调
        });
        // console.log(token)      // => {example1:[0,func]}{example1:[1,func]}
        return token;
    };
    //退订
    _observer.unsubscribe = function (token) {     //退订 传入订阅者的id进行过滤 如果退订就splice删除
        for (var m in subListObj) {
            if (subListObj[m]) {
                for (var i = 0, j = subListObj[m].length; i < j; i++) {
                    if (subListObj[m][i].token === token) {
                        subListObj[m].splice(i, 1);
                        console.log('我' + token + '不吃了')
                        return token;
                    }
                }
            }
        }
        return false;
    };
} (Observer));

2、过来吃蛋糕啦 index.html

    <script src="./js/observer.js"></script>
    <script>
        //来,订阅一个
        Observer.subscribe('subCake', function (subTip, data) {
            console.log(subTip + ": " + data);
        });
        //来,再订阅一个
        Observer.subscribe('subCake', function (subTip, data) {
            console.log(subTip + "我来啦.." + data);
        });
        //0不吃了
        Observer.unsubscribe('0')
        //发布通知
        Observer.publish('subCake', '快来分蛋糕...');
    </script>
    
      如果0没有退订:
         subCake我也收到了..快来分蛋糕...
         subCake: 快来分蛋糕...
            
      0退订了:
         我0不吃了
         subCake我也收到了..快来分蛋糕...

三、工厂模式

工厂模式:提供创建对象的接口,封装一些公用的方法,如果实现具体的业务逻辑,可以放在子类重写父类的方法
优点:弱化对象间的耦合,防止代码重复
缺点:简单业务可以用,复杂的业务会导致代码维护性差,不易阅读

//声明一个蛋糕店 负责做蛋糕 和 卖蛋糕
var CakeShop = function(){}

CakeShop.prototype = {
    sellCake: function(){

    },
    makeCake: function(type){
        console.log('aaa')
    }
}

   //定义一个继承的方法
var Extend = function(desc, src){
    for(var property in src){
        desc[property] = src[property]
    }
    return desc;
}
Object.extend = function(obj){
    return Extend.apply(this,[this.obj])
}

 //声明一个水果蛋糕,从蛋糕店
var FruitCake = function(){}
Object.extend(FruitCake, CakeShop);

console.log(FruitCake.prototype)

FruitCake.prototype.makeCake = function(type){
    var cake;
    switch (type){
        case 'apple':
            cake = new AppleCake();break;
        case 'pear':
            cake = new Pear();break;
        default:
            cake = new Orange();break;
    }
    return cake;
}

var buyCake = new FruitCake();
var myCake = buyCake.sellCake('apple')
console.log(myCake)

策略模式

策略模式是一种很好的编程思想,可以用来解决多个if条件语句,能够代码复用,逻辑清晰,也容易扩展。
来来来,上代码

//以前你是这样的
var buyCar = function(brand){
    if(brand == '奥迪') {
        return '大于80万'
    }
    if(brand == 'QQ') {
        return '小于10万'
    }
    if(brand == 'ofo') {
        return '小于1000元'
    }
}
console.log(buyCar('ofo'))   // => 小于1000元

//那么现在你应该这样
var buyCars = {
    '奥迪': function(){
        return '大于80万'
    },
    'QQ': function(){
        return '小于10万'
    },
    'ofo': function(){
        return  '小于1000元'
    }
}
var result = function(brand){
    return buyCars[brand]();
}
console.log(result('ofo'))   // => 小于1000元

// 系不系很简单

设计模式还有很多,有时间还会继续学习…

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000012586753
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞