Skip to content

Commit 98f845b

Browse files
author
Badacadabra
committed
Add State (ES5 + ES6 + CoffeeScript + TypeScript)
1 parent 3ddfad1 commit 98f845b

File tree

7 files changed

+314
-0
lines changed

7 files changed

+314
-0
lines changed

doc/GoF/Behavioral/State/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Synopsis
2+
3+
On any computer, I can see a power button. Generally, when the button is pressed while the computer is off, the computer is turned on. And when the button is pressed while the computer is on, the computer is turned off.
4+
5+
# Problem
6+
7+
A computer has two basic states: on and off. So when you press the power button, the system must be smart enough to understand when the computer is on and when it is off. It should also be smart enough to change its own state when the button is pressed.
8+
9+
# Solution
10+
11+
The State design pattern is obviously what we should use here. Here we need:
12+
13+
* An abstract representation of a computer state (abstract class or interface)
14+
* A concrete computer which is actually the context
15+
* Concrete states (on/off)
16+
17+
Contrary to the Strategy pattern, we can access the context directly from the concrete states. This allows state changes from states themselves.

doc/GoF/Behavioral/State/State.dia

2.17 KB
Binary file not shown.

doc/GoF/Behavioral/State/State.png

20.5 KB
Loading
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ==============================
2+
# ABSTRACT STATE
3+
# ==============================
4+
5+
class ComputerState
6+
constructor: ->
7+
throw new Error "You cannot instantiate an abstract class!" if @constructor is ComputerState
8+
9+
power: (pc) ->
10+
throw new Error "You cannot call an abstract method!"
11+
12+
# ==============================
13+
# CONCRETE STATES
14+
# ==============================
15+
16+
class Off extends ComputerState
17+
power: (pc) ->
18+
pc.setCurrentState pc.getStates().on
19+
20+
class On extends ComputerState
21+
power: (pc) ->
22+
pc.setCurrentState pc.getStates().off
23+
24+
# ==============================
25+
# CONCRETE CONTEXT
26+
# ==============================
27+
28+
class Computer
29+
constructor: ->
30+
@_currentState = null
31+
@_states =
32+
off: new Off
33+
on: new On
34+
35+
power: ->
36+
@_currentState.power(@)
37+
38+
getCurrentState: ->
39+
@_currentState
40+
41+
setCurrentState: (state) ->
42+
@_currentState = state
43+
44+
getStates: ->
45+
@_states
46+
47+
# ==============================
48+
# CLIENT CODE
49+
# ==============================
50+
51+
pc = new Computer
52+
53+
pc.setCurrentState pc.getStates().off
54+
55+
console.log pc.getCurrentState().constructor.name # Off
56+
pc.power()
57+
console.log pc.getCurrentState().constructor.name # On
58+
pc.power()
59+
console.log pc.getCurrentState().constructor.name # Off
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// ==============================
2+
// ABSTRACT STATE
3+
// ==============================
4+
5+
interface ComputerState {
6+
power(pc: Computer): void;
7+
}
8+
9+
// ==============================
10+
// CONCRETE STATES
11+
// ==============================
12+
13+
class Off implements ComputerState {
14+
public power(pc: Computer): void {
15+
pc.setCurrentState(pc.getStates().on);
16+
}
17+
}
18+
19+
class On implements ComputerState {
20+
public power(pc: Computer): void {
21+
pc.setCurrentState(pc.getStates().off);
22+
}
23+
}
24+
25+
// ==============================
26+
// CONCRETE CONTEXT
27+
// ==============================
28+
29+
class Computer {
30+
private currentState: ComputerState;
31+
private states: {off: Off; on: On} = {
32+
off: new Off(),
33+
on: new On()
34+
};
35+
36+
public power(): void {
37+
this.currentState.power(this);
38+
}
39+
40+
public getCurrentState(): ComputerState {
41+
return this.currentState;
42+
}
43+
44+
public setCurrentState(state: ComputerState): void {
45+
this.currentState = state;
46+
}
47+
48+
public getStates(): {off: Off; on: On} {
49+
return this.states;
50+
}
51+
}
52+
53+
// ==============================
54+
// CLIENT CODE
55+
// ==============================
56+
57+
let pc: Computer = new Computer();
58+
59+
pc.setCurrentState(pc.getStates().off);
60+
61+
// We extend this TypeScript interface to be able to get class names...
62+
interface Function {
63+
name: string;
64+
}
65+
66+
console.log(pc.getCurrentState().constructor.name); // Off
67+
pc.power();
68+
console.log(pc.getCurrentState().constructor.name); // On
69+
pc.power();
70+
console.log(pc.getCurrentState().constructor.name); // Off
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict';
2+
3+
// ==============================
4+
// ABSTRACT STATE
5+
// ==============================
6+
7+
var ComputerState = (function () {
8+
function ComputerState() {
9+
if (this.constructor === ComputerState) {
10+
throw new Error("You cannot instantiate an abstract class!");
11+
}
12+
}
13+
14+
ComputerState.prototype.power = function (pc) {
15+
throw new Error("You cannot call an abstract method!");
16+
};
17+
18+
return ComputerState;
19+
})();
20+
21+
// ==============================
22+
// CONCRETE STATES
23+
// ==============================
24+
25+
var Off = (function () {
26+
function Off() {}
27+
Off.prototype = Object.create(ComputerState.prototype);
28+
Off.prototype.constructor = Off;
29+
30+
Off.prototype.power = function (pc) {
31+
pc.setCurrentState(pc.getStates().on);
32+
};
33+
34+
return Off;
35+
})();
36+
37+
var On = (function () {
38+
function On() {}
39+
On.prototype = Object.create(ComputerState.prototype);
40+
On.prototype.constructor = On;
41+
42+
On.prototype.power = function (pc) {
43+
pc.setCurrentState(pc.getStates().off);
44+
};
45+
46+
return On;
47+
})();
48+
49+
// ==============================
50+
// CONCRETE CONTEXT
51+
// ==============================
52+
53+
var Computer = (function() {
54+
var currentState = null,
55+
states = {
56+
off: new Off(),
57+
on: new On()
58+
};
59+
60+
function Computer() {}
61+
62+
Computer.prototype.power = function () {
63+
currentState.power(this);
64+
};
65+
66+
Computer.prototype.getCurrentState = function () {
67+
return currentState;
68+
};
69+
70+
Computer.prototype.setCurrentState = function (state) {
71+
currentState = state;
72+
};
73+
74+
Computer.prototype.getStates = function () {
75+
return states;
76+
};
77+
78+
return Computer;
79+
})();
80+
81+
// ==============================
82+
// CLIENT CODE
83+
// ==============================
84+
85+
var pc = new Computer();
86+
87+
pc.setCurrentState(pc.getStates().off);
88+
89+
console.log(pc.getCurrentState().constructor.name); // Off
90+
pc.power();
91+
console.log(pc.getCurrentState().constructor.name); // On
92+
pc.power();
93+
console.log(pc.getCurrentState().constructor.name); // Off
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// ==============================
2+
// ABSTRACT STATE
3+
// ==============================
4+
5+
class ComputerState {
6+
constructor() {
7+
if (new.target !== undefined) {
8+
throw new Error("You cannot instantiate an abstract class!");
9+
}
10+
}
11+
12+
power(pc) {
13+
throw new Error("You cannot call an abstract method!");
14+
}
15+
}
16+
17+
// ==============================
18+
// CONCRETE STATES
19+
// ==============================
20+
21+
class Off extends ComputerState {
22+
power(pc) {
23+
pc.setCurrentState(pc.getStates().on);
24+
}
25+
}
26+
27+
class On extends ComputerState {
28+
power(pc) {
29+
pc.setCurrentState(pc.getStates().off);
30+
}
31+
}
32+
33+
// ==============================
34+
// CONCRETE CONTEXT
35+
// ==============================
36+
37+
class Computer {
38+
constructor() {
39+
this._currentState = null;
40+
this._states = {
41+
off: new Off(),
42+
on: new On()
43+
};
44+
}
45+
46+
power() {
47+
this._currentState.power(this);
48+
}
49+
50+
getCurrentState() {
51+
return this._currentState;
52+
}
53+
54+
setCurrentState(state) {
55+
this._currentState = state;
56+
}
57+
58+
getStates() {
59+
return this._states;
60+
}
61+
}
62+
63+
// ==============================
64+
// CLIENT CODE
65+
// ==============================
66+
67+
let pc = new Computer();
68+
69+
pc.setCurrentState(pc.getStates().off);
70+
71+
console.log(pc.getCurrentState().constructor.name); // Off
72+
pc.power();
73+
console.log(pc.getCurrentState().constructor.name); // On
74+
pc.power();
75+
console.log(pc.getCurrentState().constructor.name); // Off

0 commit comments

Comments
 (0)