创建型设计模式
1、简单工厂模式:
又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象。
很多刚刚了解面向对象之后会有很多盲区,例如一个关于登录表单验证的例子:
处理前:
// 用户名校验 var LoginAlert = function (text) { this.content = text; }; LoginAlert.prototype.show = function () { // 显示警示框 }; var userNameAlert = new LoginAlert('用户名不能多于16个字母或数字'); userNameAlert.show(); // 密码校验 var passwordAlert = new LoginAlert('输入密码不正确'); passwordAlert.show(); // 添加注册按钮 var loginConfirm = function (text) { this.content = text; }; loginConfirm.prototype.show = function () { // 显示确认框 }; var loginFailConfirm = new loginConfirm('您的用户名不存在,请重新输入!'); loginFailConfirm.show(); // 友好自定义提示框 var LoginPrompt = function (text) { this.content = text; }; LoginPrompt.prototype.show = function () { // 显示提示框 };
处理后:
function createPop (type, text) { // 创建一个对象,并对对象拓展属性和方法 var o = new Object(); o.content = text; o.show = function () {}; if (type === 'alert') { // 警示框差异部分 } if (type === 'prompt') { // 警示框差异部分 } if (type === 'confirm') { // 警示框差异部分 } // 将对象返回 return o; }; var userNameAlert = createPop('alert', '用户名不能多于16个字母或数字');
团队项目开发不同于个人开发,其对全局变量的限制很大,所以我们要尽量少创建全局变量。对于同一类对象在不同需求中的重复性使用,很多
时候不需要重复创建,代码复用是面向对象编程的一条准则。通过对简单工厂来创建一些对象,可以让这些对象公用一些资源而又私有一些资源
,这是一种很不错的实践。不过对于简单工厂模式,它的使用场合通常也就限制在创建单一对象。
2、工厂方法模式:
通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例
处理前:
// 创建Java学科类 var Java = function (content) { // 将内容保存在content里面 以备后日使用 this.content = content; // 创建对象时,通过闭包直接执行,将内容按需求的样式插入到页面内 (function (content) { var div = document.createElement('div'); div.innerHTML = content; div.style.color = 'green'; document.getElementById('container').appendChild(div); })(content); }; // 创建PHP学科类 var Php = function (content) { // 将内容保存在content里面 以备后日使用 this.content = content; // 创建对象时,通过闭包直接执行,将内容按需求的样式插入到页面内 (function (content) { var div = document.createElement('div'); div.innerHTML = content; div.style.color = 'yellow'; div.style.background = 'red'; document.getElementById('container').appendChild(div); })(content); }; // 创建Javascript学科类 var JavaScript = function (content) { // 将内容保存在content里面 以备后日使用 this.content = content; // 创建对象时,通过闭包直接执行,将内容按需求的样式插入到页面内 ( function (content) { var div = document.createElement('div'); div.innerHTML = content; div.style.background = 'pink'; document.getElementById('container').appendChild(div); })(content); }; // 学科类工厂 function JobFactory (type, content) { switch (type) { case 'Java': return new Java(content); case 'Php': return new Php(content); case 'Javascript': return new JavaScript(content); }; }
处理后:
// 安全模式创建工厂类 var Factory = function (type, content) { if (this instanceof Factory) { var s = new this[type](content); return s; } else { return new Factory(type, content); } }; // 工厂原型中设置创建所有类型数据对象的基类 Factory.prototype = { Java: function (content) { //..... }, Php: function (content) { //..... }, JavaScript: function (content) { //..... }, UI: function (content) { // 将内容保存在content里面 以备后日使用 this.content = content; // 创建对象时,通过闭包直接执行,将内容按需求的样式插入到页面内 (function (content) { var div = document.createElement('div'); div.innerHTML = content; div.style.background = 'pink'; div.style.border = '1px soild red'; document.getElementById('container').appendChild(div); })(content); }, };
对于创建多类对象,前面学过的简单工厂模式就不太适合了,这是简单工厂模式的应用局限,当然这正是工厂方法模式的价值所在,通过工厂方法模式
我们可以轻松创建多个类的实例对象,这样工厂方法对象在创建对象的方式也避免了使用者与对象之间的耦合,用户不必关心创建该对象的具体类,只
需要调用工厂方法即可。
3、抽象工厂模式:
通过对类的工厂抽象使其业务用于对产品类的创建,而不负责创建某一类产品的实例。
举例:
// 汽车抽象类,当使用其实例对象的方法时会抛出错误 var Car = function () {}; Car.prototype = { getPrice: function () { return new Error('抽象方法不能调用'); }, getSpeed: function () { return new Error('抽象方法不能调用'); } };
我们看到我们创建的这个car类其实什么都做不了,创建时没有任何属性,然而原型prototype上的方法也不能使用,否则会报错。但在继承上却是非常
有用,因为定义了一种类并且定义了该类所必备的方法,如果在子类中没有重写这些方法,那么当调用的时候就会报错,这一特点是很必要的,因为在一
些大型应用中,总会有一些子类去继承另一些父类,这些父类经常会定义一些必要的方法,却没有具体的实现,如car类中的 getPrice()和getSpeed()
方法,那么一旦子类创建了一对象,该对象总是应该具备一些方法的,如果这些方法从父类继承过来就没有具体的实现,那么实例化对象便会调用父类中
的这些方法,如果父类能有一个好的提示,那么对于忘记重写子类的这些错误遗漏的避免是很有帮助的,这也是抽象类的一个作用,即定义一个产品蔟,
并声明一些方法,如果子类中没有重写就会抛出错误。
// 抽象方法 var VehicleFactory = function (subType, superType) { // 判断抽象工厂中是否有该抽象类 if (typeof VehicleFactory[superType] === 'function') { // 缓存类 function F () {}; // 继承父类属性和方法 F.prototype = new VehicleFactory[superType](); // 将子类constructor指向子类 subType.constructor = subType; // 子类原型继承父类 subType.prototype = new F(); } else { // 不存在该抽象类抛出错误 throw new Error('未创建该抽象类'); } }; // 小汽车抽象类 VehicleFactory.Car = function () { this.type = 'car'; }; VehicleFactory.Car.prototype = { getPrice: function () { return new Error('抽象方法不能调用'); }, getSpeed: function () { return new Error('抽象方法不能调用'); } }; // 宝马汽车子类 var BMW = function (price, speed) { this.price = price; this.speed = speed; }; // 抽象工厂实现对Car抽象类的继承 VehicleFactory(BMW, 'Car'); BMW.prototype.getPrice = function () { return this.price; }; BMW.prototype.getSpeed = function () { return this.speed; }; // 掉用 var bmw = new BMW(100000, 1000); console.log(bmw.getPrice); // 100000 console.log(bmw.getSpeed); // 1000
抽象工厂模式是设计模式中最抽象的一种,也是创建模式中唯一一种抽象化创建模式,该模式创建出的结果不是一个真实的对象实例,而是一个类簇,它制
定了类的结构,这也是区别于简单工厂模式创建单一对象,工厂方法模式创建多类对象。当然由于JavaScript中不支持抽象化创建与虚拟方法,所以导致
这种模式不能像其他面向对象语言中应用的那么广泛。
4、创建者模式:
将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示
// 创建一位人类 var Human = function (param) { // 技能 this.skill = param && param.skill || '保密'; // 兴趣爱好 this.hobby = param && param.hobby || '保密'; }; // 类人原型方法 Human.prototype = { getSkill: function () { return this.skill; }, getHobby: function () { return this.hobby; } }; // 实例化姓名类 var Named = function (name) { var that = this; // 构造器 // 构造函数解析姓名与名 (function (name, that) { that.wholeName = name; if (name.indexOf(' ') > -1) { that.FirstName = name.slice(0, name.indexOf(' ')); that.seconName = name.sile(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; }; var Person = function (name, work) { // 创建应聘者对象 var _preson = new Human(); // 创建应聘者姓名解析对象 _preson.name = new Named(name); // 创建应聘者期望职位 _person.work = new Work(work); // 将创建的应聘者对象返回 return _preson; }; 使用: var person = new Person('xiao ming', 'code'); console.log(person.skill); // 保密 console.log(person.name.FirstName); // xiao console.log(person.work.work); // 工程师 console.log(person.work.workDescript); // 每天在编程中度过 person.work.changeDescript('更改一下职位描述!'); console.log(person.work.workDescript); // 更改一下职位描述
5、原型模式:
用原型实例指向创建对象的类,使用与创建新的对象的类共享原型对象的属性和方法。
处理前:
// 图片轮播类 var LoopImages = function (imgArr, container) { this.imagesArray = imgArr; this.container = container; this.createImage = function () {}; this.changeImage = function () {}; }; // 渐隐切换类 var FadeLoopImage = function (imgArr, container, arrow) { LoopImages.call(this, imgArr, container); // 切换箭头私有变量 this.arrow = arrow; this.changeImage = function () { console.log('FadeLoopImage changeImage function'); }; }; // 实例化一个渐隐切换图片类 var fadeImg = new FadeLoopImage(['01.jpg', '02.jpg', '03.jpg', '04.jpg'], 'slide', ['left.jpg', 'right.jpg']);
处理后:
// 图片轮播类 var LoopImage = function (imgArr, container) { this.imagesArray = imgArr; // 轮播图片数组 this.container = container; // 轮播图片容器 }; // 渐隐切换类 var FadeLoopImage = function (imgArr, container, arrow) { LoopImages.call(this, imgArr, container); // 切换箭头私有变量 this.arrow = arrow; }; FadeLoopImage.prototype = new LoopImages(); FadeLoopImage.prototype.changeImage = function () { console.log('FadeLoopImage changeImage function'); }; // 测试用例 console.log(fadeImg.container); // slide fadeImg.changeImage(); // FadeLoopImage changeImage function // 原型继承 function prototypeExtend () { var F = function () {}; // 缓存类,为实例化返回对象临时创建 args = arguments, i = 0, len = args.length; for (; i< lenl i++) { // 遍历每个模板对象的属性 for (var j in args[i]) { // 将这些属性赋值到缓存类原型中 F.prototype[j] = args[i][j]; } } // 返回缓存类的一个实例 return new F(); }; var penguin = prototypeExtend({ speed: 20, swim: function () { console.log('游泳速度' + this.spee) } },{ run: function () { console.log('奔跑速度' + speed) } },{ jump: function () { console.log('跳跃动作') } }); penguin.swim(); // 游泳速度20 penguin.run(10); // 奔跑速度10 penguin.jumo(); // 跳跃动作
6、单例模式:
又称为单体模式,是只允许实例化一次的对象类,有时我们也用一个对象来规划一个命名空间,井井有条的管理对象上的属性和方法。
案例:
// 惰性载入单例 var LazySingle = (function () { // 单例实例引用 var _instance = null; // 单例 function Single () { // 这里定义了私有的属性和方法 return { publicMethod: function () {}, publicProperty: '1.0' }; }; //获取单例对象接口 return function () { // 如果为创建单例创建单例 if (!_instance) { _instance = Single(); }; // 返回单例 return _instance; }; })(); 测试用例: console.log(LazySingle().publicProperty); // 1.0
单例模式有时也被称为单体模式,它只是一个只允许实例化一次的对象类,有时这么做也是为了节省系统资源。当然Javascript中单例模式经常
作为命名空间对象实现,通过单例对象我们可以将各个模块的代码井井有条的梳理在一起。