JavaScript设计模式
你将在本文中,了解什么是设计模式!
你将了解设计模式有什么优点!
你将了解两种设计模式-观察者模式和装饰模式!
一、什么是设计模式
什么是设计模式?其实简单可以理解为,设计模式其实是一套理论,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。模式的另一种解释就是一个我们如何解决问题的模版,提供解决问题的一种方案。
二、设计模式的优点
模式是行之有效的一种解决方法:通过长期以来前辈们提供固定的解决方案在软件开发中出现的问题。
模式是可以很容易地重用:一个模式通常反映解决一些问题可以开箱即用的方法。
模式善于表达:一般有一组结构和词汇可以非常优雅地帮助表达相当大的解决方案。
注:模式不是标准的解决方案,模式也不能解决所有的设计问题,也不能代替我们优秀的软件设计师。模式的角色仅仅提供给我们一个解决方案。
三、JavaScript设计模式##
1、JavaScript设计模式之观察者模式:###
先说一下个人理解:观察者模式又称“发布-订阅(Publish/Subscribe)”,发布与订阅是两个不同对象的功能。在具体编程中,发布者有了新的内容,需要向订阅者推送数据,若订阅者退订了则要对发布者中的订阅者列表进行更新。
观察者模式主要应用于对象之间一对多的依赖关系,当一个对象发生改变时,多个对该对象有依赖的其他对象也会跟着做出相应改变,这就非常适合用观察者模式来实现。使用观察者模式可以根据需要增加或删除对象,解决一对多对象间的耦合关系,使程序更易于扩展和维护。
应用场景:
【1】微信公众号订阅,多个读者订阅一个微信公众号,一旦公众号有更新,多个读者都会收到更新。(邮箱订阅同理)
【2】在《大话设计模式》一书中,提到类似的情况:如果针对发布者内容而订阅者要做不同的事情呢?比如一个按钮和三个矩形,点击按钮的时候,第一个矩形增加宽度,第二个矩形增加高度,第三个矩形则变成圆角矩形又该怎么做呢?当然我们可以在三个矩形的update内部写具体的实现代码,但是这update岂不是没有一个具体的功能描述了吗?该书中用“事件委托”解决了这个问题(此处事件委托和DOM中的事件委托应该是两码事),我个人理解这个“事件委托”在javascript中可以用一个数组表示,然后里面放各个订阅者的不同名字的update,然后一一调用。
代码解读:
--发布者相关信息解读//发布者
function Publisher(){
this.observers = [];
this.state = "";
}
//增加
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
//移除
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
//更新
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.state);
};
}
--订阅者相关信息解读//订阅者
(订阅者功能很简单,只需要一个更新(update)功能,但是每一个订阅者的更新功能可能不一样,例如应用场景中提到的【2】)
function Subscribe(){
this.update = function(data){
console.log(data);
};
}
--为了满足订阅者不同的需求,可以为每一个订阅者的实例设置单独的update。
//实际应用
var obA = new Subscribe(),
obB = new Subscribe();
var pba = new Publisher();
pba.addOb(obA);
pba.addOb(obB);
obA.update = function(state){
console.log(state+"hello!");
}
obB.update = function(state){
console.log(state+"world!");
}
pba.state = "open ";
pba.notice();
so--彩蛋
一般pm的需求肯定不会这样提。mrd文档一般为第一个input框输入中文姓名拼音,第二个input框(不可写)自动“0881+姓名拼音”,第三个文本框(不可写)自动生成邮箱“拼音+888@163.com”
部分代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<label>员工姓名拼音:<input type="text" id="pba" placeholder="请输入员工姓名拼音" /></label><br /><br />
<label>生成编号:<input type="text" id="oba" readonly /></label>
<label>生成邮箱:<input type="text" id="obb" readonly /></label>
</div>
<script type="text/javascript">
//发布者
function Publisher(obj){
this.observers = [];
var state = obj.value; //让该内容不能直接访问
//新增两个对于state的操作 获取/更新
this.getState=function(){
return state;
}
this.setState=function(value){
state = value;
this.notice();
}
this.obj = obj;
}
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.getState());
};
}
//订阅者
function Subscribe(obj){
this.obj = obj;
this.update = function(data){
this.obj.value = data;
};
}
//实际应用
var oba = new Subscribe(document.querySelector("#oba")),
obb = new Subscribe(document.querySelector("#obb"));
var pba = new Publisher(document.querySelector("#pba"));
pba.addOb(oba);
pba.addOb(obb);
oba.update = function(state){
this.obj.value = “0881”+state;
}
obb.update = function(state){
this.obj.value = state+“888@163.com”;
}
pba.obj.addEventListener('keyup',function(){
pba.setState(this.value);
});
</script>
</body>
</html>
2、JavaScript设计模式之装饰者模式:###
先说一下个人理解:装饰者模式重点在装饰,同样都是女生,如果其中一个女生化个妆涂个口红打扮(装饰)一下即可秒变女神(夺人眼球的功能)。
其实,实现装饰者模式的其中一个方法是使得每个装饰者成为一个对象,并且该对象包含了应该被重载的方法。每个装饰者实际上继承了目前已经被前一个装饰者进行增强后的对象。每个装饰方法在“继承的对象”上调用了同样的方法并获取其值,此外它还继续执行了一些操作。
应用场景:
【1】之前项目遇到过的,创建会员卡,会员卡的外观以及会员卡详情都是一样的功能。但是,对于不同的用户会和会员卡有不一样的联系,例如:未激活,未领取,设置默认卡等。可以针对会员卡这个对象,给予不一样的修饰函数。若后面要对该会员卡还有其他相应的交互,直接装饰即可~
【2】假设我们在编写一个飞机大战的游戏,随着经验值的增加,我们操作的飞机对象可以升级成更厉害的飞机,一开始这些飞机只能发射普通的子弹,升到第二级时可以发射导弹,升到第三级时可以发射原子弹。
部分代码如下:
var Plane = function(){};
Plane.prototype.fire = function(){
console.log( '发射普通子弹' );
}
var MissileDecorator = function( plane ){
this.plane = plane;
}
MissileDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '发射导弹' );
}
var AtomDecorator = function( plane ){
this.plane = plane;
}
AtomDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '发射原子弹' );
}
var plane = new Plane();
plane = new MissileDecorator( plane );
plane = new AtomDecorator( plane );
plane.fire();// 分别输出: 发射普通子弹、发射导弹、发射原子弹
导弹类和原子弹类的构造函数都接受参数plane 对象,并且保存好这个参数,在它们的fire方法中,除了执行自身的操作之外,还调用plane 对象的fire 方法。
因为装饰者对象和它所装饰的对象拥有一致的接口,所以它们对使用该对象的客户来说是透明的,被装饰的对象也并不需要了解它曾经被装饰过,这种透明性使得我们可以递归地嵌套任意多个装饰者对象。