网站建设及相关流程图,wordpress 版权信息,2022昆明今天刚刚发生的新闻,网页浏览器是windows系统自带的是JavaScript状态模式 1 什么是状态模式2 使用状态模式改造电灯程序3 缺少抽象类的变通方式4 示例#xff1a;文件上传4.1 场景描述4.2 代码过程 1 什么是状态模式
允许一个对象在其内部状态改变时改变它的行为#xff0c;对象看起来似乎修改了它的类。
比如说这样一个场景文件上传4.1 场景描述4.2 代码过程 1 什么是状态模式
允许一个对象在其内部状态改变时改变它的行为对象看起来似乎修改了它的类。
比如说这样一个场景有一个电灯电灯上面只有一个开关。当电灯开着的时候此时按下开关电灯会切换到关闭状态再按一次开关电灯又将被打开。同一个开关按钮在不同的状态下表现出来的行为是不一样的。
我们用代码来描述上面的场景
// 定义一个Light类
var Light function () {this.state off; // 给电灯设置初始状态 offthis.button null; // 电灯开关按钮
};// 在页面中创建一个真实的button节点
Light.prototype.init function () {var button document.createElement(button), // 创建一个开关按钮self this;button.innerHTML 开关;this.button document.body.appendChild(button);// 开关被按下的事件this.button.onclick function () {self.buttonWasPressed();};
};// 开关被按下的行为
Light.prototype.buttonWasPressed function () {// 如果当前是关灯状态按下开关表示开灯if (this.state off) {console.log(开灯);this.state on;} else if (this.state on) {// 如果当前是开灯状态按下开关表示关灯console.log(关灯);this.state off;}
};var light new Light();
light.init();但是灯的种类是多种多样的另外一种电灯这种电灯也只有一个开关但它的表现是第一次按下打开弱光第二次按下打开强光第三次才是关闭电灯现在我们改造上面的代码来完成这种新型电灯的制造
Light.prototype.buttonWasPressed function () {if (this.state off) {console.log(弱光);this.state weakLight;} else if (this.state weakLight) {console.log(强光);this.state strongLight;} else if (this.state strongLight) {console.log(关灯);this.state off;}
};在上面的代码中存在一些很明显的缺点
buttonWasPressed方法违反开放—封闭原则每次新增或者修改灯光的状态都需要改动buttonWasPressed方法中的代码这使其成为了一个非常不稳定的方法所有跟状态有关的行为都被封装在buttonWasPressed方法里如果这个电灯又增加了其他光的种类那这个方法会越来越庞大状态的切换不明显仅仅表现为改变state容易漏掉某些状态状态之间的切换关系是靠if、else语句增加或者修改一个状态可能需要改变若干个操作这使代码难以阅读和维护
2 使用状态模式改造电灯程序
状态模式的关键是把事物的每种状态都封装成单独的类跟此种状态有关的行为都被封装在这个类的内部所以button被按下的的时候只需要在上下文中把这个请求委托给当前的状态对象即可该状态对象会负责渲染它自身的行为。 同时我们还可以把状态的切换规则事先分布在状态类中 这样就有效地消除了原本存在的 大量条件分支语句代码如下
// OffLightState
var OffLightState function (light) {this.light light;
};
OffLightState.prototype.buttonWasPressed function () {console.log(弱光); // offLightState 对应的行为this.light.setState(this.light.weakLightState); // 切换状态到 weakLightState
};// WeakLightState
var WeakLightState function (light) {this.light light;
};
WeakLightState.prototype.buttonWasPressed function () {console.log(强光); // weakLightState 对应的行为this.light.setState(this.light.strongLightState); // 切换状态到 strongLightState
};// StrongLightState
var StrongLightState function (light) {this.light light;
};
StrongLightState.prototype.buttonWasPressed function () {console.log(关灯); // strongLightState 对应的行为this.light.setState(this.light.offLightState); // 切换状态到 offLightState
};// 改写Light类在Light类中为每个状态类都创建一个状态对象可以很明显的看到灯的种类
var Light function () {this.offLightState new OffLightState(this);this.weakLightState new WeakLightState(this);this.strongLightState new StrongLightState(this);this.button null;
};// 按下按钮的事件中将请求委托给当前持有的状态对象去执行
Light.prototype.init function () {var button document.createElement(button), // 创建buttonself this;this.button document.body.appendChild(button);this.button.innerHTML 开关;// 设置当前状态this.currState this.offLightState;this.button.onclick function () {self.currState.buttonWasPressed();};
};// 切换light对象的状态
Light.prototype.setState function (newState) {this.currState newState;
};var light new Light();
light.init();3 缺少抽象类的变通方式
在上面的代码中在状态类中将定义一些共同的行为方法Context最终会将请求委托给状态对象的这些方法在这个例子里这个方法就是buttonWasPressed。无论增加了多少种状态类它们都必须实现buttonWasPressed方法。
所以使用状态模式的时候要格外小心如果我们编写一个状态子类时忘记了给这个状态子类实现buttonWasPressed方法则会在状态切换的时候抛出异常因为Context总是把请求委托给状态对象的buttonWasPressed方法。因此我们让抽象父类的抽象方法直接抛出一个异常
var State function () {};
State.prototype.buttonWasPressed function () {throw new Error(父类的 buttonWasPressed 方法必须被重写);
};
var SuperStrongLightState function (light) {this.light light;
};
SuperStrongLightState.prototype new State(); // 继承抽象父类
SuperStrongLightState.prototype.buttonWasPressed function () {// 重写 buttonWasPressed 方法console.log(关灯);this.light.setState(this.light.offLightState);
};4 示例文件上传
4.1 场景描述
例如控制文件上传需要两个节点按钮第一个用于暂停和继续上传第二个用于删除文件
当文件在扫描状态中不能进行任何操作既不能暂停也不能删除文件只能等待扫描完成。扫描完成之后根据文件的md5值判断若确认该文件已经存在于服务器则直接跳到上传完成状态。如果该文件的大小超过允许上传的最大值或者该文件已经损坏则跳往上传失败状态。剩下的情况下才进入上传中状态上传过程中可以点击暂停按钮来暂停上传暂停后点击同一个按钮会继续上传扫描和上传过程中点击删除按钮无效只有在暂停、上传完成、上传失败之后才能删除文件
假设我们使用一个插件对象帮助我们完成上传工作
var plugin (function () {var plugin document.createElement(embed);plugin.style.display none;plugin.type application/txftn-webkit;plugin.sign function () {console.log(开始文件扫描);};plugin.pause function () {console.log(暂停文件上传);};plugin.uploading function () {console.log(开始文件上传);};plugin.del function () {console.log(删除文件上传);};plugin.done function () {console.log(文件上传完成);};document.body.appendChild(plugin);return plugin;
})();上传是一个异步的过程所以控件会不停地调用全局函数window.external.upload来通知目前的上传进度控件会把当前的文件状态作为参数state塞进window.external.upload在此例中该函数负责打印一些log
window.external.upload function (state) {console.log(state); // 可能为 sign、uploading、done、error
};4.2 代码过程
首先定义Upload类在构造函数中为每种状态子类都创建一个实例对象
var Upload function (fileName) {this.plugin plugin;this.fileName fileName;this.button1 null;this.button2 null;this.signState new SignState(this); // 设置初始状态为 waitingthis.uploadingState new UploadingState(); // 上传中this.pauseState new PauseState(this); // 暂停this.doneState new DoneState(this); // 上传完成this.errorState new ErrorState(this); // 上传错误this.currState this.signState; // 设置当前状态
};创建两个按钮一个控制文件暂停和继续上传一个用于删除文件
Upload.prototype.init function () {var that this;this.dom document.createElement(div);this.dom.innerHTML span文件名称: this.fileName /spanbutton data-actionbutton1扫描中/buttonbutton data-actionbutton2删除/button;document.body.appendChild(this.dom);this.button1 this.dom.querySelector([data-actionbutton1]); // 第一个按钮this.button2 this.dom.querySelector([data-actionbutton2]); // 第二个按钮this.bindEvent();
};为两个按钮分别绑定点击事件在点击了按钮之后Context并不做任何具体的操作而是把请求委托给当前的状态类来执行
Upload.prototype.bindEvent function () {var self this;this.button1.onclick function () {self.currState.clickHandler1();};this.button2.onclick function () {self.currState.clickHandler2();};
};// 扫描中
Upload.prototype.sign function () {this.plugin.sign();this.currState this.signState;
};
// 上传中
Upload.prototype.uploading function () {this.button1.innerHTML 正在上传点击暂停;this.plugin.uploading();this.currState this.uploadingState;
};
// 暂停
Upload.prototype.pause function () {this.button1.innerHTML 已暂停点击继续上传;this.plugin.pause();this.currState this.pauseState;
};
// 上传成功
Upload.prototype.done function () {this.button1.innerHTML 上传完成;this.plugin.done();this.currState this.doneState;
};
// 上传失败
Upload.prototype.error function () {this.button1.innerHTML 上传失败;this.currState this.errorState;
};
// 删除
Upload.prototype.del function () {this.plugin.del();this.dom.parentNode.removeChild(this.dom);
};再接下来是编写各个状态类的实现
var StateFactory (function () {var State function () {};State.prototype.clickHandler1 function () {throw new Error(子类必须重写父类的 clickHandler1 方法);};State.prototype.clickHandler2 function () {throw new Error(子类必须重写父类的 clickHandler2 方法);};return function (param) {var F function (uploadObj) {this.uploadObj uploadObj;};F.prototype new State();for (var i in param) {F.prototype[i] param[i];}return F;};
})();var SignState StateFactory({clickHandler1: function () {console.log(扫描中点击无效...);},clickHandler2: function () {console.log(文件正在上传中不能删除);},
});var UploadingState StateFactory({clickHandler1: function () {this.uploadObj.pause();},clickHandler2: function () {console.log(文件正在上传中不能删除);},
});var PauseState StateFactory({clickHandler1: function () {this.uploadObj.uploading();},clickHandler2: function () {this.uploadObj.del();},
});var DoneState StateFactory({clickHandler1: function () {console.log(文件已完成上传, 点击无效);},clickHandler2: function () {this.uploadObj.del();},
});var ErrorState StateFactory({clickHandler1: function () {console.log(文件上传失败, 点击无效);},clickHandler2: function () {this.uploadObj.del();},
});测试一下
var uploadObj new Upload(AAAAAAAAA);
uploadObj.init();
window.external.upload function (state) {// 插件调用 JavaScript 的方法uploadObj[state]();
};
window.external.upload(sign); // 文件开始扫描
setTimeout(function () {window.external.upload(uploading); // 1 秒后开始上传
}, 1000);
setTimeout(function () {window.external.upload(done); // 5 秒后上传完成
}, 5000);