当我与前端接触两个或三个月时,我刚刚听说设计模式非常重要,因此我阅读了一本有关设计模式的书并阅读了其中的一些。我不知道这些设计模式是为了什么设计的,然后我没有再次阅读它们。后来,当我自己做一些小型项目时,我觉得我不需要使用设计模式。现在,我已经与前端接触了半年,我决定重新看一下设计模式,这可能是一些灵感。因此,我找到了一本好书 - “ JavaScript设计模式”。这很容易理解,并用故事串成整本书。阅读了一些内容后,我发现在编写代码时,我不小心使用了一些设计模式,然后我不禁阅读了全部内容。阅读完整的书籍完全改变了我以前的设计模式看法,并在实际项目开发方面学到了很多经验。在这里,我将简要总结这本书,可以认为这是为您的未来参考的注释。 (通常是晦涩难懂的定义。您可以首先查看使用情况,然后回来了解相关定义)
让我首先给你一个指向这本书的链接:
什么是设计模式
设计模式是代码设计经验的摘要,以重复使用代码,确保代码的可靠性等。设计模式主要分为三种类型:创建模式,结构模式和行为模式。本书还写了两种其他类型的设计模式,技能模式和体系结构模式。 JavaScript设计模式基于面向对象的编程。在C ++和Java中,JavaScript中面向对象的编程与传统面向对象的编程之间存在一些差异。当我第一次与JavaScript接触时,这让我感到非常痛苦,但是我只能依靠自己来积累和慢慢思考。如果您想继续理解JavaScript设计模式,则必须首先了解JavaScript面向对象的编程,否则,它只会使您更加痛苦。
创建设计模式
创意设计模式是一种涉及对象创建的设计模式。他们以某种方式控制对象的创建,以避免可能的设计问题或在创建基本对象时增加设计复杂性。创意设计模式主要包括简单的工厂模式,工厂方法模式,抽象工厂模式,构建器模式,原型模式和Singleton模式。让我们一一谈谈它们。
简单的工厂模式
作者将简单的工厂模型与神奇的魔术师进行了比较。
定义
工厂对象也称为静态出厂方法,确定了某个产品对象类的实例的创建,该实例主要用于创建相同类型的对象。
使用方案
阅读上述定义后,我必须感到困惑。这是什么?让我举一个例子来解释它。例如,体育商品商店出售体育设备,其中包含许多体育用品和相关的介绍。当您来体育用品商店购买篮球时,只需询问销售人员,他会帮助您找到想要的东西。实现如下:
// 篮球基类
var Basketball = function() {
this.intro = '篮球盛行于美国';
};
Basketball.prototype = {
getMember: function() {
console.log('每个队伍需要5名队员');
},
getBallSize: function() {
console.log('篮球很大');
}
};
// 足球基类
var Football = function() {
this.intro = '足球盛行于美国';
};
Football.prototype = {
getMember: function() {
console.log('每个队伍需要11名队员');
},
getBallSize: function() {
console.log('篮球很大');
}
};
// 运动工厂
var SportsFactory = function(name) {
switch(name) {
case 'NBA':
return new Basketball();
case 'wordCup':
return new Football();
}
};
当您使用该运动工厂时,请记住SportsFactory Factory对象,它将帮助您找到所需的东西。
简单工厂模型的概念是创建对象。上面的示例是实例化不同的类,但是简单的工厂模型还可以创建相似的对象,提取相似的内容并以目标方式与它们进行处理,而没有相似性。这样,您可以通过简单地创建一个对象来替换多个类。
收获和摘要
团队发展与个人不同,并且对全球变量有很大的限制。您应该尝试尽可能少地创建全局变量。如果相同类型的对象在不同的需求中重复使用,则大多数对象无需重新创建,并且您需要学习如何重复使用代码。使用简单的工厂创建对象可以减少全局变量创建并改善代码重复使用,其使用限制是创建一个对象。
工厂方法模式
作者将工厂方法模型与名片进行了比较。
定义
通过抽象产品类,它创建了主要负责创建多个产品实例的业务。
使用方案
在实际发展中,要求的变化是正常的。一开始,要求很简单,您可以直接创建对象。如果有太多类似的要求,您可以使用简单的工厂方法来重构。但是,如果需求不断变化,您不仅需要修改出厂功能,还需要添加类,仅此而已。工厂方法模式最初旨在推迟实际创建对象到子类。
// 工厂类
var Factory = function(type, content) {
if(this instanceof Factory) {
var s = new this[type](content);
return s;
} else {
// 防止使用者不知道这是一个类,忘了加new操作符创建,导致全局变量污染
return new Factory(type, content);
}
};
Factory.prototype = {
Java: function(content) {
// ...
},
JavaScript: function(content) {
// ...
},
php: function(content) {
// ...
}
};
这样,如果将来想添加其他类,则只需要在工厂原型中添加它们即可。
收获和摘要
为了创建许多类的对象,简单的工厂模式不合适。通过出厂模式,可以轻松地创建它,以便为多个类的实例对象创建,并且可以避免用户和对象类之间的耦合。用户不必关心创建对象的特定类别,他们只需要调用工厂方法即可。
抽象的工厂模式
抽象的工厂模式使您感到一切出现都是幻觉。
定义
通过工厂抽象的课程,其业务用于创建产品类别簇,而无需对某些类别的产品实例负责。
抽象类
抽象类是被声明但无法使用的类,当您使用时会报告错误。 JavaScript中的抽象类不能像传统的面向对象的语言一样容易创建。我们可以在类方法中手动丢弃错误以模拟抽象类。您可能想知道,这样的班级的用途是什么?实际上,它在继承中非常有用。
使用方案
抽象的工厂模式不能用于创建具体对象,并且通常用作父母类创建一些子类。
// 抽象工厂方法
var VehicleFactory = function(subType, superType) {
// 判断抽象工厂中是否有该抽象类
if(typeof VehicleFactory[superType] === 'function') {
// 缓存类
function F() {};
// 继承父类属性和方法
F.prototype = new VehicleFactory[superType]();
// 将子类构造函数指向子类
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('抽象方法不能调用')
}
};
// 公交车抽象类
VehicleFactory.Bus = function() {
this.type = 'bus';
};
VehicleFactory.Bus.prototype = {
getPrice: function() {
return new Error('抽象方法不能调用');
}
};
抽象工厂实际上是子类继承父班的一种方法。在此方法中,有必要通过子类并继承父级的名称。
收获和摘要
抽象的工厂模式是设计模式中最抽象的,也是创建模式中唯一的抽象创建模式。该模式创建的结果不是真实的对象实例,而是指定类的结构的类群集。
建筑商模式
建筑商模型告诉我们,分裂是统一的。
定义
将复杂对象的构造层与其演示层分开,相同的构造过程可以采用不同的表示。
应用程序方案
现在有必要发布简历,这是为了帮助其他人在公司网站上发布简历。但是,这些简历有必要。除了在页面上发布兴趣和爱好以及一些专业外,其他信息(例如联系信息)不应在网站上发布,并且每个人都想找到的工作都可以分类。这样,我们需要创建更多的东西。目前,前三个工厂型号不合适,因此我们可以在此处使用构建器模型。
构建器模式与仅关心创建结果的工厂模式不同。尽管其目的是创建一个对象,但它更关注创建此对象的整个过程。在这种情况下,我们不仅需要候选人的榜样,而且还需要在创建过程中对候选人的利益和爱好的关注。
// 创建一位人类
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.FirstName = name.slice(name.indexOf(' '));
}
})(name, that);
};
// 实例化职位类
var Work = function(work) {
var that = this;
// 构造器,通过传入的职位特征来设置相应职位及描述
(function(work, that) {
switch(work) {
case 'code':
that.work = '工程师';
break;
case 'UI':
case 'UE':
that.work = '设计师';
break;
case 'teach':
that.work = '教师';
break;
default:
that.work = work;
}
})(work, that);
};
// 更换期望的职位
Work.prototype.changeWork = function(work) {
this.work = work;
};
让我们创建一个申请人
// 应聘者创建类
var Person = function(name, work) {
// 创建应聘者缓存对象
var _person = new Human();
// 创建应聘者姓名解析对象
_person.name = new Named(name);
// 创建应聘者期望职位
_person.work = new Work(work);
// 返回创建的应聘者对象
return _person;
}
收获和摘要
构建器模式与以前的创建设计模式不同。它关心对象的整个创建过程,因此通常会模块化创建对象的类,以便可以灵活地使用创建类的每个模块,并以高质量的质量重复使用。这种方法无形地增加了整个对象类分裂的结构的复杂性。因此,如果对象粒度很小,或者模块之间的重用速率非常低,则不建议使用构建器模式。
原型模式
原型模式是JavaScript语言的灵魂。
定义
使用原型实例指向创建对象的类,以及使用它创建新对象的类来共享原型对象的属性和方法。
使用方案
这仍然是关于父母类的子类继承的问题。为了提高性能,可以将每次创建的一些简单和差异化的属性放在构造函数中,并且可以将消耗更多资源的一些方法放置在基类原型中,以便可以避免不必要的消费。这是原型模式。
原型模式在对象创建中更多地使用,例如创建实例对象的构造函数更复杂或耗时,或者通过创建多个对象来实现。目前,最好不要使用新的关键字来复制这些基础类。您可以通过复制这些对象属性或方法来创建它们。首先,有一种针对原型对象的复制方法。
// 原型对象复制方法
function prototypeExtend() {
var F = function() {},
args = arguments,
i = 0,
len = args.length;
for (; i < len; 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.speed);
},
run: function() {
console.log('奔跑速度' + this.speed);
}
})
这样,通过protypeextend创建的对象是一个对象,并且无需使用新的对象来创建新的实例对象。
收获和摘要
原型模式实际上是一种继承,它允许多个对象共享同一原型对象的属性和方法。这种继承实现不需要创建,而是与那些继承的对象共享原型对象。原型对象更适合继承的实现,这些实现在创建复杂对象时与方法共享更稳定的属性,以及当需求不断变化时,对象结构正在不断变化。
单例模式
哈哈,让你感觉到下一个人的孤独感。
定义
也称为单片模式,仅允许一次实例化的对象类。有时,您还可以使用对象计划名称空间并以有序的方式管理对象上的属性和方法。
使用方案
Singleton模式应该是JavaScript中最常见的设计模式,并且通常为我们提供一个名称空间,以防止不同人的命名变量之间的冲突。您也可以使用它来创建一个小型代码库。
var A = {
Util: {
util_method1: function() {},
util_method2: function() {}
},
Tool: {
tool_method1: function() {},
tool_method2: function() {}
},
Ajax: {
ajax_method1: function() {},
ajax_method2: function() {}
}
...
}
如果要使用此代码库,则可以这样访问:
A.Util.util_method1();
A.Tool.tool_method2();
收获和摘要
单例图案有时称为单体图案。这是一个仅允许一次实例化的对象类,有时也是为了节省系统资源。 JavaScript中的Singleton模式通常被用作名称空间对象。通过Singleton对象,我们可以组织每个模块的代码。
结构设计模式
结构设计模式集中于如何将类或对象组合到更大,更复杂的结构中以简化设计。主要包括外观模式,适配器模式,代理模式,装饰器模式,桥梁模式,组合模式和享受模式。
外观模式
作者将该模型与软件包服务进行比较。
定义
为一组复杂的子系统界面提供了更高级的统一接口,通过该界面访问子系统接口的访问更加容易。在JavaScript中,有时将其用于统一封装基础结构兼容性,以简化用户使用。
使用方案
将单击事件添加到页面文档的文档对象时,如果您直接使用OnClick来绑定事件,则如果团队中的某人想绑定文档的单击事件,则您以前绑定的时间将被覆盖,因为这是DOM0级别事件。我们应该使用DOM2级事件处理程序提供的AddEventListener来实现它。但是,IE的旧版本不支持此方法,必须使用attachEvent。如果我们编写一种与所有浏览器兼容的方法,并且可以使用外观模式,这将更加方便。提供了具有统一功能但不是统一方法的接口的统一接口。
// 外观模式实现
function addEvent(dom, type, fn) {
// 对于支持DOM2级事件处理程序的浏览器
if(dom.addEventListener) {
dom.addEventListener(type, fn, false);
// 对于不支持addEventListener但支持attachEvent的浏览器
} else if(dom.attachEvent) {
dom.attachEvent('on' + type, fn);
} else {
dom['on' + type] = fn;
}
}
解决浏览器兼容性问题只是外观模式应用的一部分。许多代码库使用外观模式封装多个功能,以简化基础制造方法。
收获和摘要
当复杂的系统提供一系列复杂的接口方法时,它将使系统易于管理接口方法并变得更加复杂。通过外观模式,接口的次要封装可以隐藏其复杂性。
适配器模式
当我听到这个消息时,我想到了弯曲的水管的现场吗?
定义
将一个类(对象)的接口(方法或属性)转换为另一个接口,以满足用户需求,并通过适配器(对象)之间的接口之间的不兼容问题。
使用方案
该公司的活动页面正在使用该公司内部开发的A框架,但是许多新同事总是认为很难使用A框架制定新的功能要求,并且使用有限的方法。为了允许新同事尽快将其集成到项目开发中,他们可以引入JQuery框架。由于A框架与JQuery Framework非常相似,因此您可以编写一个适配器,而无需在JQuery中编写所有以前的代码。
适配器模式不仅在编程中很常见,而且在生活中也很普遍。例如,三角形插头充电器不能用于两个插头。目前,需要一个三个项目到两项插头电源适配器。这是一个适配器模式。实际上,这是为在两个代码库中编写的代码编写的额外代码以运行兼容。
JavaScript中的适配器模式也可以适用于两个代码库,这些代码库适用于参数,适用于数据,适用于服务器数据等。以参数改编为示例。
function doSomeThing(name, title, age, color, size, prize){}
很难记住这些参数的顺序,因此我们经常作为参数对象传递,如下所示:
/**
* obj.name: name
* obj.title: title
* obj.age: age
* obj.color: color
* obj.size: size
* obj.prize: prize
***/
function doSomeThing(obj){}
但是,在调用时,无法确定传递的参数是否完成。如果某些参数必须未传递,则某些参数具有默认值等。此时,您可以使用适配器适应传递的参数对象。
function doSomeThing(obj) {
var _adapter = {
name: '雨夜清荷',
title: '设计模式',
age: 24,
color: 'pink',
size: 100,
prize: 50
};
for(var i in _adapter) {
_adapter[i] = obj[i] || _adapter[i];
}
}
收获和摘要
JavaScript中的适配器在对象之间更多地使用。为了使对象可用,通常会拆分和重新包装对象,因此您需要了解适配器对象的内部结构,这也是与外观模式的差异。
代理模式
您是否曾经想过在喜p桥上的Cowherd和Weaver女孩会议的场景?
定义
由于一个对象无法直接引用另一个对象,因此有必要通过代理对象介导两个对象。
使用方案
跨域问题应该是使用代理模式解决的最典型问题之一。随着用户模块上传的照片数量越来越大,服务器需要将上传模块重新部署到另一个域,这导致了跨域问题。我们可以将上传模块位于两个对象的专辑页和服务器中抽象。如果我们想在域两端的对象之间进行通信,则需要找到一个代理对象来实现它们之间的通信。
代理对象有许多类型。诸如IMG之类的简单标签可以通过SRC向其他域下的服务器发送请求。但是,这种类型的请求是get请求,这是单向的,不会有响应数据。代理对象的另一种形式是通过脚本标签。我们需要的代理对象是在页面和浏览器之间进行通信,而JSONP实现了代理模式。我们知道SRC属性可以实现GET请求,因此我们可以将一些字段信息添加到SRC指向的URL地址。服务器获取这些字段信息并相应地生成一个内容。
// 前端浏览器页面
// 另一个域下的服务器请求接口
可以将这种方法想象为合理的船,通过它将您的请求发送到另一边,然后另一侧的人们将数据放入船上并为您带回。
收获和摘要
除了在跨域问题中使用许多应用程序外,代理模式有时会花费大量资源来实例化对象。例如,在页面加载的早期阶段,有许多文件加载。目前,在页面的第一个屏幕上延迟加载一些图片非常有益。通过代理,您可以先加载预览图像,然后加载图片高高的开销。
可以看出,代理模式可以解决系统与大型系统资源间接开销之间的耦合问题。可以保护代理对象,并且代理对象不受外界的影响。
装饰器模式
显然,房屋装饰是典型的装饰型号。
定义
在不更改原始对象的情况下,将其包装(添加属性或方法)以启用原始对象以满足用户更复杂的需求。
使用方案
静止是相对的,运动是绝对的,因此没有不断变化的需求。在实际的项目开发中,要求总是在变化。当原始功能无法再满足用户的需求时,我们需要做的就是在此基础上添加砖和瓷砖,设置新功能和属性以满足用户提出的需求。这就是装饰型号需要做的。
// 装饰者
var decorator = function(input, fn) {
// 获取事件源
var input = document.getElementById(input);
// 若事件源已经绑定事件
if(typeof input.onclick === 'function') {
// 缓存事件源原有回调函数
var oldClickFn = input.onclick;
// 为事件源定义新的事件
input.onclick = function() {
// 事件源原有回调函数
oldClickFn();
// 执行事件源新增回调函数
fn();
}
} else {
input.onclick = fn;
}
}
收获和摘要
除了装饰器模式外,适配器模式还可以扩展原始对象。不同之处在于,适配器的扩展通常是对物体内部结构的重组,因此有必要理解其自身的结构。装饰器图案将物体扩展为良性扩展。无需了解其特定的实现,但仅在外部进行封装的扩展。
桥模式
作者将该模型与城市之间的道路进行了比较。
定义
尽管系统沿多个维度发生变化,但它并没有增加其复杂性并达到脱钩。
使用方案
有时,由于类似的逻辑,页面上的一些小细节通常会导致大量肿的代码,从而使页面苦涩。现在,该项目有一个要求,即在页面上部的用户信息中添加鼠标笔触的一些特殊效果。但是,用户信息由许多小部件组成。对于用户名,鼠标的笔触直接更改背景颜色,但是诸如用户级别和用户消息之类的组件只能在内部更改数字内容,并且处理逻辑是不同的。这需要很多代码才能编写,但会感到非常多余。目前,我们必须首先提取共同点并抽象提取我们认为的抽象逻辑。
对于用户信息模块的每个部分,将鼠标滑过并离开两个事件的执行函数相似,例如,它们都在每个组件中处理一个元素,并且都使用元素的字体颜色和背景颜色处理。您可以创建这样的函数来揭开此功能。
function changeColor(dom, color, bg) {
// 设置元素的字体颜色
dom.style.color = color;
// 设置元素的背景颜色
dom.style.background = bg;
}
接下来是绑定特定元素的时间,但是仅仅知道元素事件绑定和抽象提取的样式方法的变换是不够的。他们需要与方法链接在一起。此方法是桥接方法,此模式是桥梁模式。就像开车去道湾时一样,您需要找到一条连接北京和雪阳的道路,以在两个地方成功旅行。
对于事件的桥接方法,可以使用匿名功能。
var spans = document.getElementsByTagName('span');
spans[0].onmouseover = function() {
changeColor(this, 'red', '#ddd');
}
收获和摘要
桥模式的主要特征是将实现层(例如元素绑定事件)与抽象层(例如修改Page UI逻辑)分开并将其分开,以便可以独立更改两个部分。桥模式主要是在结构之间解开。
组合模式
作者将组合模式与美味的午餐进行了比较,这感觉很生动。
定义
也称为零件模式,对象被合并为树结构,以表示“部分整体”的层次结构。组合模式允许用户与单个对象和组合对象的使用一致。
使用方案
为了加强主页的用户体验,项目经理正在准备在用户主页上添加新闻模块。当然,新闻的内容是根据用户通常关注的内容开采的。因此,有些人可能会显示短信新闻,有些人可能有图片新闻等。
让我们首先仔细分析这一要求。要求中的新闻大致分为几种独立类型。在修改某种类型的新闻时,它不会影响其他类型的新闻。这样,每种新闻都可以在面向对象的编程中抽象成一类,然后选择其中一些新闻类以将其组合到所需的模块中。目前,您可以使用组合模式。
在页面中,组合模式更常用于创建表单中,例如注册页面可能具有不同的表单提交模块。对于这些需求,我们只需要拥有一个基本的个体,然后通过一定的组合来实现它。
收获和摘要
组合模式可以为我们提供清晰的组成结构。组合对象类通过继承同一父类具有统一方法,这也有助于统一的管理和使用。
享受元模式
作者将武士模式与城市巴士进行了比较,因此他可以仔细考虑。
定义
使用共享技术有效地支持大量细颗粒对象,以避免由于对象之间具有相同内容而导致的不必要的开销。
使用方案
现在有太多新闻内容,我们有一个分页来显示所有新闻需求。一种简单而直观的方法是在加载页面后异步请求新闻数据,然后在页面中创建所有新闻插入,并显示您需要显示的页面。但是,这样做是一个很大的问题。立即创建数百个新闻并同时插入页面将导致多个页面的开销严重影响网页的性能。这里的所有新闻都有类似的结构,但是内容有所不同。武模型可以解决由相同结构引起的不必要的开销问题。
Xiangyuan模型主要共享并将其数据和方法分开,并将数据和方法分为内部数据,内部方法,外部数据和外部方法。内部方法和内部数据是指类似或常见的数据和方法,因此提取它们会减少开销。在上面的示例中,所有新闻个人都有一个共同的结构,应用作内部数据,而绑定到下一页按钮的事件是外部方法。同时,为了使用内部数据,需要提供一种操作方法。
var Flyweight = function() {
// 已创建的元素
var created = [];
// 创建一个新闻包装容器
function create() {
var dom = document.createElement('div');
// 将容器插入新闻列表容器中
document.getElementById('container').appendChild(dom);
// 缓存新创建的元素
created.push(dom);
// 返回创建的新元素
return dom;
}
return {
// 获取创建新闻元素方法
getDiv: function() {
// 如果已创建的元素小于当前页元素总个数(5个),则创建
if(created.length < 5) {
return created();
} else {
// 获取第一个元素,并插入去后面
var div = created.shift();
created.push(div);
return div;
}
}
}
}
上面创建了一个武士班。由于每个页面只能显示5个新闻,因此在Xiangyuan类中创建5个元素。您可以通过GetDiv方法获得创建的元素。以下是实施外部数据和外部方法。外部数据是我们要显示的所有新闻内容。由于每个内容都是不同的,因此绝对不会共享。首先,我们想根据新闻内容实例化页面,然后将单击事件绑定到下一页以显示下一页。
var paper = 0,
num = 5,
len = article.length;
// 添加五条新闻
for(var i = 0; i < 5; i++) {
if(article[i])
// 通过享元类获取创建的元素并写入新闻内容
Flyweight.getDiv().innerHTML = article[i];
}
// 下一页按钮绑定事件
document.getElementById('next_page').onclick = function() {
// 如果新闻内容不足5条则返回
if(article.length < 5) {
return;
}
var n = ++paper * num % len, // 获取当前页的第一条新闻索引
j = 0;
// 插入5条新闻
for(; j < 5; j++) {
// 如果存在n+j条则插入
if(article[n + j]) {
Flyweight.getDiv().innerHTML = article[n + j];
// 否则插入起始位置第n+j-len条
} else if(article[n + j - len]) {
Flyweight.getDiv().innerHTML = article[n + j - len];
} else {
Flyweight.getDiv().innerHTML = "";
}
}
}
这样,在使用享受模式来重构页面后,每个操作只需要5个元素,这可以大大提高性能。
收获和摘要
武模式的应用是提高程序的执行效率和系统性能。因此,它被广泛用于大规模系统开发中,并且可以避免程序中的数据重复。申请时,您必须找到内部状态和外部状态,以便可以更合理地提取和分离。
行为设计模式
行为设计模式用于在不同对象或抽象算法之间划分责任。行为设计模式不仅涉及类和对象,而且还涉及类或对象之间的通信模式并已实现。行为设计模式主要包括模板方法模式,观察者模式,状态模式,策略模式,责任链模式,命令模式,访问者模式,中介模式,备忘录模式,迭代器模式和解释器模式。如此多的模式确实被消化了一段时间。
模板方法模式
作者将这种模型与画猫和画老虎进行了比较。
定义
在父类中定义了一组操作算法骨架,并将某些实现步骤延迟到子类,以便子类可以重新定义算法中的某些实现步骤,而无需更改父类算法结构。
使用方案
提示盒已归一化。网站有很多页面。如果每个页面的弹出框样式不是很一致,那么它看起来就不会很和谐,并且需要统一它们的样式。新手最直观的想法是一一修改每个页面。当然,这非常昂贵。我们需要编写一个弹出框插件,封装这些弹出式框,然后致电每个页面。这是您可以在此插件中使用的模板方法模式,也无需重复多种样式。
模板方法模式是绘制要统一的多个模型的抽象,并从中抽象出最基本的模板。该模板可以用作实体或抽象对象。其他模块只需要继承此模板方法,还可以扩展某些方法。
例如,在我们的生活中,我们将蛋糕用作制造蛋糕,而我们制作的蛋糕具有相同的外观,因为它们都使用相同的模具。但是,商店中出售的蛋糕各种类型,都是蛋糕的次要加工。我们要求中的基本提示盒是我们抽象的模具。其他提示盒的功能比此提示框更多。我们只需要对它们进行一些辅助处理即可满足需求。
模板方法不仅在标准化组件时使用,而且有时在创建页面时非常常用,例如创建三种类型的导航。第一种类型是基本类型,第二种类型具有更多的消息提醒功能,并且第三种类型具有更多的URL函数以后显示。这也可以使用模板方法来实现。目前,抽象的基类是最简单的基本导航类。
// 格式化字符串方法
function formateString(str, data) {
return str.replace(/\{#(\w+)#\}/g, function(match, key) {
return typeof data[key] === undefined ? '': data[key]
});
}
// 基础导航
var Nav = function(data) {
// 基础导航样式模板
this.item = '{#name#}';
// 创建字符串
this.html = '';
// 格式化数据
for(var i = 0, len = data.length; i < len; i++) {
this.html += formateString(this.item, data[i]);
}
// 返回字符串数据
return this.html;
}
对于消息提醒类导航类,您只需要添加一个其他消息提醒组件模板,并使用消息提醒组件模板装饰传入的URL数据,以获取所需的字符串并通过调用从基类继承的方法来处理这些字符串。
var NumNav = function(data) {
// 消息提醒信息组件模板
var tpl = '{#num#}';
// 装饰数据
for(var i = data.length - 1; i >= 0; i--) {
data[i].name += data[i].name + formateString(tpl, data[i]);
}
// 继承基础导航类
return Nav.call(this, data);
}
收获和摘要
模板方法的核心在于重复使用方法,将基数核心方法封装在基类中,允许子类继承基类方法,实现基类方法共享并实现方法共享。子类继承的方法是可扩展的,它需要重写基类继承的方法。
观察者模式
作者将该模型与通信卫星进行比较。
定义
它也称为发布订阅模式或消息机制,它定义了依赖关系关系,该关系解决了主题对象与观察者之间功能的耦合。
使用方案
在团队发展中,一个人通常负责一个模块,那么每个人应如何负责在模块之间进行交流?例如,如果您实现了一些要求,则需要添加一些代码,但是此要求需要其他模块的合作,但是每个模块都由不同的人编写。由于新添加的代码,您不想影响他人实施的功能。此时,您需要使用观察者模式。
观察者模式是解决主题对象和观察者之间的耦合。例如,每个国家目前都在开发和推出卫星。这些卫星的发布是为了监视一些信息,因此可以将其视为观察者或消息系统。如果允许卫星导航飞机,则飞机是观察者或主题对象。然后,如果地面上的转移站或其他飞机需要了解飞机的信息,那么每当飞机到达一个地方时,座椅的信息将被发送到卫星,卫星将把信息广播到已经订阅飞机的转运站,以便避免使用某些飞机事故。
目前,观察者需要至少有两种方法,一种是从某个飞机接收消息,另一个是向订阅的公交站发送响应消息。但是,并非每个转移站都需要始终监视飞机的状态,因此也有一种取消注册的方法。 Of course, these messages still need to be saved, so a container for saving messages is needed. At this time, the prototype of the observer comes out. He has a message container and three methods, subscribe to the message method, unsubscribe to the message method, and send the subscription message method.
var Observer = (function() {
// 防止消息队列暴露而被篡改,故将消息容器作为静态私有变量保存
var __messages = {};
return {
// 注册信息接口
regist: function() {},
// 发布信息接口
fire: function() {},
// 移除信息接口
remove: function() {}
}
})();
The following is to implement these interfaces yourself.
Harvest and summary
The observer pattern is mainly about solving the coupling between classes or objects, decoupling two interdependent objects, making them dependent on the observer's message mechanism. In this way, for any subscriber, the changes in other subscriber objects will not affect themselves. They can be either the sender of the message or the executor of the message, which depends on which of the three methods in the observer object (subscribe, log out, and publish a message).
Status Mode
The author likens this pattern to Super Mary.
定义
When an object's internal state changes, it causes a change in its behavior, which looks like it changes the object.
使用方案
When writing code, you often encounter many conditional judgment statements when writing code. So how to reduce the conditional judgment statements in the code? For the management of independent results within this type of branch conditions, the state mode can be used. Each condition is a state of the object. Facing different judgment results, it is actually a state within the object.
The different judgment results are encapsulated within the state object, and the state object then returns a callable interface method to call a certain method inside the state object.
// 投票结果状态对象
var ResultState = function() {
// 判断结果保存在内部状态中
var States = {
// 每种状态作为一种独立方法保存
state0: function() {
console.log('这是第一种情况'):
},
state1: function() {
console.log('这是第二种情况'):
},
state2: function() {
console.log('这是第三种情况'):
},
state3: function() {
console.log('这是第四种情况'):
}
}
// 获取某种状态并执行对应方法
function show(result) {
States['state' + result] && States['state' + result]();
}
return {
// 返回调用状态方法接口
show: show
}
}();
If you want to call the third result, you can call it as follows
ResultState.show(3);
For state mode, the main purpose is to convert different results of conditional judgment into the internal state of the state object. This internal state is generally used as a private variable of the state object, and then provide an interface method object that can call the internal state of the state object.
Harvest and summary
The state mode not only solves the problem of bloated branch judgment statements in the program, but also converts each branch into a state independently, which facilitates the management of each state without traversing all branches every time.
Policy Mode
The author compared this model to the living Zhuge.
定义
Encapsulate a defined set of algorithms so that they can be replaced by each other. The encapsulated algorithm has a certain degree of independence and will not change with the client.
使用方案
At the end of the year, the company's product exhibitions and sales pages will carry out major promotions. On Christmas, some products are sold at 50% off, some products are sold at 20% off, and some products are sold at 10% off. A lucky feedback event will be held on New Year's Day. Ordinary users will get 30% off, and high-end VIP users will get 50% off. At this time, the above status mode is not applicable, because there is only one promotional situation for each product every day, and you can use the strategy mode at this time.
Structurally, it is very similar to the state mode. It also encapsulates an object internally, and then implements calls to the internal objects through the returned interface object. The difference is that the policy mode does not require management of states, there is no dependency between states, the sword of the strategy can be replaced by each other, and some independent algorithms are saved inside the policy object. Take a look at the implementation of the policy object:
// 价格策略对象
var PriceStrategy = function() {
// 内部算法对象
var strategy = {
// 100返30
return30: function(price) {},
// 100返50
return50: function(price) {},
// 9折
percent90: function(price) {},
// 8折
percent80: function(price) {},
// 5折
percent50: function(price) {},
}
// 策略算法调用接口
return function(algorithm, price) {
return strategy[algorithm] && strategy[algorithm](price);
}
}();
Harvest and summary
The main feature of the strategy model is to create a series of policy algorithms. Each group of algorithms processes the business the same, but the processing process or processing results are different, so they can be replaced by each other, which solves the coupling between the algorithm and the user.
Responsibility Chain Model
The author compares this model to an orderly station.
定义
Resolve the coupling between the sender of the request and the recipient of the request, and decompose the request process through multiple objects on the responsibility chain, and realize the transmission of the request between multiple objects, knowing that the last object completes the processing of the request.
使用方案
The project manager is ready to improve the input verification and prompt interaction experience on the page. For example, after the user enters information in the input box, some alternatives are prompted below the input box. After the user input is completed, the user input information must be verified, etc. Many modules on the page require users to submit information. In order to enhance the user experience, most of these input boxes need to have the above two functions. This requirement needs to be completed now, but some modifications may be made to the original form interactive experience in the future, that is, this is a semi-finished product requirement. In this case, we need to separate everything we need to do in the requirements, so that the complete requirements will become independent module requirements, so that the progress of our project will not be affected by the changes in the requirements in the future, which will also be conducive to future unit testing. This is actually a chain of responsibilities.
For the above requirements, binding events to the input box is the first part, the second part is to create xhr for asynchronous data acquisition, the third part is to adapt the response data, format the received data into a processable form, and the last part is to pass the corresponding data to the component creator to generate the component.
Harvest and summary
The chain of responsibilities defines the direction of delivery of requests, and implements a complex logical operation through the delivery of requests by multiple objects. Therefore, the responsibility chain model granates the responsible requirements one by one to realize the requirements within each minimum score, and passes the requests sequentially.对于职责链上的每一个对象来说,它可能是请求的发起者也可能是请求的接收者,通过这种方式不仅仅简化原对象的复杂度,而且解决原请求的发起者与原请求的接收者之间的耦合。
命令模式定义
将请求与实现解耦并封装成独立对象,从而使不同的请求对客户端的实现参数化。
使用方案
现在的需求是要做一个活动页面,平铺式的结构,不过页面的每个模块都有些相似的地方,比如每个预览产品图片区域,都有一行标题,然后标题下面是产品图片,只是图片的数量与排列不同。我们需要一种自由创建视图模块的方法,有时候创建多张图片有时候只创建一张图片,这时候可以试试命令模式。
命令模式是将创建模块的逻辑封装在一个对象里,这个对象提供一个参数化的请求接口,通过调用这个接口并传递一些参数实现调用命令对象内部中的一些方法。请求部分很简单,只需要按照给定参数格式书写指令即可,所以实现部分的封装才是重点,因为它要为请求部分提供所需方法。
那么哪些对象需要被命令化呢?既然需要动态展示不同模块,所以创建元素这一需求就是变化的,因此创建元素方法、展示方法应该被命令化。
// 模块实现模块
var viewCommand = (function() {
var tpl = {
// 展示图片结构模块
product: [
'',.....,''
].join(''),
// 展示标题结构模块
title: [
'',.....,''
].join(''),
},
// 格式化字符串缓存字符串
html = '';
// 格式化字符串
function formateString(str, obj) {}
// 方法集合
var Action = {
// 创建方法
create: function(data, view) {
// 解析数据
if(data.length) {
// 遍历
for(var i = 0, len = data.length; i < len; i++) {
html += formateString(tpl[view], data[i]);
}
} else {
html += formateString(tpl[view], data);
}
},
// 展示方法
display: function(container, data, vuew) {
// 如果传入数据
if(data) {
// 根据给的数据创建视图
this.create(data, view);
}
// 展示模块
document.getElementById(container).innerHTML = html;
// 展示后清空缓存字符串
html = '';
}
}
// 命令接口
return function excute(msg) {
// 解析命令,如果msg.param不是数组则将其转化为数组
msg.param = Object.prototype.toString.call(msg.param) === "[object Array]" ? msg.param : [msg.param];
// Action内部调用的方法引用this,此处保证作用域this执行传入Action
Action[msg.command].apply(Action, msg.param)
}
})();
下面就可以测试这个命令对象了:
var productData = [
{
src: 'command/02.jpg',
text: '绽放的桃花'
},
{
src: 'command/03.jpg',
text: '阳光下的温馨'
}
],
// 模块标题数据
titleData = {
title: '夏日里的一片温馨',
tips: '暖暖的温情带给人们家的感觉'
}
// 调用命令对象
viewCommand({
command: 'display',
param: ['title', titleData, 'title']
});
viewCommand({
command: 'create',
param: ['product', productData, 'product']
});
有了命令模式,想创建任何页面视图都是一件很简单的事情。
收获与总结
命令模式是将执行的命令封装,解决命令发起者与命令执行者之间的耦合,每一条命令实质上是一个操作。命令的是使用者不必了解命令执行者的命令接口是如何实现的,只需要知道如何调用。
访问者模式
作者把这种模式比喻成驻华大使。
定义
针对于对象结构中的元素,定义在不改变对象的前提下访问结构中元素的新方法。
使用方案
用DOM2级事件为页面中元素绑定事件时,为css设置一些样式如下:
var bindEvent = function(dom, type, fn) {
if(dom.addEventListener) {
dom.addEventListener(type, fn, false);
} else if(dom.attachEvent) {
dom.attachEvent('on' + type, fn);
} else {
dom['on' + type] = fn;
}
}
var demo = document.getElementById('demo');
bindEvent(demo, 'click', function() {
this.style.background = 'red';
});
这个在IE浏览器中会出问题,因为IE的attachEvent事件中this指向的竟然是window而不是这个元素,所以如果想获取事件对象必须用window.e来获取。这个问题可以借用访问者模式来解决。
访问者模式的思想是我们在不改变操作对象的同时,为它添加新的操作方法,来实现对操作对象的访问。下面看看IE的实现方式:
function bindIEEvent(dom, type, fn, data) {
var data = data || {};
dom.attachEvent('on' + type, function(e){
fn.call(dom, e, data);
});
};
上面实现方法的核心就是调用call方法,call方法的作用就是更改函数执行时的作用域,这正是访问者模式的精髓。
收获与总结
访问者模式解决数据与数据操作方法之间的耦合,将数据的操作方法独立于数据,使其可以自由化演变。访问者更适合那些数据稳定但是数据的操作方法易变的环境下。
中介者模式
作者把这种模式比喻成媒婆,好吧,我笑了这里。
定义
通过中介者对象封装一系列对象之间的交互,是对象之间不再相互引用,降低他们之间的耦合。有时中介者对象也可以改变对象之间的交互。
使用方案
项目经理准备在用户首页上的导航模块添加一个设置层,让用户可以通过设置层来设置导航展开样式。但是页面中好多模块都有导航,这要改起来工作量也很大,上面讲的观察者模式虽然能解决模块之间的耦合,但是这里我们并没有需要向设置层发送请求的需求,设置层只是单向控制导航模块内导航的样式。这样的单向通信就可以使用中介者模式。
观察者模式和中介者模式都是通过消息收发机制实现,不过在观察者模式中,一个对象既可以是消息的发送者也可以是消息的接收者,而中介者模式中消息的发送方只有一个就是中介者对象,而且中介者对象不能订阅消息,只有那些活跃对象(订阅者)才能订阅中介者消息。
如果用中介者模式来解决上面的问题,那么中介者对象就是设置层模块对象,它负责向各个导航模块对象发送用户设置消息,而各个导航模块则应该作为消息的订阅者存在,实现如下:
// 中介者对象
var Mediator = function() {
// 消息对象
var _msg = {};
return {
// 订阅消息方法,type:消息名称 action:消息回调函数
register: function(type, action) {
// 如果消息存在
if(_msg[type])
// 存入回调函数
_msg[type].push(action);
else {
// 不存在则建立消息容器
_msg[type] = [];
_msg[type].push(action);
}
},
// 发布消息方法
send: function(type) {
// 如果该消息已经被订阅
if(_msg[type]) {
// 遍历已存储的消息回调函数
for(var i = 0, len = _msg[type].length; i < len; i++) {
// 执行回调函数
_msg[type][i] && _msg[type][i]();
}
}
}
}
}();
这样就创建了一个中介者对象,下面就可以利用这个中介者对象完成我们的需求了。
收获与总结
同观察者模式一样,中介者模式的主要业务也是通过模块间或者对象间的复杂通信,来解决模块间或对象间的耦合。在中介者模式中,订阅者是单向的,只能是订阅者而不能是发布者。而消息统一由中介者对象发布。
备忘录模式定义
在不破坏对象的封装性的前提下,在对象之外捕获并保存该对象内部状态以便日后对象使用或者对象恢复到以前的某个状态。
使用方案
在前面提到的新闻页面中,有上一页和下一页的按钮,页面的内容是用异步请求获取的。如果点击下一页按钮接着再点击上一页那么之前那一页又要进行一次异步请求,这是多余的操作。因为第一次已经获取了数据,不需要再发送多余的请求。这个时候可以用备忘录模式来缓存请求过的数据。也就是说每次发生请求的时候对当前状态做一次记录,将请求到的数据以及对应得页码缓存下来,如果之后返回到之前浏览过的页面,直接在缓存中查询即可,不用发生异步请求。先创建一个新闻缓存器:
// Page备忘录类
var Page = function() {
// 信息缓存对象
var cache = {};
return function(page, fn) {
// 判断该页数据是否在缓存中
if(cache[page]) {
// 显示该页内容
showPage(page, cache[page]);
// 执行成功回调函数
fn && fn();
} else {
// 否则异步请求
$.post('./data/getNewsData.php', {
page: page
}, function(res) {
// 成功返回
if(res.errNo == 0) {
showPage(page, res.data);
cache[page] = res.data;
fn && fn();
} else {
// 处理异常
}
})
}
}
}
上面代码可以看出Page缓存器内部缓存了每次请求回来的新闻数据,这样以后如果用户想回看某页新闻数据就不需要发送不必要的请求了。
收获与总结
备忘录模式最主要的任务是对现有的数据或状态进行缓存,为将类某个时刻使用或恢复做准备。但是当数据量过大时,会严重占用系统提供的资源,此时对缓存器的优化是很有必要的,复用率低的数据缓存下来是不值得的。
迭代器模式
作者把这种模式比喻成一个点钞机。
定义
在不暴露对象内部结构的同时,可以顺序地访问聚合对象内部的元素。
使用方案
迭代器模式主要是解决重复循环迭代的问题,之前接触过面向对象语言的应该都对迭代器有所了解。迭代器就是用来顺序地访问一个聚合对象内部元素的,它可以简化我们遍历操作,就行银行里的点钞机,有了它可以大幅度降低我们的点钞成本。下面创建一个常用的迭代器对象:
var Iterator = function(items, container) {
// 获取父元素
var container = container && document.getElementById(container) || document,
// 获取元素
items = container.getElementsByTagName(items),
// 获取元素长度
length = items.length,
// 当前索引值
index = 0;
// 缓存原生数组splice方法
var splice = [].splice;
return {
// 获取第一个元素
first: function() {},
// 获取最后一个元素
second: function() {},
// 获取前一个元素
pre: function() {},
// 获取后一个元素
next: function() {},
// 获取某一个元素
get: function(num) {},
// 对每一个元素执行某一个方法
dealEach: function(fn) {},
// 对某一个元素执行某一个方法
dealItem: function(num, fn) {},
// 排他方式处理某一个元素
exclusive: function() {}
}
}
下面具体实现迭代器里面的这些方法,然后就可以用这个迭代器对象啦。
收获与总结
通过迭代器我们可以顺序地访问一个聚合对象中的每一个元素。在开发中,迭代器极大简化了代码中的循环语句,使代码结构清晰紧凑。用迭代器去处理一个对象时,只需要提供处理的方法,而不必去关心对象的内部结构,这也解决了对象的使用者与对象内部结构之间的耦合。
解释器模式定义
对于一种语言,给出其文法表示,并定义一种解释器,通过使用这种解释器来解释语言中定义的句子。
使用方案
一个页面中的某些功能好坏有时是靠一定的数据依据支撑的。项目经理想看看用户对最近新增的功能使用情况,前后端要给出统计数据,然而前端交互统计项中要给出交互元素路径。这件事情与冒泡事件类似,只不过在这个路径中还要关心同一层级中当前元素的兄弟元素。比如下面的结构:
要获取button相对于class为wrap的div元素的Xpath路径,那么可以表示为DIV>DIV2>SPAN。
上面对需求的描述是一种文法,描述的是一组规则,现在要做的事实现一个规则解释器来解释上面的规则。首先要分析给出的文法,查找他们的相似点,然后该清楚我们要先实现什么再实现什么,基本上问题就能解决了。
收获与总结
一些描述性语句,几次功能的提取抽象,形成了一套语法法则,这就是解释器模式要处理的事情。是否能应用解释器模式的一条重要准则是能否根据需求解析出一套完整的语法规则,不论该语法规则简单或是复杂都是必须的。
技巧型设计模式
技巧型设计模式是通过一些特定技巧来解决组件的某些方面的问题,这类技巧一般通过实践经验总结得到。这本书中总结了8种技巧型设计模式,分别是链模式,委托模式,数据访问对象模式,节流模式,简单模板模式,惰性模式,参与者模式和等待者模式。有兴趣的同学可以去买书来看哦,这里就不一一解释了。
架构型设计模式
架构型设计模式是一类框架结构,通过提供一些子系统,指定它们的职责,并将它们条理清晰地组织在一起。现在流行的前端框架都用了这种类型的设计模式。本书总结了6种架构型设计模式,分别是同步模块模式,异步模块模式,Widget模式,MVC模式,MVP模式和MVVM模式。
学习设计模式的学习对于我们来说任重而道远,我们需要在实践中不断思考不断总结。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请联系本站,一经查实,本站将立刻删除。如若转载,请注明出处:http://www.fjwyxt.com/html/tiyuwenda/9646.html