转载

浅谈工作中的设计模式01

前言

记得刚毕业的时候参加了一次校招面试,之前表现的很好,最后时面试官问我懂不懂设计模式,我说不懂,然后就进去了;后面又参加了某大公司的校招,开始表现还行,后面面试官问我懂不懂设计模式,我说懂(上次后补习了下),最后把工厂模式的代码背写到了纸上,然后就没有然后了......

现在回想起来当时有点傻有点天真,没有几十万的代码量,没有一定的经验总结,居然敢说懂设计模式,这不是找抽么?

现在经过几年工作学习,倒是是时候系统的回忆下平时工作内容,看用到了什么设计模式,权当总结。

小钗对设计模式的理解程度有限,文中不足之处请您拍砖。

面向对象的实现

设计模式便是面向对象的深入,面向对象的应用,所以类的实现是第一步:

PS:这里 依赖了underscore ,各位自己加上吧。

 1 //window._ = _ || {};  2 // 全局可能用到的变量  3 var arr = [];  4 var slice = arr.slice;  5 /**  6 * inherit方法,js的继承,默认为两个参数  7 *  8 * @param  {function} origin  可选,要继承的类  9 * @param  {object}   methods 被创建类的成员,扩展的方法和属性 10 * @return {function}         继承之后的子类 11 */ 12 _.inherit = function (origin, methods) { 13  14   // 参数检测,该继承方法,只支持一个参数创建类,或者两个参数继承类 15   if (arguments.length === 0 || arguments.length > 2) throw '参数错误'; 16  17   var parent = null; 18  19   // 将参数转换为数组 20   var properties = slice.call(arguments); 21  22   // 如果第一个参数为类(function),那么就将之取出 23   if (typeof properties[0] === 'function') 24     parent = properties.shift(); 25   properties = properties[0]; 26  27   // 创建新类用于返回 28   function klass() { 29     if (_.isFunction(this.initialize)) 30       this.initialize.apply(this, arguments); 31   } 32  33   klass.superclass = parent; 34  35   // 父类的方法不做保留,直接赋给子类 36   // parent.subclasses = []; 37  38   if (parent) { 39     // 中间过渡类,防止parent的构造函数被执行 40     var subclass = function () { }; 41     subclass.prototype = parent.prototype; 42     klass.prototype = new subclass(); 43  44     // 父类的方法不做保留,直接赋给子类 45     // parent.subclasses.push(klass); 46   } 47  48   var ancestor = klass.superclass && klass.superclass.prototype; 49   for (var k in properties) { 50     var value = properties[k]; 51  52     //满足条件就重写 53     if (ancestor && typeof value == 'function') { 54       var argslist = /^/s*function/s*/(([^/(/)]*?)/)/s*?/{/i.exec(value.toString())[1].replace(//s/i, '').split(','); 55       //只有在第一个参数为$super情况下才需要处理(是否具有重复方法需要用户自己决定) 56       if (argslist[0] === '$super' && ancestor[k]) { 57         value = (function (methodName, fn) { 58           return function () { 59             var scope = this; 60             var args = [ 61               function () { 62                 return ancestor[methodName].apply(scope, arguments); 63               } 64             ]; 65             return fn.apply(this, args.concat(slice.call(arguments))); 66           }; 67         })(k, value); 68       } 69     } 70  71     //此处对对象进行扩展,当前原型链已经存在该对象,便进行扩展 72     if (_.isObject(klass.prototype[k]) && _.isObject(value) && (typeof klass.prototype[k] != 'function' && typeof value != 'fuction')) { 73       //原型链是共享的,这里处理逻辑要改 74       var temp = {}; 75       _.extend(temp, klass.prototype[k]); 76       _.extend(temp, value); 77       klass.prototype[k] = temp; 78     } else { 79       klass.prototype[k] = value; 80     } 81  82   } 83  84   if (!klass.prototype.initialize) 85     klass.prototype.initialize = function () { }; 86  87   klass.prototype.constructor = klass; 88  89   return klass; 90 };

使用测试:

 1 var Person = _.inherit({  2     initialize: function(opts) {  3         this.setOpts(opts);  4     },  5   6     setOpts: function (opts) {  7         for(var k in opts) {  8             this[k] = opts[k];  9         } 10     }, 11  12     getName: function() { 13         return this.name; 14     }, 15  16     setName: function (name) { 17         this.name = name 18     } 19 }); 20  21 var Man = _.inherit(Person, { 22     initialize: function($super, opts) { 23         $super(opts); 24         this.sex = 'man'; 25     }, 26  27     getSex: function () { 28         return this.sex; 29     } 30 }); 31  32 var Woman = _.inherit(Person, { 33     initialize: function($super, opts) { 34         $super(opts); 35         this.sex = 'women'; 36     }, 37  38     getSex: function () { 39         return this.sex; 40     } 41 }); 42  43 var xiaoming = new Man({ 44     name: '小明' 45 }); 46  47 var xiaohong = new Woman({ 48     name: '小红' 49 });
xiaoming.getName() "小明" xiaohong.getName() "小红" xiaoming.getSex() "man" xiaohong.getSex() "women"

单例模式(Singleton)

单列为了保证一个类只有一个实例,如果不存在便直接返回,如果存在便返回上一次的实例,其目的一般是为了资源优化。

javascript中实现单例的方式比较多,比较实用的是直接使用对象字面量:

1 var singleton = { 2     property1: "property1", 3     property2: "property2", 4     method1: function () {} 5 };

类实现是正统的实现,一般是放到类上,做静态方法:

浅谈工作中的设计模式01

在实际项目中,一般这个应用会在一些通用UI上,比如mask,alert,toast,loading这类组件,还有可能是一些请求数据的model,简单代码如下:

 1 //唯一标识,一般在amd模块中  2 var instance = null;  3   4 //js不存在多线程,这里是安全的  5 var UIAlert = _.inherit({  6     initialize: function(msg) {  7         this.msg = msg;  8     },  9     setMsg: function (msg) { 10         this.msg = msg; 11     }, 12     showMessage: function() { 13         console.log(this.msg); 14     } 15 }); 16  17 var m1 = new UIAlert('1'); 18 m1.showMessage();//1 19 var m2 = new UIAlert('2'); 20 m2.showMessage();//2 21 m1.showMessage();//1

如所示,这个是一个简单的应用,如果稍作更改的话:

 1 //唯一标识,一般在amd模块中  2 var instance = null;  3   4 //js不存在多线程,这里是安全的  5 var UIAlert = _.inherit({  6     initialize: function(msg) {  7         this.msg = msg;  8     },  9     setMsg: function (msg) { 10         this.msg = msg; 11     }, 12     showMessage: function() { 13         console.log(this.msg); 14     } 15 }); 16 UIAlert.getInstance = function () { 17     if (instance instanceof this) { 18         return instance; 19     } else { 20         return instance = new UIAlert(); //new this 21     } 22 } 23  24 var m1 = UIAlert.getInstance(); 25 m1.setMsg(1); 26 m1.showMessage();//1 27 var m2 = UIAlert.getInstance(); 28 m2.setMsg(2); 29 m2.showMessage();//2 30 m1.showMessage();//2

如所示,第二次的改变影响了m1的值,因为他们的实例msg是共享的,这个便是一次单列的使用,而实际场景复杂得多。

以alert组件为例,他还会存在按钮,一个、两个或者三个,每个按钮事件回调不一样,一次设置后,第二次使用时各个事件也需要被重置,比如事件装在一个数组eventArr = []中,每次这个数组需要被清空重置,整个组件的dom结构也会重置,好像这个单例的意义也减小了,真实情况是这样的意义在于全站,特别是对于webapp的网站,只有一个UI dom的根节点,这个才是该场景的意义所在。

而对mask而言便不太适合全部做单例,以弹出层UI来说,一般都会带有一个mask组件,如果一个组件弹出后马上再弹出一个,第二个mask如果与第一个共享的话便不合适了,因为这个mask应该是各组件独享的。

单例在javascript中的应用更多的还是来划分命名空间,比如underscore库,比如以下场景:

① Hybrid桥接的代码

window.Hybrid = {};//存放所有Hybrid的参数

② 日期函数

window.DateUtil = {};//存放一些日期操作方法,比如将“2015年2月14日”这类字符串转换为日期对象,或者逆向转换

......

工厂模式(Factory)

工厂模式是一个比较常用的模式,介于javascript对象的不定性,其在前端的应用门槛更低。

工厂模式出现之初意在解决对象耦合问题,通过工厂方法,而不是new关键字实例化具体类,将所有可能的类的实例化集中在一起。

一个最常用的例子便是我们的Ajax模块:

 1 var XMLHttpFactory = {};  2 var XMLHttpFactory.createXMLHttp = function() {  3     var XMLHttp = null;  4     if (window.XMLHttpRequest){  5     XMLHttp = new XMLHttpRequest()  6     }else if (window.ActiveXObject){  7     XMLHttp = new ActiveXObject("Microsoft.XMLHTTP")  8     }  9     return XMLHttp; 10 }

浅谈工作中的设计模式01

使用工厂方法的前提是,产品类的接口需要一致,至少公用接口是一致的,比如我们这里有一个需求是这样的:

浅谈工作中的设计模式01

可以看到各个模块都是不一样的:

① 数据请求

② dom渲染,样式也有所不同

③ 事件交互

但是他们有一样是相同的:会有一个共同的事件点:

① create

② show

③ hide

所以我们的代码可以是这样的:

浅谈工作中的设计模式01

 1 var AbstractView = _.inherit({  2     initialize: function() {  3         this.wrapper = $('body');  4         //事件管道,实例化时触发onCreate,show时候触发onShow......  5         this.eventsArr = [];  6     },  7     show: function(){},  8     hide: function (){}  9 }); 10 var SinaView = _.inherit(AbstractView, { 11 }); 12 var BaiduView = _.inherit(AbstractView, { 13 });

每一个组件实例化只需要执行实例化操作与show操作即可,各个view的显示逻辑在自己的事件管道实现,真实的逻辑可能是这样的

 1 var ViewContainer = {  2     SinaView: SinaView,  3     BaiduView: BaiduView  4 };  5 var createView = function (view, wrapper) {  6     //这里会有一些监测工作,事实上所有的view类应该放到一个单列ViewContainer中  7     var ins = new ViewContainer[view + 'View'];  8     ins.wrapper = wrapper;  9     ins.show(); 10 } 11 //数据库读出数据 12 var moduleInfo = ['Baidu', 'Sina', '...']; 13  14 for(var i = 0, len = moduleInfo.length; i < len; i++){ 15     createView(moduleInfo[i]); 16 }

如之前写的坦克大战,创建各自坦克工厂模式也是绝佳的选择,工厂模式暂时到此。

今天暂时到此,我们后面再继续。

正文到此结束
Loading...