2005
6
7
8
9
10
11
12
2006
1
2
3
4
5
6
7
8
9
10
11
12
2007
1
2
3
4
5
6
7
8
9
10
11
12
2008
1
2
3
4
5
6
7
8
9
10
11
12
2009
1
2
3
4
5
6
7
8
9
10
11
12
2010
1
2
3
4
5
6
7
8
9
10
11
12
2011
1
2
3
4
5
6
7
8
9
10
11
12
2012
1
2
3
4
5
6
7
8
9
10
11
12
2013
1
2
3
4
5
6
7
8
9
10
11
12
2014
1
2
3
4
5
6
7
8
9
10
11
12
2015
1
2
3
4
5
6
7
8
9
10
11
12
2016
1
2
3
4
5
6
7
8
9
10
11
12
2017
1
2
3
4
5
6
7
8
9
10
11
12
2018
1
2
3
4
5
6
7
8
9
10
11
12
2019
1
2
3
4
5
6
7
8
9
10
11
12
2020
1
2
3
4
5
6
7
8
9
10
11
12
2021
1
今さらStateパターンの話。 某所で「Stateパターンは重いからイヤだ」ということで、 こんな感じのコードを見かけたんだけど: class CDPlayer def handle_event(event) case event when :play handle_play when :stop handle_stop end end def handle_play case @state when :stopping start_music @state = :playing when :playing pause_music @state = :pausing when :pausing resume_music @state = :playing end end def handle_stop if @state != :stopping stop_music @state = :stopping end end def start_music; end def pause_music; end def resume_music; end def stop_music; end end 再生ボタンを二度押しするとポーズになるという仕様ね。 まぁ、イヤだといってるのにわざわざStateパターンを 使う必要もないんだけど: class CDPlayer def handle_event(event) state_code = @state.handle_event(event) case state_code when :playing @state = @playing when :pausing @state = @pausing when :stopping @state = @stopping end end def start_music; end def pause_music; end def resume_music; end def stop_music; end end class Playing def handle_event(event) case event when :play @player.pause_music return :pausing when :stop @player.stop_music return :stopping end end end class Pausing def handle_event(event) case event when :play @player.resume_music return :playing when :stop @player.stop_music return :stopping end end end class Stopping def handle_event(event) if event != :stop @player.stop_music end return :stopping end end かなり適当なコードで申し訳ない。 まぁ、これを見ると、確かに「Stateパターンて重いよね」 という話になるかもしんないけど。 でも、思ったんだけど、Stateパターンでは、多態を使って switch文をなくすことが強調されるわけだけど。でも、 GoFだとContextっていうのがいるよね。あれが実はポイント なんじゃないかと思ったんだよね。 関心の分離という話があって: 関心の分離 Stateパターンで実現したいのは何かというと、状態遷移と いう関心事を切り離したいのが本線なんじゃないかと 思ったわけ: class CDPlayer def handle_event(event) @context.handle_event(event) end def start_music; end def pause_music; end def resume_music; end def stop_music; end end class CDPlayerContext def handle_event(event) state_code = @state.handle_event(event) case state_code when :playing @state = @playing when :pausing @state = @pausing when :stopping @state = @stopping end end end この関心の分離の話は、最初のコードにもいえるわけで: class CDPlayer def handle_event(event) @context.handle_event(event) end def start_music; end def pause_music; end def resume_music; end def stop_music; end end class CDPlayerContext def handle_event(event) case event when :play @player.handle_play when :stop @player.handle_stop end end def handle_play case @state when :stopping @player.start_music @state = :playing when :playing @player.pause_music @state = :pausing when :pausing @player.resume_music @state = :playing end end def handle_stop if @state != :stopping @player.stop_music @state = :stopping end end end で、「Stateパターンは重いか?」の話に戻るんだけど。 自分はそんなに重いとは思わないんだよね。クラスが 増えるのは面倒っちゃ面倒だけど。でも、個々の状態 クラスは重くなりにくいし。上のコードでいえば、音楽の 再生なんかに関する機能はCDPlayerに集まってるでしょ。 で、状態クラスが重くなるんだったら、それこそState パターンが活きてくる。 -- ところで、上のコードで「停止状態のときに停止ボタンが 押されたらどうするの?」という話があって: class Stopping def handle_event(event) if event != :stop @player.stop_music end return :stopping end end あ、個人的には: class Stopping def handle_event(event) @player.stop_music return :stopping end end って書きたいほうなんだけど。 で、このコードだと「停止状態から停止状態に遷移する」 ってことになるんだけど。こういう感じで「ある状態から 同じ状態に遷移する」っていうのは状態遷移でよくある話で。 +------+ |aState|<--+ +------+ | | | +------+ こんな感じの遷移図が描かれるわけだけど。 でも、現実には、「同じ状態に遷移する」というのと、 「どこにも遷移しない」というのじゃ違うことがあるん だよね。遷移の入り口と出口にフックを仕掛けるときとか。 「どこにも遷移しない」というのを実現するとすると: class CDPlayerContext def handle_event(event) state_code = @state.handle_event(event) case state_code when :playing @state = @playing when :pausing @state = @pausing when :stopping @state = @stopping when :stay ; end end end class Stopping def handle_event(event) if event == :stop return :stay end @player.stop_music return :stopping end end ほら、ここでContextを分離しておいた効果が出てきた。 CDPlayerをいじらずに、遷移の仕方を変えることができた。 だから、やっぱり、状態クラスを抽出するよりも、まずは Contextを抽出したほうがいいんだよね。
Copyright © 1905 tko at jitu.org