2017.09.01
Keywords
- 设计模式
Check Out
- 设计模式中的原则
- 设计模式之单例模式
- 设计模式之构造函数模式
- 设计模式之工厂模式
- 设计模式之代理模式
- 设计模式之建造者模式
- 设计模式之命令模式
- 设计模式之观察者模式
- 设计模式之适配器模式
- 设计模式之职责链模式
- 设计模式之迭代器模式
- 设计模式之外观模式
- 设计模式之策略模式
- 设计模式之中介者模式
- 设计模式之原型模式
- 设计模式之模版方法
- 设计模式之装饰者模式
- 设计模式之组合模式
New
设计模式中的原则:根本原因是为了代码复用,增加可维护性
- 开闭原则:对扩展开放,对修改关闭
- 里氏装换原则:子类继承父类,可以直接调用
- 依赖倒转原则:引用一个对象,如果对象有底层类型,直接引用底层
- 接口隔离原则:每一个接口应该是一个角色
- 合成/聚合复用原则:应该直接使用一些已有的对象,使之成为一个新对象的一部分
- 迪米特原则:一个对象应对其他对象了解得尽可能少
站在巨人的肩膀上
设计模式之单例模式
单例模式就是保证一个类只有一个实例。实现的方法一般是判断实例是否存在,如果存在就直接返回,否则就创建了之后再返回。 在 Javascript 中,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来来访问该对象。
模式的作用:
- 模块间通信
- 系统中某个类的对象只能存在一个
- 保护自己的属性和方法
注意事项:
- 注意this的使用(指向调用者)
- 闭包容易造成内存泄漏,不需要的赶快干掉
- 注意new的成本(继承)
实例
// 1. 独立的对象 建2个 xiaowang xiaoli
// 2. 让xiaoli 跟 xiaowang 通过门铃进行通信
// 3. 先看一下 xiaowang 家有没有门,若果有门直接通过门铃通讯,如果没有先建门
// 4. 两个单利之间开始通信
var xiaowang = (function(argument) {
// 类
var xiaowangjia = function(message) {
this.menling = message;
};
// 实例
var men;
var info = {
sendMessage: function(message) {
if(!men) {
// 保证只有一个实例
men = new xiaowangjia(message);
}
return men;
}
};
// 唯一的访问点
return info;
}());
var xiaoli = {
callXiaowang: function(msg) {
var _xw = xiaowang.sengMessage(msg);
alert(_xw.menling);
_xw = null; // 等待垃圾回收
}
};
xiaoli.callXiaowang(msg);
// 页面上6个按钮 abcdef
// $('#a').click(function(){ })
// $('#b').click(function(){ })
// $('#c').click(function(){ })
// $('#d').click(function(){ })
// $('#e').click(function(){ })
// $('#f').click(function(){ })
// a b c => top
// e d f => nav
var top = {
init: function() {
this.render();
this.bind();
},
a: 'btn-a',
render: function() {
var self = this;
self.btnA = $('#a');
}
bind: function() {
var self = this;
self.btnA.click(function(){
self.test();
});
},
test: function() {
a = '5';
}
};
var nav = {
init: function() {
this.render();
this.bind();
},
a: 'nav',
render: function() {
var self = this;
self.btnB = $('#a');
}
bind: function() {
var self = this;
self.btnB.click(function(){
self.test();
});
},
test: function() {
top.a = 'nav visited...'
}
};
top.init();
nav.init();
设计模式之构造函数模式
构造函数用于创建特定类型的对象,不仅声明了使用的对象,还可以接受参数以便于第一次创建对象的时候设置对象的成员值。 在 Javascript 中,构造函数通常是认为用来实现实例的,javascript没有类的概念,但是有特殊的构造函数,构造函数内部使用的this 关键字引用的是新创建的对象。
作用
- 用于创建特定类型的对象
- 第一次声明的时候给对象赋值
- 自己声明构造函数,赋予属性和方法
注意事项
- 声明函数的时候处理业务逻辑(但是不要把所有的业务逻辑)
- 区分单例的区别,配合单例模式实现初始化
- 构造函数首字母大写(建议)
- 注意new的成本(继承,如公用的方法放到原型上)
// 1. 用于创建特定类型的对象
// 2. 首字母大写
// 3. 建议字符串 单引号
var AA = {
Zaomen: function (huawen) {
if(!(this instanceof Zaomen)) {
return new Zaomen();
}
this.suo = 'common';
if (huawen) {
this.huawen = 'common';
}
this.create = function() {
return 'suo' + this.suo + 'huawen' + this.huawen;
}
}
}
var BB = {
Zaomen: function (huawen) {
if(!(this instanceof Zaomen)) {
return new Zaomen();
}
this.suo = 'common';
if (huawen) {
this.huawen = 'common';
}
this.create = function() {
return 'suo' + this.suo + 'huawen' + this.huawen;
}
}
}
var xiaozhang = new Zaomen();
// 也可以写成 var xiaozhang = Zaomen()
xiaozhang.create();
设计模式之工厂模式
工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便于创建的时候指定自己的对象类型(抽象工厂),适用于依赖于很多设置文件时。
作用
- 对象的构建非常复杂
- 需要依赖具体的环境创建不同的实例
- 处理大量具有相同属性的小对象
注意事项
- 不能滥用工厂,有时候仅仅是给代码增加复杂度
实例
var factory = {};
factory.chanyifu = function() {
this.gonren = 50;
alert('我们有'+this.gonren);
}
factory.yunshu = function() {
alert('产鞋子');
}
factory.changzhang = function() {
return new factory
}
设计模式之代理模式
代理模式就是为其他对象提供一种代理以控制这个对象的访问。代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件、资源,内存中的对象,或者是一些难以复制的东西。
作用:
- 远程代理(一个对象将不同空间的对象进行局部代理)
- 虚拟代理(根据需要创建开销很大的对象如渲染网页暂时用占位代替真图)。
- 安全代理(控制真实对象的访问权限)。
- 智能指引(调用对象代理处理另外一些事情如垃圾回收机制)
注意事项:
- 不能滥用代理,有时候仅仅是给代码增加复杂度
实例:
// 代理模式需要3方
// 买家
function maijia() {
this.name = '小明';
}
// 中介
function zhongjie() {
}
zhongjie.prototype.maifang = function() {
new fangdong(new maijia()).maifang(20);
}
// 房东
function fangdong(maijia) {
this.maijia_name = maijia.name;
this.maifang = function(money) {
alert('shoudao')
}
}
(new zhongjie).maifang();
设计模式之建造者模式
建造者模式可以将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。建造者模式实际就是一个指挥者,一个建造者,一个使用指挥者调用具体建造者工作得处结果的客户。主要用于“分布构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法。而复杂对象的各个部分经常变化。
作用:
- 分步创建一个复杂的对象。
- 解耦封装过程和具体创建的组件
- 无需关心组件如何组装
注意事项:
- 需要一个稳定的算法进行支持
- 加工工艺是暴露的
实例:
// 1. 发送一个请求 白富美
// 2. $.ajax 建造者模式 包工头
// 3. 工人完整的工程
$.ajax({
url: 'a.php',
success: function() {
// 工人
}
});
// 1. 产出的东西是房子
// 2. 包工头调用工人进行开工
function Fangzi() {
this.woshi = '';
this.keting = '';
this.chufang = '';
}
function Baogongtou() {
this.gaifangzi = function(gongren) {
gongren.jian_woshi();
gongren.jian_keting();
gongren.jian_chufang();
return gongren.jian_wojiaogong();
}
}
function Gongren() {
this.jian_woshi = function() {
console.log('woshi haole')
}
this.jian_keting = function() {
console.log('keting haole')
}
this.jian_chufang = function() {
console.log('chufang haole')
}
this.jiaogong = function() {
var _fangzi = new Fangzi();
return _fangzi;
}
}
var gongren = new Gongren();
var baogongtou = new Baogongtou();
var fangzi = baogongtou.gaifangzi(gongren);
设计模式之命令模式
命令模式:用来对方法调用进行参数处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行。该模式旨在将函数的调用、请求和操作封装成一个单一的对象,然后对这个对象进行一系列的处理。也可以消除调用操作的对象和实际操作的对象之间的耦合。这为具体的类更换带来了极大的恶灵活性。
作用:
- 将函数的封装、求、调用结合为一体
- 调用具体的函数解耦命令对象与接受对象
- 提高程序模块化的灵活性
注意事项:
- 不需要接口一致,直接调用函数即可,以免造成浪费
实例:
var lian = {};
lian.paobing = function(pao_num) {
alert(pao_num)
}
lian.bubing = function(bu_num) {
alert(bu_num)
}
lian.lianzhang = function(mingling) {
lian[mingling.type](mingling.num);
}
// 总司令开始发令
lian.lianzhang({
type: 'paobing',
num: 100
});
lian.lianzhang({
type: 'bubing'
num: 3000
});
设计模式之观察者模式
观察者模式又叫发布订阅模式(publish/subscribe),它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化就会通知所有的观察者对象,使得它们能够自动更新自己。
作用:
- 支持简单的广播通信,自动通知所有已尽订阅过的对象
- 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性
- 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
注意事项:
- 监听要在触发之前
- 实例:
~(function(){
var o = $({});
o.jianting = function(){
o.on.apply(o,arguments);
}
o.fabu = function() {
o.trigger.apply(o,arguments);
}
o.qingchu = function(){
o.off.apply(o,arguments)
}
})();
$.jianting('/test/ls', function(e,a,b,c){
alert(a+'||' + b + '||' +c);
});
$.jianting('/test/ls', function(e,a,b,c){
alert('ok');
});
$.fabu('/test/ls', [1,2,3]);
设计模式之适配器模式
适配器模式是讲一个类(对象)的接口(方法或属性)转换成客户希望的另外一个接口,适配器模式使得原来由于接口不兼容而不能一起工作的那些类(对象)可以一起工作。
作用:
- 使用一个已经存在的对象,但是其方法或接口不符合你的要求
- 创建一个可复用的对象,该对象可以与其他不相关或不可见的对象协同工作
- 使用已经存在的一个对象货多个对象,但是不能进行继承已匹配它的接口
注意事项:
- 与代理模式的区别,代理模式是不改变原接口,适配是原接口不符合规范
实例:
var aa = {
test: function() {
},
go: function() {
}
}
aa.test()
// 休假美好时光
function pp() {
this.test = function(){
console.log('我是新的test');
}
aa.prototype.gogo = function(first_argument) {
console.log('我是新的gogo');
}
function shipeiqi() {
var s = new pp;
var aa = {
test: function(){
s.test();
}
go: function(){
s.gogo();
}
}
return aa;
}
}
设计模式之职责链模式
职责链模式是多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递改请求,直到有一个对象处理它为止。 链中收到请求的对象要么亲自处理它,要么转发给下一个候选者。提交方并不明确有多少个对象会处理它,任一候选者都可以响应响应的请求,可以在运行时刻决定哪些候选者参与到链中。
作用:
- dom的冒泡有些类似职责链
- nodejs 当 controller 中有很多负责操作逻辑的时候拆分中间件
- 解耦发送者和接受者
注意事项:
- js 中每一次 点 都是有代价的,要在必要的时候应用
实例:
function Laoban(xiangmujingli) {
if( xiangmujingli) {
this.xiangmujingli = xiangmujingli;
}
}
laoban.prototype.write = function(php) {
this.xiangmujingli.write(php);
}
function xiangmujingli (coder) {
if( coder) {
this.coder = coder;
}
}
xiangmujingi.prototype.write = funciton(php) {
this.coder.write(php);
}
function coder (php) {
}
coder.prototype.write = function(php) {
console.log('coding ...' + php);
}
var begin = new laoban(new xiangmujingi(new coder()));
begin.write('php');
设计模式之迭代器模式
迭代器模式提供一钟方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示。
作用:
- 为了遍历不同集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
- 对于集合内部结果常常变化各异,我们不想暴露其内部结构,但又要让客户代码透明访问其中的元素
注意事项:
- 至少要有两个方法, hasNext, Next(),才能做到遍历所有对象
- 遍历的同时更改迭代器所在的集合结构可能会导致的问题
实例:
var arr = ['1', '2', '3'];
var diedai = (function(arr) {
var lenght = arr.length;
var index = 0;
return {
hasNext: function() {
return index < length;
},
next: function() {
var data = arr[index];
index += 1;
return data;
},
reset: function() {
index = 0;
}
}
})(arr);
while(diedai.hasNext()) {
console.log(diedai.next());
}
设计模式之外观模式
外观模式就是为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口使得这一子系统更加容易使用。 外观模式不仅简化类中接口,而且对接口与调用者也进行了解耦。经常被开发者必备,可以将一些复杂的操作封装起来,并创建一个简单的接口用于调用。
作用:
- 在设计初期,应该要有意识地将不同的两个层分离,比如经典的三层结构。
- 在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观可以提供一个简单的接口,减少它们之间的依赖。
- 在维护一个遗留的大型系统时,为系统开发一个外观 Facade 类,为设计粗糙和高度复杂的遗留代码提供比较清晰的接口,让新系统和Facade对象交互。
注意事项:
- 连续使用会产生一定的性能问题,因为在每次调用时都要监测问题性。
实例:
var fuhao = {
}
fuhao.huofang = function(){
return "馒头";
}
fuhao.chuliliangshi = function() {
return "面粉";
}
fuhao.mantou = function() {
this.chuliliangshi();
this.huofang();
}
// 想拿到馒头,第一个需要做的就是让系统产生馒头
fuhao.men = function() {
return this.mantou();
}
设计模式之策略模式
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。
作用:
- 所有的这些算法都是做相同的事情,只是实现不同。
- 相同的方式调用所有的方法,减少了各种算法类于使用算法类之间的耦合。
- 单独定义算法类,也方便了单元测试
注意事项:
- 不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。
实例:
var input = $('#input').val();
var val = {
isEmpty: function() {
},
isTel: function() {
}
};
var ise = val.isEmpty(input.val());
var isTel = val.isTel(input.val());
if(!ise $$ isTel) {
alert('通过审核');
}
$.input.val({
isEmpty: false,
isTel: true
});
设计模式之中介者模式
中介者模式(Mediator),用一个中介对象封装一系列的对象交互。中介者使个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的关系。
作用:
- 软件开发中,中介者是一个行为设计模式,通过提供一个统一的接口让系统的不同部分进行通信。一般,如果有系统有很多字模块需要直接沟通,都要创建一个中央控制点让其各模块痛哟中央控制点进行交互。终结者模式可以让这些字模块不需要直接沟通,而达到进行解耦的目的。
注意事项:
- 当系统出现了多对多关系复杂难以管理时才考虑引入。
实例:
var Feiji = function (name) {
this.name = name;
}
Feiji.prototype.send = function(msg.to) {
tatai.send(msg, to);
}
Feiji.prototype.jieshou = function(msg) {
console.log(this.name + '' + msg);
}
var tatai = {
all: {},
zhuce: function(feiji) {
this.all[feiji.name] = feiji;
}
send: function(msg, to) {
this.all[to.name](msg);
}
}
var feiji1 = new Feiji('feiji1');
var feiji2 = new Feiji('feiji2');
设计模式之原型模式
原型模式是指用原型实例指向创建对象的种类,并通过拷贝这些原型创建新的对象。
作用:
- 原型对象本身就是有效地利用了每个构造器创建的对象
注意事项:
- 注意浅拷贝和深拷贝问题
实例:
var myobj = {
str: 'mystring',
num: 1,
myarr: [],
myobj: {
innerobj: {
test: 25
},
innerstr: 'myobjInnerstr'
}
};
// 浅拷贝
function clone(obj) {
var ret = {}, k, b;
for(k in obj) {
ret[k] = obj[k];
}
return ret;
}
function deepClone(obj) {
var ret = {}, k, b;
if ( (b = (obj instanceof Array)) || obj instanceof Object) {
ret = b ? [] : {};
for(k in obj) {
ret[k] = deepClone[obj[k]]
}
}
return ret;
}
var result = clone(myobj);
result.myobj.innerstr = 'outter';
console.log(result);
设计模式之模版方法
模版方法定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模版方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。是一种代码复用的基本技术。模版方法导致一种反向控制。
作用:
- 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现
- 各子类中公共的行为应被提取出来并集中到一个公共父类中的避免代码复用
- 控制子类扩展,模版方法只在特定点调用钩子操作,这样就允许在这些点进行扩展
注意事项:
- 和策略模式不同,模版方法使用继承来改变算法的一部分,而策略模式使用委托来改变整个算法
实例:
function God() {
}
God.prototype.zaoren_yanjing = function(first_argument) {
cosole.log('eye');
};
God.prototype.zaoren_bizi = function(first_argument) {
cosole.log('bizi');
};
God.prototype.zaoren_zuiba = function(first_argument) {
cosole.log('zuiba');
};
God.prototype.aihao = function(first_argument) {
throw new Error('hook only, you explore by you self');
}
function Xiaoming() {
console.log('小明是上帝的子类');
God.call(this);
}
Xiaoming.prototype = new God();
Xiaoming.prototype.aihao = function() {
console.log('小明爱讲笑话');
}
设计模式之装饰者模式
装饰者提供比继承更有弹性的替代方案,用于包转同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用。 装饰者用于通过重载方法的形式添加新功能。该模式可以在被装饰者前面或者后面加上自己的行为已达到特定的目的。
作用:
- 是一种实现继承的替代方案。当脚本运行时,在子类中增加行为会影响原类所有实例,而装饰者不然。取而代之的是它能给不同对象各自添加新行为。
- 添加辅助的额外功能
- 把类(函数)的核心职责和装饰功能区分开了
注意事项:
- 装饰的类跟被装饰的类,要求拥有相同的访问接口方法(功能)。
- 装饰类的要有对被装饰类的引用,用于在装饰类的相应方法。调用相应被装饰类方法,然后对其进行修饰。
- 把每个要装饰的功能放在单独的函数里
实例: ```js var Fangzi = function () {
} Fangzi.prototype.kongjian = function () { console.log('我是空的房间') } var Zhuangshi = function (fangzi) { this.zfangzi = fanzi; } Zhuangshi.prototype.kongjian = function() { this.zfangzi.kongjian(); console.log('我添加了一个家具'); } var fangzi = new Fangzi(); var zhuangshi = new Zhuangshi(fangzi); zhuangshi.kongjian();
### 设计模式之组合模式
组合模式将对象组合成树形结构以表示“部分-整体” 的层次结构,
组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 作用:
1. 表示对象的部分-整体层次结构
2. 用户将统一的使用组合结构中的所哟方法
- 注意事项:
1. 该模式经常喝装饰者一起使用,他门通常有一个公共的父类(原型),因此必需支持具有 add, remove, getChild等操作的 component 接口。
- 实例:
```js
function zhengti () {
}
Zhengti.prototype.kafei = function() {
throw new Error('不能直接使用');
}
Zhengti.prototype.mianbao = function() {
throw new Error('不能直接使用');
}
function Guke () {
}
Guke.prototype.daincan = function() {
// var t =new Zhengti();
// t.kafei();
this.fafei();
this.mianbao();
}
Guke.prototype.kafei = function() {}
Guke.prototype.mianbao = function() {}