ActionScript 3.0 State Design Pattern: An Aid Game

Take This One to Work

Take This One to Work

After creating the post on the acquaintance relationship between classes, I started thinking about the aggregate relationship and delegation. One of the best examples of delegation can be found in the State design patterns. All of the states are delegated to objects that handle each state. So I thought it’d be a good idea to create a little game that demonstrates both delegation and a state engine at work. Then, when I do the post on aggregation relations, I’ll have a simple reference point. This one is set up so that you can put it into your lunch bucket and take it to work. What’s more, it’s designed so that you can extend it into a more interesting game.

I decided that a good starting point would be to have a helicopter fly aid packages to different places on the screen. It would start at the airport and then fly the aid to different sprite objects representing groups of people in need. However, to get started, all I did was to set up five buttons to move the helicopter around the screen beginning at the airport. Figure 1 shows what the game looks like. (It is set up on an 800 by 600 display; so I had to cut off part of the right side to fit in this blog format.) The movement buttons are in the lower left portion of the screen.

<em><strong>Figure 2: </strong> State Pattern moves Helicopter Object</em>

Figure 1: State Pattern moves Helicopter Object

Click on the Play button to get an idea of how it works:
play


The Little State Engine that Could

The concept of a state machine or state engine brings the focus to changing states. Each state is delegated to a separate class, and as an object’s state changes, the state is handled by (delegated to) the appropriate class. In order to keep track of the current state, all requests are handled by a Context class. The Context class acts as an interface for the Client, and so the Client need not deal with the State objects directly. Figure 2 shows the class diagram for the State pattern.

<em><strong>Figure 1: </strong>State Design Pattern Class Diagram</em>

Figure 2: State Design Pattern Class Diagram

If you look at the class diagram, you may be thinking that it looks a lot like the Strategy design pattern. You’d be right! They look very similar, but their intent is quite different. In the class diagram, you’ll see the little red state label. Therein lies the difference between the State and Strategy patterns. The State pattern delegates different states and the Strategy pattern delegates different algorithms. Thus, the intent is quite different even though the class diagrams are similar.

Direction and Movement

In the past, I’ve used State designs for doing work with video players changing states such as play, record, pause and stop. For this one, I wanted to change directions of a moving object, and I selected a relatively simple design for changing the direction (movement state) of a helicopter. Using the vertical plane of a computer monitor for a reference point, the movement states are up, down, left, right, and stop. I used a very simple movement algorithm—the helicopter crabs along at 8 pixels per button click. (If you’re interested in more robust movement, see page 458 where Chandima has a nice algorithm for moving along continuously.) Remember, the purpose of this blog is to help understand design patterns, and so we try to focus on relatively clean examples. Once you get the design pattern down, it’s easy to add some really great algorithms.

With that in mind, I created the different classes for each of the different states along with classes for a Client and Context that work with a State interface. Figure 3 shows the file class diagram for the example:

<em><strong>Figure 3: </strong>State File Class Diagram</em>

Figure 3: State File Class Diagram

The Client requests are through the Context class. In turn, the Context class contains the moving object (chopper) as a property, and so it’s available to the different concrete state classes that change its state.

The Delegation Master

When working with the State Design Pattern, the state files tend to grow with added states. That’s because each state class must have the complete interface of the State class, which means all of the states. So, to save some time, I’ve put it online for you in CS3 and CS4 files. If you’re using Flex (Flash Builder) all you’ll need is a sprite object for the helicopter and the graphics you can see in Figure 1.) Click the Download button to get all of the files you need:
kilroy

Okay, we’re all set to look at the Context class. The role of the Context class is to keep track of the current state and set the state requested by the Client.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package 
{
	//Context class
	import flash.display.Sprite;
 
	class Context
	{
		private var stoph:State;
		private var moveup:State;
		private var movedown:State;
		private var moveleft:State;
		private var moveright:State;
		private var stateNow:State;
		public var chopper:Sprite;
 
		public function Context( chopper:Sprite )
		{
			this.chopper=chopper;
			trace("State is here");
			moveup=new MoveUp(this);
			stoph=new Stop(this);
			movedown=new MoveDown(this);
			moveleft=new MoveLeft(this);
			moveright=new MoveRight(this);
			stateNow=stoph;
		}
		public function upMove():void
		{
			stateNow.doMoveUp();
		}
		public function stopMove():void
		{
			stateNow.doStopNow();
		}
		public function downMove():void
		{
			stateNow.doMoveDown();
		}
		public function leftMove():void
		{
			stateNow.doMoveLeft();
		}
		public function rightMove():void
		{
			stateNow.doMoveRight();
		}
 
		public function setState(stateNow:State):void
		{
			trace("A new stateNow is set:");
			this.stateNow=stateNow;
		}
		public function getState():State
		{
			return stateNow;
		}
		public function getUp():State
		{
			return this.moveup;
		}
		public function getStop():State
		{
			return this.stoph;
		}
		public function getDown():State
		{
			return this.movedown;
		}
 
		public function getLeft():State
		{
			return this.moveleft;
		}
 
		public function getRight():State
		{
			return this.moveright;
		}
	}
}

The “chopper” is the helicopter object passed from the Client through a parameter. It is typed to the Sprite parent (interface) to follow the principle of programming to the interface instead of the implementation. In Flash, the Helicopter class is in the Library, while in Flex, you can put it anywhere you want as long as it can be instantiated in the Client as a Sprite type and passed to the Context class.

As you can see, several variables are initialized as State objects. The default state is Stop, and so when the program starts, the chopper object is in the stop state. Then, you will see a series of setter functions such as:

?View Code ACTIONSCRIPT
1
2
3
4
public function leftMove():void
{
	stateNow.doMoveLeft();
}

Such setter functions are called from the Client, but the Client never sees the concrete state object that they are delegated to by the Context. The Context passes on the request to another function that sets the current state and calls the requested state. Voila! Delegation at work.

The States

The next step is to set up a State interface with all of the different methods that will be set up in each state. That’s easy because we are using an ActionScript interface, and not the more general interface that is virtually any parent class.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
package 
{
	//Interface
	public interface State
	{
		function doMoveUp():void;
		function doMoveDown():void;
		function doStopNow():void;
		function doMoveLeft():void;
		function doMoveRight():void;
	}

Next, each of the concrete state classes must implement the full interface. However, only certain methods are used in each state to implement the state. The way I chose to implement the state was in the state itself. What results is that when the state is initially called, nothing happens because that current state (or calling state) does not contain the new state. This is a bit different from other state designs I’ve done, but it works well enough when the state change is movement. The following listing contains all of the five states:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package 
{
	//MoveDown State;
	class MoveDown implements State
	{
		//private var chopper:Sprite=new Helicopter();
		private var context:Context;
 
		public function MoveDown(context:Context)
		{
			trace("--MoveDown State--");
			this.context=context;
		}
		public function doMoveUp():void
		{
			context.setState(context.getUp());
		}
		public function doStopNow():void
		{
			context.setState(context.getStop());
		}
		public function doMoveDown():void
		{
			context.chopper.y+=8;
			context.setState(context.getDown());
		}
		public function doMoveLeft():void
		{
			context.setState(context.getLeft());
		}
		public function doMoveRight():void
		{
			context.setState(context.getRight());
		}
 
	}
}
 
/////
 
package 
{
	//MoveLeft State;
	class MoveLeft implements State
	{
		private var context:Context;
 
		public function MoveLeft(context:Context)
		{
			trace("--MoveLeft State--");
			this.context=context;
		}
		public function doMoveUp():void
		{
			context.setState(context.getUp());
		}
		public function doStopNow():void
		{
			context.setState(context.getStop());
		}
		public function doMoveDown():void
		{
			context.setState(context.getDown());
		}
		public function doMoveLeft():void
		{
			context.chopper.x-=8;
			context.setState(context.getLeft());
		}
		public function doMoveRight():void
		{
			context.setState(context.getRight());
		}
 
	}
}
 
/////
 
package 
{
	//MoveRight State;
	class MoveRight implements State
	{
		private var context:Context;
 
		public function MoveRight(context:Context)
		{
			trace("--MoveRight State--");
			this.context=context;
		}
		public function doMoveUp():void
		{
			context.setState(context.getUp());
		}
		public function doStopNow():void
		{
			context.setState(context.getStop());
		}
		public function doMoveDown():void
		{
			context.setState(context.getDown());
		}
		public function doMoveLeft():void
		{
			context.setState(context.getLeft());
		}
		public function doMoveRight():void
		{
			context.chopper.x+=8;
			context.setState(context.getRight());
		}
	}
}
 
/////
 
package 
{
	//MoveUp State;
	class MoveUp implements State
	{
		private var context:Context;
 
		public function MoveUp(context:Context)
		{
			trace("--MoveUp State--");
			this.context=context;
		}
		public function doMoveUp():void
		{
			context.chopper.y-=8;
			context.setState(context.getUp());
		}
		public function doStopNow():void
		{
			context.setState(context.getStop());
		}
		public function doMoveDown():void
		{
			context.setState(context.getDown());
		}
		public function doMoveLeft():void
		{
			context.setState(context.getLeft());
		}
		public function doMoveRight():void
		{
			context.setState(context.getRight());
		}
 
	}
}
 
/////
 
package 
{
	//Stop State;
	class Stop implements State
	{
		private var context:Context;
 
		public function Stop(context:Context)
		{
			trace("--Stop State--");
			this.context=context;
		}
		public function doMoveUp():void
		{
			context.setState(context.getUp());
		}
		public function doStopNow():void
		{
			context.setState(context.getStop());
		}
		public function doMoveDown():void
		{
			context.setState(context.getDown());
		}
		public function doMoveLeft():void
		{
			context.setState(context.getLeft());
		}
		public function doMoveRight():void
		{
			context.setState(context.getRight());
		}
 
	}
}

Each movement state simply increments or decrements the relative value of the x and y properties of the chopper. So while it may look complex, it really is not. Also, it is very easy to change; so if you want to add continuous movement, you should be able to do so.

The Client

This particular Client could have been set up to get all of its buttons elsewhere, but otherwise, all it does is to fire off event handlers to make requests. However, it is essential that it begins by instantiating a Helicopter instance that it passes to the Context class through a Sprite typed parameter. So, the Client, never even talks to the state objects. Instead, it just sends all requests for changing the direction and movement of the Helicopter instance through the Context.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package 
{
	//Client makes requests
	import flash.display.Sprite;
	import fl.controls.Button;
	import flash.events.MouseEvent;
 
	public class Client extends Sprite
	{
		public var chopper:Sprite=new Helicopter();
		private var contextNow:Context=new Context(chopper);
 
		private var upBtn:Button=new Button();
		private var downBtn:Button=new Button();
		private var stopBtn:Button=new Button();
		private var leftBtn:Button=new Button();
		private var rightBtn:Button=new Button();
		private var testBtn:Button=new Button();
 
		private const AEROPORTX:Number=84;
		private const AEROPORTY:Number=64;
 
		public function Client()
		{
			chopper.x=AEROPORTX,chopper.y=AEROPORTY,
			addChild(chopper);
 
			//Default
			contextNow.stopMove();
 
			upBtn.x=132,upBtn.y=465;
			upBtn.width=45, upBtn.height=25;
			upBtn.label="Up";
			upBtn.addEventListener(MouseEvent.CLICK, doUp);
			addChild(upBtn);
 
			stopBtn.x=132,stopBtn.y=513;
			stopBtn.width=45,stopBtn.height=25;
			stopBtn.label="Stop";
			stopBtn.addEventListener(MouseEvent.CLICK, doStop);
			addChild(stopBtn);
 
			downBtn.x=132,downBtn.y=561;
			downBtn.width=45,downBtn.height=25;
			downBtn.label="Down";
			downBtn.addEventListener(MouseEvent.CLICK, doDown);
			addChild(downBtn);
 
			leftBtn.x=69,leftBtn.y=513;
			leftBtn.width=45,leftBtn.height=25;
			leftBtn.label="Left";
			leftBtn.addEventListener(MouseEvent.CLICK, doLeft);
			addChild(leftBtn);
 
			rightBtn.x=200,rightBtn.y=513;
			rightBtn.width=45,rightBtn.height=25;
			rightBtn.label="Right";
			rightBtn.addEventListener(MouseEvent.CLICK, doRight);
			addChild(rightBtn);
		}
 
		private function doUp(e:MouseEvent):void
		{
			contextNow.upMove();
		}
 
		private function doStop(e:MouseEvent):void
		{
			contextNow.stopMove();
		}
 
		private function doDown(e:MouseEvent):void
		{
			contextNow.downMove();
		}
 
		private function doLeft(e:MouseEvent):void
		{
			contextNow.leftMove();
		}
 
		private function doRight(e:MouseEvent):void
		{
			contextNow.rightMove();
		}
	}
}

As you can see, it can make five different requests based on the button event handlers, and all of them are through Context methods (e.g., contextNow.rightMove()).

Because the example is so simple, you may be thinking that you could have done the same thing with a single class and not an exhaustive amount of code. You’d be absolutely right. Also, you would not lose an ounce of functionality. However, the focus of this example is to see how delegation is used and how easy it would be to use the exact same code for all different kinds of movement. Further, you could re-use the code for moving as many objects as you wanted. Because the entire program is so loosely coupled, substitution and change is very simple.

Anyway, you can do a lot with the State Design Pattern. For starters, why not improve on this little application and turn it into a game? Fo example, you could add an independent sprite to be picked up at the airport and dropped at the 10 different locations. Add one timer for continuous movement and another for keeping score of how fast you could move all of the aid packages to the needy orange sprites.

This might be a good resource for our next Golden LunchBox contest!

40 Responses to “ActionScript 3.0 State Design Pattern: An Aid Game”


  • Hi Bill,
    I’ve been studying the Aid Game program and I see we need two clicks on each button to get each state do the movement job.

    Wouldn’t it be better if we change the design and have only two states: moving and stopped?
    I think we need that if we are going to use Chandima’s continuous movement algorithm.

    Thank you
    Curro

  • Hi Curro,

    You need two clicks –one to change state and the other to move it. However, once in a given state, it only takes a single click. All I was pointing to in Chandima’s example was continuous movement; not change of direction. If you were going to use just stop and go, you’d only be able to go in a single direction. You’d need at least 3–stop; change direction; and go.

    Go ahead and make changes, and let’s see what you come up with.

    Kindest regards,
    Bill

  • You could make the implementation better in two ways.

    Firstly have a superclass, MoveState implement all the methods since the default is always a variant of: context.setState(.context.getUp());
    Then each individual state could extend the superclass and only override its particular state. So, the moveUp State has only:
    override public function doMoveUp():void {
    this.context.chopper.y-=8;
    this.context.setState(this.context.getUp());
    }

    This would save a LOT of repetition.

    The next improvement would be instead of the states calling methods in the parent context have them dispatch an event to do it. Looser coupling would be better.

    Thanks for the example though, good starting point.
    Rob

  • Rob,

    You’ve violated the Second Principle of Object-Oriented Design!

    Favor object composition over class inheritance.

    Your approach does not make coupling looser. It is a step backwards because you take a perfectly good composition and revert to inheritance. Why would you do that?

    Repetition is not an issue here. Take the example and extend the composition. Let’s see what you got!

    Kindest regards,
    Bill

  • Bill,
    I have followed the composition but I have made a few changes:

    Context extends now the EventDispatcher class.
    I somehow follow Chandima’s movement system
    I have added two new classes: one for Helicopter and one for the blocks
    I have detached the aide Sprite from the Helicopter
    The chopper registration point is different

    I think I have coupled the classes because now I have to type chopper as Helicopter.

    I am not posting the code here because I always make such a mess on the pages when I post it.

    How can I send it?

    Regards

    Curro

  • Hi Curro,

    Put it in a .zip file and send it to me at:

    wdsanders@comcast.net

    Thanks,
    Bill

  • Hi Bill,

    You reply to me suggesting I’m violating the rule of favouring composition over inheritance, but I don’t think that’s true. The composition I’m using is essentially by using an abstract superclass that each of the sub-classes, the upstate, downstate, etc implement. However, they only need implement the behaviour they actually carry out rather than repeating in each state all the behaviour they don’t carry out. That is better.

    Having each behaviour repeated exactly except for one method in every every state will surely lead to mistakes. What if you decide to get the state classes to send an event instead of calling a method in the parent. Instead of making those changes in a superclass you would have to repeat those changes multiple times in every state. That doesn’t sound good to me. I think we have to use the rule of ‘favouring composition over inheritance’ with some eye to when it makes sense, we shouldn’t just ignore inheritance as a tool in our armoury.

    Regards, Rob

  • Bill,

    In case it’s helpful or makes things clearer, here’s a link to a zip file containing the implementation I’m talking about:
    StateEvents_withEvents.zip.

    Regards, Rob

  • Hi Rob,

    Thanks again for responding. (Also I fixed up the reference to Curro.)

    You’re still missing a fundamental point, I believe. You’re confusing a repetition of code with code in different contexts and states.

    Let me illustrate: Suppose you’re in Room A. You get an instruction—”Go to the next room.” The next room is Room B. In Room B, you get an identical instruction—”Go to the next room.” The instruction “Go to the next room” means something different in each of the two rooms—or states. The specific meaning of “Go to the next room is context dependent. So in one context it means one thing and in another context; something entirely different. That is why the code is not the same—different states; different meanings.

    It seems that your comment is trying to fix algorithms and misses the essential point and approach of design patterns. The focus is on composition and delegation, and as I’ve noted elsewhere (and as do GoF), composition and inheritance work together. However, when you have composition, it is favored over an approach that favors inheritance as your suggestion does.

    This is a difficult point and one that programmers either eventually get or not. I must have read Chapter 1 in GoF over 100 times and pp. 18-22, 200 times, and I’m still having to go back over those pages. So if you re-read those pages (and perhaps the chapter on the State Design Pattern), you’ll better see what I mean.

    If you want to suggest changes; think about adding something that works with the composition; you can change the algorithms (function or method implementation) in the design without changing the structure.

    I tried the link you sent with no joy. If you send it again, I’ll make sure everyone can read it.

    Kindest regards,
    Bill

  • Hello Bill,

    I do appreciate your thoughts. However, I’d suggest that the instruction ‘go to the next room’ is the default instruction that I would encapsulate in an abstract class that implements state. Then the individual states need only implement the behaviour that varies – another key OO concept. These individual concrete instances of each state interface are still held as compositions in the context controller.

    You must have tried the link before I’d got it set up, hopefully this one works and perhaps seeing the code might show that I don’t think I’m not understanding, nor failing to implement the State pattern. But I’m enjoying the thought provoking debate for sure.

    StatePattern_withEvents.zip

    Regards, Rob

  • Wouldn’t this example be better done as strategy instead of state? There is a lot of repetition that was stated by Rob. So wouldn’t it make more sense to pass in a strategy of movement for each of the buttons rather than a state? I understand if you were using this as an example of how to use a state system, but it seems like a difficult task if you wanted a moveDiagonalNW, moveDiagonalSW, moveDiagonalNE, and moveDiagonalSE. Now you not only need to add 4 new classes, but 4 new methods to each of the other classes. A strategy pattern would only need to call a different implementation of a move method.

  • Hi Bill,

    The game now lets you choose what type of aid(medical aid or food aid) you want to take to the blocks. It actually lets you drop the aid on the blocks if the aid is of the type required by the block(You just have to stop on the block).When all the blocks have their aid the game fishishes and the chopper goes back to the airport.

    Anyway I am still not very happy with the two clicks per action. I am going to try another version with 3 states: stopped, flying and hover.I hope to solve the double click problem this way but I still don’t know how to do it.

    @Rob, I’ve been looking at your code. I don’t understand why your Context class extens Sprite. You don’t use any of its functions. I don’t like the the big switch there. I think that kills the state pattern purpose and you still need two clikcs to get anything done.
    What I like is the idea of an event based change.

    Regards to everybody

    Curro

  • Curro,

    I extend Sprite simply so I can attach an event listener. I solved the two clicks by adding in each move a test:
    function domoveright
    if stateNow not equal to moveright then
    dispatch state change event right
    end
    stateNow domoveright

    in pseudocode.

    the switch just seemed an easy way for the contect to respond to the event. Better I thought than having the state directly call a method of the context.

  • Hi Bob and Rob,
    I have solved the problem of the two ckicks per action with two states:one StoppedState, from which you can only go up and one FlyingState, from which you can move in 8 directions. You only need a click to change, start or stop movement.
    Of course I use timer based movement.
    I’ll try to put a zip file on a friends website so that you can download it.

    It’s been real fun doing this.

    Thank you

    Curro

  • Curro,

    I look forward to seeing your code.

    It’s funny, after reading what Todd said too, I woke up this morning wondering whether two states of flying or landed might be better and then, only when in a flying state of swapping out strategies of flight – moveRight strategy, moveLeft strategy etc all of which only have a method called Fly?

    Rob

  • Actually my earlier suggestion, in pseudocode, of adding a test into the context in order to avoid two clicks also completely circumvents the state mechanism and returns conditional logic into the context and is a bad idea – even though it solves the problem. Sorry.

  • Hi Rob,

    What happens too often in some of these discussions is that we quickly move away from the point of the post into an attempt to find a better way to implement a solution. Quick minds have a tendency to do that.

    Perhaps a better way to respond to your comments is to rewind to the original intent of this post. Let me direct your attention to the first paragraph of the original post. This post is about setting up a simple yet workable example of composition—namely delegation. Then, I would be able to write the post on aggregation with a focus on delegation and have an example for reference.

    When doing this I was thinking of what Gamma, Helm, Johnson and Vlissides had said and ways I could implement their explanation of delegation. The quote I am referring to on page 21 is:

    Delegation works best when it’s used in highly stylized ways—that is, in standard patterns.

    I implemented a simple standard State pattern so that in the next post on aggregation (which is the relationship shown between the Context class and State interface) I could point to this little post and say, Look at the Aid State example.

    To understand design patterns, we need to keep our focus on the participants in the design and the relations between the participants. Like everyone who spends time on this blog, I want to better understand all design pattern aspects and not get side-tracked by trying to work out algorithms. Once we get the design pattern set and working, we can go back and fiddle with the algorithms all we want—they’re just operations.

    One of the problems we encounter is that design patterns were developed for seriously large and complex operations. All of us have grappled with 1000+ line code and became entangled in major problems when it comes time to change and re-write the whole thing. Had we used design patterns, we would not have been spider-webbed by our own code. However, we use short and simple examples on this blog for the purpose of exposing the structures of design patterns. As a result, many programmers look for ways to reduce the code or change relationships between participants in the pattern. That, I am afraid, is what has happened here and the point of the post is lost.

    So I ask that you please bear with me, and when we discuss aggregation relations, you will be able to easily see how delegation works in this example. I don’t mean to sound school-marmish, but we do need to keep focused to understand this stuff.

    Kindest regards,
    Bill

  • Hi Bill,
    You are right there. After reading your post I’ve reviewed my version and seen that we can solve the problem of the two clicks per action by simply placing the actual movement code(”chopper.x = 3″, etc or, in my version, “context.xSpeed = 3″, etc.) in each function of each state. That way the actions take place before each state is set and we only need one click to get things done. This means we don’t need to cut down on States to get one click responses.

    Besides, having 5 states instead of only two lets us extend the app and add subtleties to get more realistic movement(for example, we could detach the chopper’s rotor from the chopper sprite and make it move differently depending on the different states)

    This discussion it’s being so instructive that’s difficult to leave it.

    Thanks

    Curro

  • Hi Bill,
    I’ve read again your original post and your chapter on the State pattern and I see I’ve missed the point altogether and been fooling around.

    I want to beg your pardon as it was me who started deviating from the original purpose.

    We (rob, Todd and me)focused on an implementation instead of on the benefits and purpose of the State pattern: adding encapsulated behaviours to a class.

    @ Rob @Todd:
    We should have extended the game by adding new behaviours (States) instead of modifying the original classes and focusing on details.

    I am now adding a Rescue behaviour to the game so that we can also pick up injured people from the blocks and take them to the security of the airport.

    Bill,
    thank you very much for your patience and savoir être. I am a teacher and I kwow now that I’ve been unruly and naughty.

    Sorry,

    Curro

  • Hi Curro,

    I don’t think any of our readers have been unruly or naughty. (On this blog at least….) Getting design patterns right requires a whole new way of thinking, and it’s not easy. However, on the other end (once you get it), you’ll look back and see your programming has ascended to a whole new level.

    For Chandima and I, and most other programmers I know, this is a continuous learning process, and while it does get easier, as soon as you think you’ve got it all, it has a way of humbling us.

    Kindest regards,
    Bill

  • Hello everybody!

    Bob, Rob and Curro might release a complete mini-game out of this post! That would be really cool indeed. This post would have become a great Lunch Box context!

    @Bill Sanders, I’ve seen this situation happening before in your blog. People eager to discuss implementation details instead of the “bigger” concepts and at some point you have to moderate everything. Kind of a bummer but necessary.

    I noticed that this is happening usually when you post implementation examples. It is great to have the examples but they stimulate people to discuss it, like it or not. It would be nice to have some specific post category for implementation and other for concepts. Could even be in pairs: “State Pattern concept thoughts and deviations” and “State Pattern: down and dirty on code!”. That could help guide the discussions to the aimed direction and even improve your posts!

    When I read it, I thought you were focusing on the implementation because you really go into its details and don’t discuss much on the advantages and limitations of the patterns, the participants or theory behind it. Not really focused on what you had in mind, IMHO.

    I hope you don’t get me wrong here and take this post as a really sincere light-hearted opinion that can help improve something.

    Cheers!

  • Hi William,

    I see your point. Of course I knew what I was going to talk about next, and I needed a Q&D example for reference. My focus was on the larger structure. Maybe there’ll be a surprise for you yet.

    Kindest regards,
    Bill

  • Respectfully, William R. J. Ribeiro, I disagree. The examples serve as a good implementation of what Bill is talking about with us. I enjoy the example posts the most, although I do think occasionally there needs to be just theory, which is already being done on this site.
    I am still not sure of my original thought though. Moving an item around the screen still seems like more of a need for a strategy pattern. Where the game might be better served using a state pattern for what level the game is in, or what box was interacted with. Still not sure why a state pattern would be used to interact with the helicopter’s movements.

  • Hey Bill, great post.
    One thing I’m wondering: why not have your state interface implement a single method with a return value such as;
    function move(currentPos:point):point;
    The state machine could then just set a current state based on the button clicked (perhaps link each state to a button with a dictionary so you could use a single handler function).
    After the current state is set, simply call “move” on that state, and use the return value to update the chopper position.

    The states would remain unaware of the state machine, and would all implement the same interface.

    Keep up the good work!
    -t.

  • Hi Timbot,

    I wasn’t paying that much attention to the state algorithms in that example but rather was putting it together to look at aggregate relations. However, the basic structure is okay, and what needs to be tweaked is a better sense of ongoing state. When I was working with a video player and a State design, going to a playing state, to a stopped state, to a record state or a play state was relatively easy because they had more intuitive state-like characteristics. For example, a play state is an ongoing state that continuously plays until the video is complete. The “states” that I used in this example were just little moves and non-continuous.

    What I’m working on now is an update of this Aid Game so that the states are more intuitively “state-like”—that is they keep on a given state. A light bulb being in an on state or an off state provides a better intuitive grasp of being in a state. The little jumps don’t have that same sense.

    When I revise the Aid Game, I’ll pay more attention to the transition from state to state and I think you’ll better understand the State design pattern and why you don’t want to change the basic structure provided by the Gang of Four.

    Kindest regards,
    Bill

  • Hi William,
    There is one thing that I don’t get in this design pattern…
    Why are you moving the chopper inside the states? Why are you doing VERY specific (Changing the x & y properties of a movieClip) inside the state?

    If you one day change it to something else, that doesn’t have the x property, or it’s not even a chopper that you want to move… you’ll have to go over all the states and fix that, and it doesn’t have to do nothing with the design pattern, but still you will have to go over all the states and fix it…

    I read the chapter about the State design pattern in your book, and you said it many times, that this pattern is good for moving for state to state in a very specific way, like from Play to Pause, and from Pause to Play, but not from Pause to Stop etc….
    IMHO, State is good for when you want something to change states in a very complicated way… and you want to avoid this “Switch Case” all over your code, and this complicated way of moving from state to state is the power of this design pattern… putting chopper.x-=8 in the state is too specific to your case…

    If I had to implement a code that can be in many different states, and the moving from state to state is described by a very specific model (Like a graph) I would use the state pattern, without letting it know about the objects that get changed…

    for example:
    If an “Object” can Lay down, Sit, Stand, Run, Sleep, Jump, and Fly. I can see that this “Object” should not be able to Jump if it’s not Standing or running… and can not Sit if it’s not Standing or Laying down etc.
    This sounds like state pattern, and I would not change the Object currentFrame to be in the right animation… I would let it know in some other way(s), but the state pattern is general and separate from the animation of the object!

    I hope that I’m not too wrong. correct me if I do.

    Thanks
    Gil

  • thanks for that last explanation, cleared up a lot for me Bill.

  • Hi Gil,

    Your points are good ones. As I noted in one of my other comment replies, I was just putting something together quickly to demonstrate an aggregate relationship. That I was able to do.

    However, I was paying minimal attention to the essence of both a State machine and a State design pattern. I am working on a follow-up to give the movement for “state-like” qualities. This is what I have planned:

    -move up
    -move down
    -move left
    -move right
    -stop

    The movement will be continuous so that each will have more “stateness” ;>} Also, I’ll set it up so that you have to go to stop before reversing direction. However, like I said, the example was just to show the aggregation.

    Thanks for you comments, and it looks like you really are getting this stuff!!

    Kindest regards,
    Bill

  • Hi every body,
    I don’t quite agree with Gil about how abstract has a State pattern to be.

    If the states are “moveUp”, “moveDown”, an so on it makes real sense to have them move up or down an object. I don’t mean to change the x or y property of a sprite but they can change, let us say, some general XSpeed or ySpeed properties in the Context class, that would then be applied to any “movable” object on the stage based on continuous movement.

    I think the states must have some level of concretion. If they can’t do anything … what do we want them for?

    Curro

  • Hi Curro,

    The State pattern need not be abstract—only the State interface. Of course the concrete states themselves must do something—or more accurately—transition into another state that changes. So, to move from a Stop state, you need to fire off a method that moves and then sends a message to the Context that the new state is now moves. Remember OOP Quote #1!

    Cheers,
    Bill

  • Hi Bill,
    You mean adding another layer of abstraction between the states and the movement code or simply placing the movement code before we change the state in the context?

    In one of the implementations I’ve done with the Aid Game the movement code(xSpeed = 3,ySpeed ..)inside each move function is called before calling setState. It’s effective but I’m not quite sure if that is OOP. It looks like a mixture of the Strategy and the State pattern and not very elegant. You have to repeat that code in each move function.
    How could we “fire off a method that moves and then sends a message to the Context that the new state is now moves”?

    Is calling setState such a message?

    Hope this is not asking too much.

    Thank you

    Curro

  • Hi Curro,

    Exactly! When each state is called via the Context, it has the following code:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    
    <snip>
    //The move up state is called
    public function doMoveUp():void
    {
    	context.setState(context.getUp());
    }
    </snip>

    It uses the getter as a parameter — getUp() — and the setter — setState(..) — to communicate with the Context. This is how the Context object knows the current state.

    Kindest regards,
    Bill

  • Hi Bill,
    Reading GoF(pp 308-312 )I’ve seen a variation of the State pattern where State is not an interface but a superclass which implements default behaviours. Then we only have to override the default behaviours in the states if we want to do so. This allows for greater flexibility as we don’t have to implement all the behaviours in the different states.

    The context has to instantiate only one state subclass,and it does so through a Singleton interface(anathema?). It also has to pass itself as a parameter in all the move functions. Then either the subclasses or the superclass instantiate the other states when needed.

    What do you think of all this?

    I have followed that system for our AidGame and works beautifully.

    Thank you

    Curro

  • Hi again,
    to make things clearer I have placed the whole enchilada (I liked that expression) in the following link:
    AidGameGoF

    Thanks again for maintaining this wonderful site.

    Curro

  • Well it seems the link doesn’t work.
    I’ll try again:
    AidGame

    Sorry for the inconvenience
    Curro

  • Hi Curro,

    Keep in mind that in the GoF book the term interface is used to refer to both abstract classes and interfaces. They follow the Liskov principle and by doing so are able to work with the child classes through the interfaces rather than tied up in implementations from concrete classes.

    As far as the Singleton is concerned…shudder.

    Take care,
    Bill

  • Hi Bill,
    I was referring to the keyword “interface” as opposed to “class.” Thanks to your book and this site I now know that Interface can be both.
    In fact I can now understand the GoF book, which only four months ago was completely cryptic and unintlligible for me.

    Thanks again

    Curro

  • Hi Curro,

    Thank you, and I hope I did not sound patronizing. Also, another thing that you might want to look out for in the GoF book is their use of terms that sound like Design Patterns but are actually operations. For example, you mentioned Singleton in one of your previous comments from GoF. That can be a reference to a participant class, an operation or a design pattern. I was unable to find in the State chapter in GoF; so I’m not sure how they were using it. Let me know and I’ll look at it again.

    Kindest regards,
    Bill

  • Hi Bill,
    You didn’t sound patronizing at all and I am a really novice programmer.
    As for the singleton mentioned in the GoF State pattern it’s on page 312:”TCPState subclasses maintain no local state, so they can be shared, and only one instance is required. The unique instance of each TCPState is obtained calling the static Instance operation(9) .”
    Then footnote 9 says:
    “This makes each TCPState subclass a Singleton …”

    The instance methods are not implemented in the sample code and that made me think that instance was a built in method in C++, but it isn’t. So I decided to implement them following the Singleton pattern. I had had just a look at your articles on the Singleton pattern so I didn’t feel very comfortable doing that.
    Anyway I like the GoF variation of the State pattern. I find it very flexible.

    I find the Liskov Substitution Principle difficult to grasp, though I have read the chapter on it in “Agile Software Development” by Robert C. Martin twice. If you have seen the code I placed for downloading and seen a violation of the LSP, I’d be delighted if you could show it. Then I would learn a bit more.

    In fact, I have suggested treating the different principles in the suggestions scribbit window.

    Thank you once more

    Curro

  • Hi Curro,

    The easiest way to grasp the Liskov Substitution Principle is to think of it as—program to the interface and only subclass from interfaces or abstract classes. Then the references are always to the interface. If you make a change to the child, everything still works because you have typed a reference to the interface and not a concrete class.

    I’ve added a new post on working with the State pattern. (Fixing up the Aid Game.)

    Cheers,
    Bill

Leave a Reply