Skip to content

Commit 5192bb0

Browse files
committed
Fix Enter Actions section in State.
1 parent 421f59a commit 5192bb0

File tree

3 files changed

+133
-80
lines changed

3 files changed

+133
-80
lines changed

book/state.markdown

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -359,36 +359,35 @@ The <a href="object-pool.html" class="pattern">Object Pool</a> pattern can help.
359359
## Enter and Exit Actions
360360

361361
The goal of the State pattern is to encapsulate all of the behavior and data for
362-
one state in a single class. We've done that pretty well, but there's still a
363-
loose end. We moved handling user input and updating the charge time into
364-
`DuckingState`, but there's a bit of ducking-specific code left outside. Over in
365-
the standing state, when we *start* ducking, it does some initialization:
362+
one state in a single class. We're partway there, but we still have
363+
some loose ends.
366364

367-
^code start-ducking
365+
When the heroine changes state, we also switch her sprite. Right now, that code is owned by the state she's switching *from*. When she goes from ducking to standing, the ducking state sets her image:
368366

369-
The ducking state should take care of resetting the charge time (after
370-
all, it's the object that has that field) and playing the animation. We can
367+
^code enter-standing-before
368+
369+
What we really want is each state to control its own graphics. We can
371370
handle that by giving the state an *entry action*:
372371

373-
^code ducking-with-enter
372+
^code standing-with-enter
374373

375-
Back in `Heroine`, we wrap changing the state in a little convenience method
376-
that calls that on the new state:
374+
Back in `Heroine`, we modify the code for handling state changes to call that
375+
on the new state:
377376

378377
^code change-state
379378

380-
In the standing code we use it like:
379+
This lets us simplify the ducking code to:
381380

382-
^code enter-ducking
381+
^code enter-standing
383382

384-
Now ducking really is encapsulated. One particularly nice thing about entry
385-
actions is that they run when you enter the state regardless of which state
386-
you're coming *from*.
383+
All it does is switch to standing and the standing state takes care of
384+
the graphics. Now our states really are encapsulated. One particularly nice thing
385+
about entry actions is that they run when you enter the state regardless of
386+
which state you're coming *from*.
387387

388388
Most real-world state graphs have multiple transitions into the same state. For
389-
example, maybe our heroine can fire her weapon while standing, ducking, and
390-
jumping. That means you end up duplicating some code everywhere that transition
391-
occurs. Entry actions give you a place to consolidate that.
389+
example, our heroine will also end up standing after she lands a jump or dive. That means we would end up duplicating some code everywhere that transition
390+
occurs. Entry actions give us a place to consolidate that.
392391

393392
We can, of course, also extend this to support an *exit action*. This is just a
394393
method we call on the state we're *leaving* right before we switch to the new

code/cpp/state.h

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -517,12 +517,14 @@ namespace State
517517
friend class JumpingState;
518518
public:
519519
void setGraphics(Animate animate) {}
520+
void changeState(HeroineState* state) {}
520521
private:
521522
HeroineState* state_;
522523
};
523524

524525
class StandingState : public HeroineState {};
525526
class DuckingState : public HeroineState {};
527+
526528
class JumpingState : public HeroineState {
527529
void handleInput(Heroine& heroine, Input input)
528530
{
@@ -538,6 +540,42 @@ namespace State
538540
class DivingState : public HeroineState {};
539541
}
540542

543+
namespace EnterActionsBefore
544+
{
545+
class HeroineState {};
546+
547+
class Heroine
548+
{
549+
friend class JumpingState;
550+
public:
551+
void setGraphics(Animate animate) {}
552+
void changeState(HeroineState* state) {}
553+
};
554+
555+
class StandingState : public HeroineState {};
556+
class DuckingState : public HeroineState {
557+
public:
558+
HeroineState* handleInput(Heroine& heroine, Input input);
559+
};
560+
561+
//^enter-standing-before
562+
HeroineState* DuckingState::handleInput(Heroine& heroine,
563+
Input input)
564+
{
565+
if (input == RELEASE_DOWN)
566+
{
567+
heroine.setGraphics(IMAGE_STAND);
568+
return new StandingState();
569+
}
570+
571+
// Other code...
572+
//^omit
573+
return NULL;
574+
//^omit
575+
}
576+
//^enter-standing-before
577+
}
578+
541579
namespace EnterActions
542580
{
543581
class Heroine;
@@ -547,67 +585,70 @@ namespace State
547585
public:
548586
virtual ~HeroineState() {}
549587
virtual void enter(Heroine& heroine) {}
550-
virtual void handleInput(Heroine& heroine, Input input) {}
551-
virtual void update(Heroine& heroine) {}
588+
virtual HeroineState* handleInput(Heroine& heroine, Input input)
589+
{
590+
return NULL;
591+
}
552592
};
553593

554594
class Heroine
555595
{
556596
public:
597+
void handleInput(Input input);
557598
void setGraphics(Animate animate) {}
558-
void superBomb() {}
559-
void changeState(HeroineState* state);
560599
private:
561600
HeroineState* state_;
562-
double yVelocity_;
563601
};
564602

565-
//^ducking-with-enter
566-
class DuckingState : public HeroineState
603+
//^standing-with-enter
604+
class StandingState : public HeroineState
567605
{
568606
public:
569-
//^omit
570-
DuckingState()
571-
: chargeTime_(0)
572-
{}
573-
574-
//^omit
575607
virtual void enter(Heroine& heroine)
576608
{
577-
chargeTime_ = 0;
578-
heroine.setGraphics(IMAGE_DUCK);
609+
heroine.setGraphics(IMAGE_STAND);
579610
}
580611

581612
// Other code...
582-
//^omit
583-
private:
584-
int chargeTime_;
585-
//^omit
586613
};
587-
//^ducking-with-enter
614+
//^standing-with-enter
588615

589616
//^change-state
590-
void Heroine::changeState(HeroineState* state)
617+
void Heroine::handleInput(Input input)
591618
{
592-
delete state_;
593-
state_ = state;
594-
state_->enter(*this);
619+
HeroineState* state = state_->handleInput(*this, input);
620+
if (state != NULL)
621+
{
622+
delete state_;
623+
state_ = state;
624+
625+
// Call the enter action on the new state.
626+
state_->enter(*this);
627+
}
595628
}
596629
//^change-state
597630

598-
class StandingState : public HeroineState
631+
class DuckingState : public HeroineState
599632
{
600633
public:
601-
virtual void handleInput(Heroine& heroine, Input input)
634+
virtual HeroineState* handleInput(Heroine& heroine, Input input);
635+
};
636+
637+
//^enter-standing
638+
HeroineState* DuckingState::handleInput(Heroine& heroine,
639+
Input input)
640+
{
641+
if (input == RELEASE_DOWN)
602642
{
603-
//^enter-ducking
604-
if (input == PRESS_DOWN)
605-
{
606-
heroine.changeState(new DuckingState());
607-
}
608-
//^enter-ducking
643+
return new StandingState();
609644
}
610-
};
645+
646+
// Other code...
647+
//^omit
648+
return NULL;
649+
//^omit
650+
}
651+
//^enter-standing
611652
}
612653

613654
namespace Concurrent

html/state.html

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -592,63 +592,76 @@ <h3><a href="#instantiated-states" name="instantiated-states">Instantiated state
592592

593593
<h2><a href="#enter-and-exit-actions" name="enter-and-exit-actions">Enter and Exit Actions</a></h2>
594594
<p>The goal of the State pattern is to encapsulate all of the behavior and data for
595-
one state in a single class. We&#8217;ve done that pretty well, but there&#8217;s still a
596-
loose end. We moved handling user input and updating the charge time into
597-
<code>DuckingState</code>, but there&#8217;s a bit of ducking-specific code left outside. Over in
598-
the standing state, when we <em>start</em> ducking, it does some initialization:</p>
599-
<div class="codehilite"><pre><span class="c1">// In standing state:</span>
600-
<span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">PRESS_DOWN</span><span class="p">)</span>
595+
one state in a single class. We&#8217;re partway there, but we still have
596+
some loose ends.</p>
597+
<p>When the heroine changes state, we also switch her sprite. Right now, that code is owned by the state she&#8217;s switching <em>from</em>. When she goes from ducking to standing, the ducking state sets her image:</p>
598+
<div class="codehilite"><pre><span class="n">HeroineState</span><span class="o">*</span> <span class="n">DuckingState</span><span class="o">::</span><span class="n">handleInput</span><span class="p">(</span><span class="n">Heroine</span><span class="o">&amp;</span> <span class="n">heroine</span><span class="p">,</span>
599+
<span class="n">Input</span> <span class="n">input</span><span class="p">)</span>
601600
<span class="p">{</span>
602-
<span class="c1">// Change state...</span>
603-
<span class="n">chargeTime_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
604-
<span class="n">setGraphics</span><span class="p">(</span><span class="n">IMAGE_DUCK</span><span class="p">);</span>
601+
<span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">RELEASE_DOWN</span><span class="p">)</span>
602+
<span class="p">{</span>
603+
<span class="n">heroine</span><span class="p">.</span><span class="n">setGraphics</span><span class="p">(</span><span class="n">IMAGE_STAND</span><span class="p">);</span>
604+
<span class="k">return</span> <span class="k">new</span> <span class="nf">StandingState</span><span class="p">();</span>
605+
<span class="p">}</span>
606+
607+
<span class="c1">// Other code...</span>
605608
<span class="p">}</span>
606609
</pre></div>
607610

608611

609-
<p>The ducking state should take care of resetting the charge time (after
610-
all, it&#8217;s the object that has that field) and playing the animation. We can
612+
<p>What we really want is each state to control its own graphics. We can
611613
handle that by giving the state an <em>entry action</em>:</p>
612-
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">DuckingState</span> <span class="o">:</span> <span class="k">public</span> <span class="n">HeroineState</span>
614+
<div class="codehilite"><pre><span class="k">class</span> <span class="nc">StandingState</span> <span class="o">:</span> <span class="k">public</span> <span class="n">HeroineState</span>
613615
<span class="p">{</span>
614616
<span class="nl">public:</span>
615617
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">enter</span><span class="p">(</span><span class="n">Heroine</span><span class="o">&amp;</span> <span class="n">heroine</span><span class="p">)</span>
616618
<span class="p">{</span>
617-
<span class="n">chargeTime_</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
618-
<span class="n">heroine</span><span class="p">.</span><span class="n">setGraphics</span><span class="p">(</span><span class="n">IMAGE_DUCK</span><span class="p">);</span>
619+
<span class="n">heroine</span><span class="p">.</span><span class="n">setGraphics</span><span class="p">(</span><span class="n">IMAGE_STAND</span><span class="p">);</span>
619620
<span class="p">}</span>
620621

621622
<span class="c1">// Other code...</span>
622623
<span class="p">};</span>
623624
</pre></div>
624625

625626

626-
<p>Back in <code>Heroine</code>, we wrap changing the state in a little convenience method
627-
that calls that on the new state:</p>
628-
<div class="codehilite"><pre><span class="kt">void</span> <span class="n">Heroine</span><span class="o">::</span><span class="n">changeState</span><span class="p">(</span><span class="n">HeroineState</span><span class="o">*</span> <span class="n">state</span><span class="p">)</span>
627+
<p>Back in <code>Heroine</code>, we modify the code for handling state changes to call that
628+
on the new state:</p>
629+
<div class="codehilite"><pre><span class="kt">void</span> <span class="n">Heroine</span><span class="o">::</span><span class="n">handleInput</span><span class="p">(</span><span class="n">Input</span> <span class="n">input</span><span class="p">)</span>
629630
<span class="p">{</span>
630-
<span class="k">delete</span> <span class="n">state_</span><span class="p">;</span>
631-
<span class="n">state_</span> <span class="o">=</span> <span class="n">state</span><span class="p">;</span>
632-
<span class="n">state_</span><span class="o">-&gt;</span><span class="n">enter</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">);</span>
631+
<span class="n">HeroineState</span><span class="o">*</span> <span class="n">state</span> <span class="o">=</span> <span class="n">state_</span><span class="o">-&gt;</span><span class="n">handleInput</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span>
632+
<span class="k">if</span> <span class="p">(</span><span class="n">state</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
633+
<span class="p">{</span>
634+
<span class="k">delete</span> <span class="n">state_</span><span class="p">;</span>
635+
<span class="n">state_</span> <span class="o">=</span> <span class="n">state</span><span class="p">;</span>
636+
637+
<span class="c1">// Call the enter action on the new state.</span>
638+
<span class="n">state_</span><span class="o">-&gt;</span><span class="n">enter</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">);</span>
639+
<span class="p">}</span>
633640
<span class="p">}</span>
634641
</pre></div>
635642

636643

637-
<p>In the standing code we use it like:</p>
638-
<div class="codehilite"><pre><span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">PRESS_DOWN</span><span class="p">)</span>
644+
<p>This lets us simplify the ducking code to:</p>
645+
<div class="codehilite"><pre><span class="n">HeroineState</span><span class="o">*</span> <span class="n">DuckingState</span><span class="o">::</span><span class="n">handleInput</span><span class="p">(</span><span class="n">Heroine</span><span class="o">&amp;</span> <span class="n">heroine</span><span class="p">,</span>
646+
<span class="n">Input</span> <span class="n">input</span><span class="p">)</span>
639647
<span class="p">{</span>
640-
<span class="n">heroine</span><span class="p">.</span><span class="n">changeState</span><span class="p">(</span><span class="k">new</span> <span class="n">DuckingState</span><span class="p">());</span>
648+
<span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">RELEASE_DOWN</span><span class="p">)</span>
649+
<span class="p">{</span>
650+
<span class="k">return</span> <span class="k">new</span> <span class="n">StandingState</span><span class="p">();</span>
651+
<span class="p">}</span>
652+
653+
<span class="c1">// Other code...</span>
641654
<span class="p">}</span>
642655
</pre></div>
643656

644657

645-
<p>Now ducking really is encapsulated. One particularly nice thing about entry
646-
actions is that they run when you enter the state regardless of which state
647-
you&#8217;re coming <em>from</em>.</p>
658+
<p>All it does is switch to standing and the standing state takes care of
659+
the graphics. Now our states really are encapsulated. One particularly nice thing
660+
about entry actions is that they run when you enter the state regardless of
661+
which state you&#8217;re coming <em>from</em>.</p>
648662
<p>Most real-world state graphs have multiple transitions into the same state. For
649-
example, maybe our heroine can fire her weapon while standing, ducking, and
650-
jumping. That means you end up duplicating some code everywhere that transition
651-
occurs. Entry actions give you a place to consolidate that.</p>
663+
example, our heroine will also end up standing after she lands a jump or dive. That means we would end up duplicating some code everywhere that transition
664+
occurs. Entry actions give us a place to consolidate that.</p>
652665
<p>We can, of course, also extend this to support an <em>exit action</em>. This is just a
653666
method we call on the state we&#8217;re <em>leaving</em> right before we switch to the new
654667
state.</p>

0 commit comments

Comments
 (0)