常见设计模式
2018-07-16 本文已影响0人
佩佩216
设计模式简单说明
概念:设计模式是对软件设计中普遍存在(反复出现)的各种问题所提出的解决方案。
优点:为了可重用代码,让代码更容易被他人理解,保证代码可靠性。
分类:
创建型模式:工厂方法模式+ 抽象工厂模式 +单粒模式 + 建造者模式 + 原型模式;
结构型模式:适配器模式 + 代理模式 + ···;
行为型模式:观察者模式 + 命令模式 + ···
工厂模式
简单说明:目的是用于创建对象,通常在类或者是类的静态方法中实现。
目标:
- 用一套方法去创建相似的目标;
- 在编译时不知道具体类型的情况下,为用户提供创建对象的接口;
核心步骤:
- 提供一个父构造函数;
- 在父构造函数的原型上添加共享的方法;
- 在父构造函数身上提供一个静态方法(静态工厂方法);
- 先获取参数(产品类型);
- 判断构造函数是否存在(容错性处理);
- 设置原型链继承:设置子构造函数的原型对象为父构造函数的一个实例对象;
- 使用子构造函数创建实例对象;
- 返回新创建的实例对象.
- 定义特定的工厂客户(静态方法);
- 通过父构造函数的静态方法来创建产品对象。
//1. 提供一个父构造函数;
function PhoneMake() {
}
//2. 在父构造函数的原型上添加共享的方法;
PhoneMake.prototype.logDes = function () {
console.log('我们的口号是' + this.des);
}
//3. 在父构造函数身上提供一个静态方法(静态工厂方法);
PhoneMake.factory =function (typeStr) {
//1.先获取参数(产品类型)
var type = typeStr;
//2.判断构造函数是否存在(容错性处理)
if(typeof PhoneMake[type] != 'function'){
throw '暂时无法生产此款手机!'
}
//3.设置原型链继承:设置子构造函数的原型对象为父构造函数的一个实例对象
PhoneMake[type].prototype = new PhoneMake();
//4.使用子构造函数创建实例对象
var obj = new PhoneMake[type]();
//5.返回新创建的实例对象
return obj;
}
//4. 定义特定的工厂客户(静态方法);
PhoneMake.iphone = function () {
this.des = '最安全,最好用的手机'
}
PhoneMake.oppo = function () {
this.des = '照亮你的美!'
}
PhoneMake.vivo =function () {
this.des = '充电五分钟,通话两小时'
}
PhoneMake.meizu = function () {
this.des = '我就是我,不一样的烟火'
}
//5. 通过父构造函数的静态方法来创建产品对象。
var iphone = PhoneMake.factory('iphone');
var oppo = PhoneMake.factory('oppo');
var vivo = PhoneMake.factory('vivo');
var meizu = PhoneMake.factory('meizu');
iphone.logDes();
oppo.logDes();
vivo.logDes();
meizu.logDes();
huawei.logDes();
单例模式
思想:保证一个特定的类只有一个实例,即当我们第二次创建新对象的时候,得到的应该是和第一次创建的对象一模一样的对象(同一个对象)。
JavaScript中的单例模式
JavaScript是一门弱类型,动态,基于原型的语言,并没有类,只有对象。
在JavaScript中要实现单例模式有很多种方式。
- 使用全局变量方式存储创建出来的实例对象
思想:- 提供一个全局变量;2. 提供一个构造函数;3. 判断这个全局变量是否有值,如果有值直接返回;4. 如果没有值,就把this赋值给全局变量;5. 通过this设置属性和方法。
问题:使用一个全局变量来实现单例,这个全局变量在整个作用域中都可以被访问或者修改,很容易导致被覆盖或者修改。修改后创建出来的对象就不再是之前的单例对象了。
- 提供一个全局变量;2. 提供一个构造函数;3. 判断这个全局变量是否有值,如果有值直接返回;4. 如果没有值,就把this赋值给全局变量;5. 通过this设置属性和方法。
var instance ;
function Person() {
if(instance){
console.log('再一次创建对象,直接返回之前的对象');
return instance;
}
instance = this;
this.name = 'zs';
this.age = 20;
console.log('第一次创建对象');
}
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);//true
instance = 'demo';//修改了全局变量instance的值,
var p3 = new Person();
console.log(p1 == p3);//false
全局变量方式实现单例-即时函数
说明:通过即时函数来限定作用域,外部无法修改内部instance的值(现在是一个局部变量)。
var Person;
(function () {
var instance;
Person = function () {
if(instance){
return instance;
}
instance = this;
this.name = '默认';
}
})();
var p1 = new Person();
var p2 = new Person();
- 通过构造函数静态属性来缓存实例对象
思想:- 提供一个构造函数;2. 在内部判断构造函数的静态属性中是否拥有实例对象;3.把内部创建的实例化对象赋值给构造函数的静态属性;4. 创建实例对象;5. 通过this设置属性和方法。
问题:在构造函数外部可以直接访问其静态成员(属性和方法),可能会导致实例对象的丢失。
//1. 提供一个构造函数;
function Person() {
// 2. 在内部判断构造函数的静态属性中是否拥有实例对象;
if(Person.instance){
console.log('之前创建过,直接返回');
return Person.instance;
}
//设置实例对象的属性和方法
this.name = 'zs';
console.log('第一次创建');
//3.把内部创建的实例化对象赋值给构造函数的静态属性;
Person.instance = this;
}
// 4. 创建实例对象;
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);//true
// 5. 通过this设置属性和方法。
Person.instance = 'demo'
var p3 = new Person();
console.log(p1 == p3);//false
- 通过惰性函数定义来实现
思路:
1.提供一个构造函数;2.在构造函数内部提供一个私有变量来缓存实例;3. 利用惰性函数定义更新构造函数;4.把this赋值instance;5.通过this设置属性和方法
注意点: 1.创建出来的单粒对象的构造器属性始终指向旧的构造函数;2.创建单粒对象之后设置的原型对象和这个单粒对象的原型对象不是同一个,设置到原型上的属性和方法无法访问。
//1.提供一个构造函数
function Person() {
//2.在构造函数内部提供一个私有变量
var instance;
//3. 利用惰性函数定义更新构造函数
Person = function () {
return instance;
}
//4.把this赋值instance
instance = this ;
//5.通过this设置属性和方法
this.name = '默认的';
this.age = 20;
}
Person.prototype.des = 'des';
var p1 = new Person();
Person.prototype.log = 'log';
var p2 = new Person();
console.log(p1.constructor == Person);//false
console.log(p1.des); //des
console.log(p2.des); //des
console.log(p1.log); // undefined
console.log(p2.log); // undefined
解决以上惰性函数问题思路:
1.提供一个构造函数;2.在构造函数内部提供一个私有变量;3.利用惰性函数定义更新构造函数;4.设置原型链继承;5.调用new构造函数方法创建一个实例化对象赋值给instance;6.修正instance实例的构造器属性,指向新的构造函数;7. 设置实例属性和方法;8. 返回instance对象。
//1.提供一个构造函数
function Person() {
//2.在构造函数内部提供一个私有变量
var instance;
//3. 利用惰性函数定义更新构造函数
Person = function () {
return instance;
}
// 4.设置原型对象(设置新构造函数的原型是旧构造函数的原型)
Person.prototype = this;
// 5.利用新构造函数创建对象
instance = new Person();
//6.修正构造器属性
instance.constructor = Person;
//7.通过instance设置属性和方法
instance.name = '默认的';
//8.返回instance 对象
return instance ;
}
Person.prototype.des = 'des';
var p1 = new Person();
Person.prototype.log = 'log';
var p2 = new Person();
console.log(p1.constructor == Person);//true
console.log(p1.des); //des
console.log(p2.des); //des
console.log(p1.log); // log
console.log(p2.log); // log
观察者模式
观察者模式又名为发布-订阅者模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。
优点
- 观察者模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案;
- 观察者模式可以取代对象之间硬性编码的通知机制,一个对象不再是显示的调用另外一个对象的接口,这种模式让两个对象松耦合的联系在一起,它们不需要清楚彼此的实现细节就能够相互通信;
- 在这种设计模式中,不再是一个对象调用另外一个对象的方法,而是一个对象订阅另一个对象的特定活动,并且在状态改变后获得通知。
模式
- 订阅者也称为观察者;
- 被观察的对象称为发布者或者是主题;
- 当发生一个重要事件的时候,发布者将会通知所有订阅者并且经常以事件的形式来传递消息。
思想
- 提取成公共的发行者对象;
- 提供一个工具函数,能够利用发行者对象的模板来快速创建新的发布者;
- 创建发布者;
- 创建订阅者(当发布者发布消息的时候,订阅者能够收到信息--自动调用订阅者的方法)
//1. 提取公共的发行者对象;
var publisher = {
addUser: function (fn, type) {
//对订阅的类型进行判断
var type = type || 'eat';
if (typeof this.user[type] == 'undefined') {
this.user[type] = [];
}
if (typeof fn != 'function') {
throw '不支持'
}
this.user[type].push(fn);
},
removeUser: function (fn, type) {
this.publish(type,fn);
},
publish: function (type, fn) {
var type = type || 'eat';
for (var i = 0; i < this.user[type].length; i++) {
//判断是发布状态还是移除状态
if (typeof fn == 'function') {
//移除状态
if (this.user[type][i] == fn) {
this.user[type].splice(i, 1);
}
}
else {
//发布状态
this.user[type][i]();
}
}
}
};
var rose = {
eat : function () {
this.publish('eat');
},
sleep : function () {
this.publish('sleep')
},
//发布者变为订阅者,处理关心状态
rose_lol : function() {
console.log('一起开黑怎么样?');
}
};
//2. 提供一个工具函数,能够利用发行者对象的模板来快速创建新的发布者;
function makePublisher(obj) {
for (var key in publisher){
//只拷贝实例方法
if(publisher.hasOwnProperty(key) && typeof publisher[key] == 'function')
obj[key] = publisher[key];
}
obj.user = {
}
}
//3. 创建发布者;
makePublisher(rose);
//4. 创建订阅者(当发布者发布消息的时候,订阅者能够收到信息--自动调用订阅者的方法)
var jack = {
jack_eat :function () {
console.log('带女神去吃麻辣烫--jack');
},
jack_sleep :function () {
console.log('给女神唱安眠曲--jack');
},
//订阅者变发布者
lol :function () {
this.publish('lol')
}
};
var tom = {
tom_eat : function () {
console.log('带女神吃火锅');
},
tom_sleep : function () {
console.log('带女神看星星');
},
tom_lol : function () {
console.log('开黑带上我!');
}
};
makePublisher(jack);
// 注册观察者
jack.addUser(rose.rose_lol,'lol');
jack.addUser(tom.tom_lol,'lol');
rose.addUser(jack.jack_eat,'eat');
rose.addUser(tom.tom_sleep,'sleep');
//发布者发布信息
jack.lol();