状态模式的关键是区分事物内部的状态,事物的内部状态改变往往会带来事物行为的变化。 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
一 电灯程序
// 普通处理方式
var Light = function() {
this.state = "off"; // 电灯初始状态
this.button = null; // 电灯的开关按钮
};
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 = "weakLight";
} else if (this.state === "weakLight") {
console.log("强光");
this.state = "strongLight";
} else {
console.log("关灯");
this.state = "off";
}
};
var light = new Light();
light.init();
以上程序是违反开放-封闭原则的,每次新增或者修改light的状态,都需要改动buttonWasPressed方法中的代码。状态的切换非常不明显,堆砌了了很多的if else语句, 造成难以阅读和维护。
// 状态模式改进
var State = function() {};
// 抽象方法buttonWasPressed
State.prototype.buttonWasPressed = function() {
throw new Error("父类的buttonWasPressed方法必须被重写");
};
// offLightState
var OffLightState = function(light) {
this.light = light;
};
OffLightState.prototype = new State(); // 继承抽象父类
OffLightState.prototype.buttonWasPressed = function() {
console.log("弱光");
this.light.setState(this.light.weakLightState);
};
// WeakLightState
var WeakLightState = function(light) {
this.light = light;
};
WeakLightState.prototype = new State(); // 继承抽象父类
WeakLightState.prototype.buttonWasPressed = function() {
console.log("强光");
this.light.setState(this.light.strongLightState);
};
// StrongLightState
var StrongLightState = function(light) {
this.light = light;
};
StrongLightState.prototype = new State(); // 继承抽象父类
StrongLightState.prototype.buttonWasPressed = function() {
console.log("超强光");
this.light.setState(this.light.superLightState);
};
// SuperLightState
var SuperLightState = function(light) {
this.light = light;
};
SuperLightState.prototype = new State();
SuperLightState.prototype.buttonWasPressed = function() {
console.log("关灯");
this.light.setState(this.light.offLightState);
};
// Context 上下文
var Light = function() {
this.offLightState = new OffLightState(this); // 持有状态对象的引用
this.weakLightState = new WeakLightState(this);
this.strongLightState = new StrongLightState(this);
this.superLightState = new SuperLightState(this);
this.button = null;
};
Light.prototype.init = function() {
var button = document.createElement("button"),
self = this;
this.button = document.body.appendChild(button);
this.button.innerHTML = "开关";
this.currState = this.offLightState; // 设置默认的初始状态
this.button.onclick = function() {
// 定义用户的请求动作
self.currState.buttonWasPressed();
};
};
Light.prototype.setState = function(newState) {
this.currState = newState;
};
var light = new Light();
light.init();
// 文件上传
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;
})();
var Upload = function(fileName) {
this.plugin = plugin;
this.fileName = fileName;
this.button1 = null;
this.button2 = null;
this.state = "sign";
};
Upload.prototype.init = function() {
this.dom = document.createElement("div");
this.dom.innerHTML =
"<span>文件名称:" +
this.fileName +
'</span>\
<button data-action="button1">扫描中</button>\
<button data-action="button2">删除</button>';
document.body.appendChild(this.dom);
this.button1 = this.dom.querySelector('[data-action="button1"]');
this.button2 = this.dom.querySelector('[data-action="button2"]');
this.bindEvent();
};
Upload.prototype.bindEvent = function() {
var self = this;
this.button1.onclick = function() {
if (self.state === "sign") {
console.log("扫描中,点击无效");
} else if (self.state === "uploading") {
self.changeState("pause");
} else if (self.state === "pause") {
self.changeState("uploading");
} else if (self.state === "done") {
console.log("文件已完成上传,点击无效");
} else if (self.state === "error") {
console.log("文件上传失败,点击无效");
}
};
this.button2.onclick = function() {
if (
self.state === "done" ||
self.state === "error" ||
self.state === "pause"
) {
self.changeState("del");
} else if (self.state === "sign") {
console.log("文件正在扫描中,不能删除");
} else if (self.state === "uploading") {
console.log("文件正在上传中,不能删除");
}
};
};
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.errrorState;
};
Upload.prototype.del = function() {
this.plugin.del();
this.dom.parentNode.removeChild(this.dom);
};
Upload.prototype.changeState = function(state) {
switch (state) {
case "sign":
this.plugin.sign();
this.button1.innerHTML = "扫描中,任何操作无效";
break;
case "uploading":
this.plugin.uploading();
this.button1.innerHTML = "正在上传,点击暂停";
break;
case "pause":
this.plugin.pause();
this.button1.innerHTML = "已暂停,点击继续上传";
break;
case "done":
this.plugin.done();
this.button1.innerHTML = "上传完成";
break;
case "error":
this.button1.innerHTML = "上传失败";
break;
case "del":
this.plugin.del();
this.dom.parentNode.removeChild(this.dom);
console.log("删除完成");
break;
}
this.state = state;
};
var uploadObj = new Upload("Javascript设计模式与开发实践");
uploadObj.init();
window.external.upload = function(state) {
uploadObj.changeState(state);
};
window.external.upload("sign");
setTimeout(function() {
window.external.upload("uploading");
}, 1000);
setTimeout(function() {
window.external.upload("done");
}, 5000);
// 状态模式改写文件上传
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;
})();
var Upload = function(fileName) {
this.plugin = plugin;
this.fileName = fileName;
this.button1 = null;
this.button2 = null;
this.signState = new SignState(this);
this.uploadingState = new UploadingState(this);
this.pauseState = new PauseState(this);
this.doneState = new DoneState(this);
this.errrorState = new ErrorState(this);
this.currState = this.signState;
};
Upload.prototype.init = function() {
this.dom = document.createElement("div");
this.dom.innerHTML =
"<span>文件名称:" +
this.fileName +
'</span>\
<button data-action="button1">扫描中</button>\
<button data-action="button2">删除</button>';
document.body.appendChild(this.dom);
this.button1 = this.dom.querySelector('[data-action="button1"]');
this.button2 = this.dom.querySelector('[data-action="button2"]');
this.bindEvent();
};
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.errrorState;
};
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("Javascript设计模式与开发实践");
uploadObj.init();
window.external.upload = function(state) {
uploadObj[state]();
};
window.external.upload("sign");
setTimeout(function() {
window.external.upload("uploading");
}, 1000);
setTimeout(function() {
window.external.upload("done");
}, 5000);
// JavaScript 版本的状态机
var Light = function() {
this.currState = FSM.off;
this.button = null;
};
Light.prototype.init = function() {
var button = document.createElement("button"),
self = this;
button.innerHTML = "已关灯";
this.button = document.body.appendChild(button);
this.button.onclick = function() {
self.currState.buttonWasPressed.call(self);
};
};
var FSM = {
off: {
buttonWasPressed: function() {
console.log("关灯");
this.button.innerHTML = "下一次按我开灯";
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function() {
console.log("开灯");
this.button.innerHTML = "下一次按我关灯";
this.currState = FSM.off;
}
}
};
var light = new Light();
light.init();
// delegate 函数 闭包实现状态模式
var delegate = function(client, delegation) {
return {
buttonWasPressed: function() {
return delegation.buttonWasPressed.apply(client, arguments);
}
};
};
var FSM = {
off: {
buttonWasPressed: function() {
console.log("关灯");
this.button.innerHTML = "下一次按我开灯";
this.currState = this.onState;
}
},
on: {
buttonWasPressed: function() {
console.log("开灯");
this.button.innerHTML = "下一次按我关灯";
this.currState = this.offState;
}
}
};
var Light = function() {
this.offState = delegate(this, FSM.off);
this.onState = delegate(this, FSM.on);
this.currState = this.offState;
this.button = null;
};
Light.prototype.init = function() {
var button = document.createElement("button"),
self = this;
button.innerHTML = "已关灯";
this.button = document.body.appendChild(button);
this.button.onclick = function() {
self.currState.buttonWasPressed();
};
};
var light = new Light();
light.init();