代码要写成别人看不懂的样子(四)

it2024-01-27  68

文章目录

前言一、建造者模式二、原型模式三、单例模式单例模式妙用惰性单例

前言

  上节我们一块学习了工厂相关的设计模式,可以方便的帮我们创建类或者实例。本节我们换个角度,不关心产出什么实例,我们来研究一下实例生产的过程。

  这里有个问题,为什么要关注生产过程,我们要的不都是最后的实例对象吗?话是没错,但是有些实例太复杂了,我们需要一步一步划分好功能,依次创建。才能完成最终对象的创建。

  举个例子,我们现在想要一套房子,房子是一个对象没错,我们还要关心这个房子有没有卧室,厨房怎么样,客厅大不大等等。那么这些关注点,都是需要我们创建的。

一、建造者模式

  关注建造细节,下面我们创建一个简历的构造函数。

//创建人类 var Human = function(param) { //技能 this.skill = param && param.skill || '保密'; //兴趣爱好 this.hobby = param && param.hobby || '保密'; } //类人原型方法 Human.prototype = { getSkill: function() { renturn this.skill; }, getHobby: function() { renturn this.hobby; } } //实例化姓名类 var Named = fucntion(name) { var that = this; //构造器 //构造函数解析姓名的姓与名 (function(name, that) { if(name.indexOf(' ') > -1) { that.firstName = name.slice(0, name.indexOf(' ')); that.secondName = name.slice(name.indexOf(' ')); } }) (name, that); } //实例化职位类 var Work = function(work) { var that = this; //构造器 //构造函数中通过传入的职位特征来设置相应职位以及描述 (function(work, that) => { switch(work) { case 'code': that.work = '工程师'; that.workDescript = '沉迷编程无法自拔'; break; case 'UI': case 'UE': that.work = '设计师'; that.workDescript = '设计是一种艺术'; break; case 'teach': that.work = '教师'; that.workDescript = '我分享,我快乐'; break; default : that.work = work; that.workDescript = '对不起,我们还不清楚职位的相关描述'; } }) (work, that); } //更换期望的职位 Work.prototype.changeWork = function(work) { this.work = work; } //更改对职位的描述 Work.prototype.changeDescript = function(setence) { this.workDescript = setence; }

  我们的最终目的是创建一位应聘者,创建过程需要用到上面抽象的三个类,这时候需要一个建造者类,在建造者类中,我们需要通过对三个类的组合调用,创建出一个完整的应聘者对象。

/****** * 应聘者建造者 * 参数 name : 姓名(全名) * 参数 work : 期望职位 */ var Person = function(name, work) { //创建应聘者缓存对象 var _person = new Human(); //创建应聘者姓名解析对象 _person.name = new Named(name); //创建应聘者期望职位 _person.work = new Work(work); return _person; }

  上面,我们通过三部分来创建一位应聘者。

var person = new Person('xiao ming', 'code');

  建造者模式与工厂最大的不同,就是建造者会参与具体的创建,干涉建造细节,生成的对象,也更复杂。

二、原型模式

  接下来要介绍的设计模式,是整个语言的灵魂,原型模式。

  原型模式指的是:用原型实例指向创建对象的类,使用于创建新的对象的类共享原型对象的属性以及方法。

  举个例子,我们现在创建一个轮播图对象,不同的轮播图效果不一样,有左右滑动的,还有上下滑动的,另外渐变的轮播图也有。

//图片轮播类 var LoopImages = function(imgArr, container) { this.imagesArray = imgArr; //轮播图片数组 this.container= container; //轮播图片容器 this.createImage = function() {} //创建轮播图片 this.changeImage = function() {} //切换下一张图片 } //上下滑动切换类 var SlideLoopImg = function(imgArr, container) { //构造函数继承图片轮播类 LoopImages.call(this, imgArr, container); //重写继承的切换下一张图片方法 this.changeImage = function() { console.log('SlideLoopImg changeImage function'); } } //渐隐切换类 var FadeLoopImg = function(imgArr, container, arrow) { LoopImages.call(this, imgArr, container); //切换箭头私有变量 this.arrow = arrow; this.changeImage = function() { console.log('FadeLoopImg changeImage function'); } }

  创建一个显隐轮播图片来测试

var fadeImg = new FadeLoopImg([ '01.jpg', '02.jpg', '03.jpg', '04.jpg' ], 'slide', [ 'left.jpg', 'right.jpg' ]) fadeImg.changeImage();

  上面代码已经实现了我们需要的功能,但是还有一部分可以优化,比如 LoopImage 作为基类,始终是要被子类继承的,那么把一些方法写到父类的构造函数里就会存在一部分性能问题,因为每次子类继承都要创建一次父类,如果父类里面有很多耗时操作,那么每次创建都会有很大的性能开销,这时候如果把这些耗时操作放到父类的原型上,那么就会节约一大部分性能。

//图片轮播类 var LoopImages = function(imgArr, container) { this.imagesArray = imgArr; //轮播图片数组 this.container= container; //轮播图片容器 //下面两部分我们移到原型当中,这样就不会每次实例化父类的时候都执行这些耗时操作了 //this.createImage = function() {} //创建轮播图片 //this.changeImage = function() {} //切换下一张图片 } LoopImages.prototype = { //创建轮播图片 createImage: function() { console.log('LoopImages createImage function'); } , //切换下一张图片 changeImage : function() { console.log('LoopImages changeImage function'); } } //上下滑动切换类 var SlideLoopImg = function(imgArr, container) { //构造函数继承图片轮播类 LoopImages.call(this, imgArr, container); } SlideLoopImg.prototype = new LoopImages(); //重写继承的切换下一张图片 SlideLoopImg.prototype.changeImage = function() { console.log('SlideLoopImg changeImage function'); } //渐隐切换类 var FadeLoopImg = function(imgArr, container, arrow) { LoopImages.call(this, imgArr, container); //切换箭头私有变量 this.arrow = arrow; } FadeLoopImg.prototype = new LoopImages(); FadeLoopImg.prototype.changeImage = function() { console.log('FadeLoopImg changeImage function'); }

  原型对象还有一个特点,由于原型对象是一个共享的对象,那么不管父类的实例还是子类的实例,都是对它的指向引用,如果原型对象进行扩展,那么无论是子类还是父类,都会继承下来。所以在扩展的时候,一定要慎重。

LoopImages.prototype.getImageLength = function() { return this.imagesArray.length; } FadeLoopImg.prototype.getContainer = function() { return this.getContainer ; } console.log(fadeImg.getImageLength); console.log(fadeImg.getContainer);

三、单例模式

  接下来一起学习最常用的一种模式,一个人的狂欢。单例模式只允许实例化一次对象类。这种模式的出现,是为了更好的管理我们书写的方法,比如 A 写了一个 getElement 的方法, B 也想写,但是命名又不能一样,这就需要我们对自己的方法进行整理归类,比如我们定义一个自己的命名空间 MySelf

var Myself = { getElement: function(id) { return document.getElementById(id) }, changeType: function(id, key, value) { this.getElement(id).style[key] = value; } }

  上面的代码,就是我们自己的小型代码库,任何我们用到的方法都可以放到这个里面去,当我们想要使用的时候,通过 Myself.getElement 就可以。有了 Myself ,这个命名空间,我们就不用担心会和别人的命名冲突。 JQuery 就是前辈们的一个命名空间,里面有着很多方便我们使用的方法。当我们整理的比较好的时候,也可以发布自己的代码库供别人使用。

单例模式妙用

  单例模式还有一个妙用,就是用来管理静态变量。由于在es6之前,JS 中并没有 static 这个关键字,所以是没有静态变量的,但是我们可以手动实现静态变量的功能。

  所谓静态变量,就是指那些,可以访问,但是不能修改的变量。

var Conf = ( //私有变量 var conf = { MAX_NUM: 100, MIN_NUM: 1, COUNT: 1000 } //返回取值器方法 return { //取值器方法 get: function(name) { return conf[name] ? conf[name] : null; } } )(); var count = Count.get('COUNT'); console.log(count); //1000

惰性单例

  有时候单例对象需要延迟创建,所以还有一种延迟创建的单例模式。

//惰性载入单例 var = LazySingle = (function() { //单例实例引用 var _instance = null; //单例 function Single() { /*这里定义私有属性和方法*/ return { publicMethod: function() {}, publicProperty: '10' } } //获取单例对象接口 return function() { //如果为创建单例,则创建单例 if(!_instance) { _instance = Single(); } //返回单例 return _instance; } })(); console.log(LazySingle().publicProperty) //1.0

  到这里,我们已经介绍完了所有关于“创”,“建”,类型的设计模式,从最开始的工厂相关模式,到后面的建造者,原型,单例,这部分包含的功能可以涵盖我们代码的大部分区域,各位同学自己在编码的时候,可以多试试每种模式,我个人的建议是,先按住一个,然后往死里用,合适不合适都这样用,等到什么时候快用吐了,你就真的掌握这种思想了。

  

最新回复(0)