introduce
State mode allows an object to change its behavior when its internal state changes, and the object appears to modify its class.
text
For example, when we download things, we usually have several states, such as ReadyState, DownloadingState, PausedState, DownloadedState, DownloadedState, and FailedState. That is to say, in each state, you can only do what the current state can do, but not what other states can do.
Because the State pattern describes how downloads (Download) behave differently in each state. The key idea of this pattern is to introduce an abstract class called State (or a function in JS) to represent the download state. The State function (as a prototype) declares some common interfaces for subclasses (inheritance functions) of each state. Each inheritance function implements behaviors related to a specific state, such as DownloadingState and DownloadedState respectively implements the behaviors being downloaded and downloaded. These behaviors can be maintained through Download.
Let's implement a game, first define the State function as a prototype of other basic functions:
The code copy is as follows:
var State = function () {
};
State.prototype.download = function () {
throw new Error("This method must be overloaded!");
};
State.prototype.pause = function () {
throw new Error("This method must be overloaded!");
};
State.prototype.fail = function () {
throw new Error("This method must be overloaded!");
};
State.prototype.finish = function () {
throw new Error("This method must be overloaded!");
};
We define 4 method interfaces for the prototype of State, which correspond to download (download), pause, failure, and end so that the subfunction can be rewritten.
Before writing a subfunction, we first write a ReadyState function so that state can be passed to the first download state:
The code copy is as follows:
var ReadyState = function (oDownload) {
State.apply(this);
this.oDownload = oDownload;
};
ReadyState.prototype = new State();
ReadyState.prototype.download = function () {
this.oDownload.setState(this.oDownload.getDownloadingState());
// After Ready, you can start downloading, so you set the state acquisition method in the Download function
console.log("Start Download!");
};
ReadyState.prototype.pause = function () {
throw new Error("Download has not started yet, cannot be paused!");
};
ReadyState.prototype.fail = function () {
throw new Error("The file has not started downloading yet, how can it be said that it has failed!");
};
ReadyState.prototype.finish = function () {
throw new Error("The file has not started downloading, of course it cannot be finished!");
};
This function takes an instance of the Download maintenance function as a parameter. The Download function is used to control state changes and acquisitions (similar to the central controller, allowing external calls). ReadyState rewrites the prototype download method to start downloading. Let's continue to look at the main functions of the Download function:
The code copy is as follows:
var Download = function () {
this.oState = new ReadyState(this);
};
Download.prototype.setState = function (oState) {
this.oState = oState;
};
// Four public methods exposed to the outside for external calls
Download.prototype.download = function () {
this.oState.download();
};
Download.prototype.pause = function () {
this.oState.pause();
};
Download.prototype.fail = function () {
this.oState.fail();
};
Download.prototype.finish = function () {
this.oState.finish();
};
//Get various states and pass in the current this object
Download.prototype.getReadyState = function () {
return new ReadyState(this);
};
Download.prototype.getDownloadingState = function () {
return new DownloadingState(this);
};
Download.prototype.getDownloadPausedState = function () {
return new DownloadPausedState(this);
};
Download.prototype.getDownloadedState = function () {
return new DownloadedState(this);
};
Download.prototype.getDownloadedFailedState = function () {
return new DownloadFailedState(this);
};
The prototype of the Download function provides 8 methods, 4 are operational behaviors for downloading states, and the other 4 are used to obtain the current four different states. These 4 methods all receive this as a parameter, that is, pass the Download instance itself as a parameter to the state object (ReadyState and the inheritance function to be implemented later), which makes the state object accessible to oDownlaod than if necessary.
Next, continue to define 4 related state functions:
The code copy is as follows:
var DownloadingState = function (oDownload) {
State.apply(this);
this.oDownload = oDownload;
};
DownloadingState.prototype = new State();
DownloadingState.prototype.download = function () {
throw new Error("The file is already downloading!");
};
DownloadingState.prototype.pause = function () { this.oDownload.setState(this.oDownload.getDownloadPausedState());
console.log("Pause download!");
};
DownloadingState.prototype.fail = function () { this.oDownload.setState(this.oDownload.getDownloadedFailedState());
console.log("Download failed!");
};
DownloadingState.prototype.finish = function () {
this.oDownload.setState(this.oDownload.getDownloadedState());
console.log("Downloaded!");
};
The main thing to note about DownloadingState is that the file that is already downloading cannot be downloaded again, and other states can be continuously performed.
The code copy is as follows:
var DownloadPausedState = function (oDownload) {
State.apply(this);
this.oDownload = oDownload;
};
DownloadPausedState.prototype = new State();
DownloadPausedState.prototype.download = function () {
this.oDownload.setState(this.oDownload.getDownloadingState());
console.log("Continue to download!");
};
DownloadPausedState.prototype.pause = function () {
throw new Error("It has been paused, why do you still have to pause!");
};
DownloadPausedState.prototype.fail = function () { this.oDownload.setState(this.oDownload.getDownloadedFailedState());
console.log("Download failed!");
};
DownloadPausedState.prototype.finish = function () {
this.oDownload.setState(this.oDownload.getDownloadedState());
console.log("Downloaded!");
};
It should be noted in the DownloadPausedState function that downloads that have been paused cannot be paused again.
The code copy is as follows:
var DownloadedState = function (oDownload) {
State.apply(this);
this.oDownload = oDownload;
};
DownloadedState.prototype = new State();
DownloadedState.prototype.download = function () {
this.oDownload.setState(this.oDownload.getDownloadingState());
console.log("Re-download!");
};
DownloadedState.prototype.pause = function () {
throw new Error("What else will you pause after downloading?");
};
DownloadedState.prototype.fail = function () {
throw new Error("The download has been successful, why will it fail?");
};
DownloadedState.prototype.finish = function () {
throw new Error("The download is successful, you can't do it anymore!");
};
DownloadedState function, similarly, after successful download, you can no longer set finish, you can only set the re-download status.
The code copy is as follows:
var DownloadFailedState = function (oDownload) {
State.apply(this);
this.oDownload = oDownload;
};
DownloadFailedState.prototype = new State();
DownloadFailedState.prototype.download = function () {
this.oDownload.setState(this.oDownload.getDownloadingState());
console.log("Try to re-download!");
};
DownloadFailedState.prototype.pause = function () {
throw new Error("Failed download cannot be paused!");
};
DownloadFailedState.prototype.fail = function () {
throw new Error("It all failed, why did it still fail!");
};
DownloadFailedState.prototype.finish = function () {
throw new Error("Failed download will definitely not succeed!");
};
Similarly, the failure state of the DownloadFailedState function cannot fail again, but you can try to download it again after finishing.
Calling the test code is very simple. Let's demonstrate it in HTML. First, you need jquery, and then there are 3 buttons representing: start download, pause, and re-download. (Note that you use firebug to view the results in Firefox, because the console.log method is used).
The code copy is as follows:
<html>
<head>
<link type="text/css" rel="stylesheet" href="http://www.cnblogs.com/css/style.css" />
<title>State Pattern</title>
<script type="text/javascript" src="/jquery.js"></script>
<script type="text/javascript" src="Download.js"></script>
<script type="text/javascript" src="states/State.js"></script>
<script type="text/javascript" src="states/DownloadFailedState.js"></script>
<script type="text/javascript" src="states/DownloadPausedState.js"></script>
<script type="text/javascript" src="states/DownloadedState.js"></script>
<script type="text/javascript" src="states/DownloadingState.js"></script>
<script type="text/javascript" src="states/ReadyState.js"></script>
</head>
<body>
<input type="button" value="Start download" id="download_button" />
<input type="button" value="pause" id="pause_button" />
<input type="button" value="re-download" id="resume_button" />
<script type="text/javascript">
var oDownload = new Download();
$("#download_button").click(function () {
oDownload.download();
});
$("#pause_button").click(function () {
oDownload.pause();
});
$("#resume_button").click(function () {
oDownload.download();
});
</script>
</body>
</html>
Summarize
The usage scenarios of the status mode are also particularly clear, with the following two points:
1. The behavior of an object depends on its state, and it must change its behavior according to its state at run time.
2. An operation contains a large number of branch statements, and these branch statements depend on the state of the object. The state is usually a representation of one or more enumeration constants.