こんにちは、KOUKIです。
この記事では、前回に引き続き、デザインパターンの一つであるStateパターンについて紹介します。
<目次>
前回
デザインパターンまとめ
シチュエーション
復習ですが、State パターンは、 モノではなく「状態」をクラスとして表現するパターンです。
※Go言語ではクラスは存在しないので、構造体やインターフェースがそれらを表現します。
Handmade State Machine
WebSocketの接続状態を例に、Stateパターンを学習していきましょう。
WebSocketの接続状態
今回は、Stateの状態を定数として実装します。
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 |
package main import ( "bufio" "fmt" "os" "strconv" ) type WebSocketState int const ( // ソケット準備 Prepare WebSocketState = iota // ソケットは作成されているが、まだコネクションが開いていない状態 Connecting // コネクションが開き、通信の準備ができている状態 Open // コネクションが閉じる過程にある状態 Closing // コネクションが閉じられたか、もしくは開けていなかった状態 Closed ) func (s WebSocketState) String() string { switch s { case Prepare: return "Prepare" case Connecting: return "Connecting" case Open: return "Open" case Closing: return "Closing" case Closed: return "Closed" } return "Unknown" } |
トリガー
Stateの状態を変更するトリガーも定数として実装します。
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 |
type Trigger int const ( // WebSocketオブジェクト作成 Create Trigger = iota // 接続呼び出し Call // 接続確立 OnHook // 接続切断移行 OffHook // 接続切断 Close // 状態確認 ReadyState // メッセージ送受信 Message ) func (t Trigger) String() string { switch t { case Create: return "Create" case Call: return "Call" case OnHook: return "OnHook" case OffHook: return "OffHook" case Close: return "Close" case ReadyState: return "ReadyState" case Message: return "Message" } return "Unknown" } |
ルール
状態とトリガーを組み合わせて、ルールを作りましょう。
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 |
type TriggerRule struct { trigger Trigger state WebSocketState } // Create -> Connecting -> Open -> Closing -> Close var rules = map[WebSocketState][]TriggerRule{ Prepare: { {Call, Connecting}, {Close, Closed}, {ReadyState, Connecting}, }, Connecting: { {OnHook, Open}, {OffHook, Closing}, {ReadyState, Connecting}, }, Open: { {OffHook, Closing}, {Message, Open}, {ReadyState, Open}, }, Closing: { {Close, Closed}, {ReadyState, Closing}, }, Closed: { {Create, Prepare}, {ReadyState, Closed}, }, } |
例えば、Open(接続完了)状態の場合は、OffHook(接続切断状態への移行)、Message(メッセージを送る)、ReadyState(現在の状態の確認)の3つのトリガーを持たせました。その他の状態も同様です。
使ってみよう
ここまで実装したプログラムを使ってみましょう。
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 |
func main() { state, exitState := Prepare, Closed // StateがClosedになるまでループ for ok := true; ok; ok = state != exitState { // fmt.Println("WebSockets Connection State is", state) fmt.Println("#####WEB SOCKETS#####") fmt.Println("Select a trigger:") for i := 0; i < len(rules[state]); i++ { tr := rules[state][i] fmt.Println(strconv.Itoa(i), "<", tr.trigger, ">") } // 標準入力からデータを読み込む input, _, _ := bufio.NewReader(os.Stdin).ReadLine() i, _ := strconv.Atoi(string(input)) nowTrigger := rules[state][i].trigger if nowTrigger == ReadyState { s := fmt.Sprintf("WebSockets Connection State is \"%s\"", state) fmt.Println("\n" + s + "\n") } else if nowTrigger == Message { fmt.Println("\n" + "Receive and Send Messages!!!" + "\n") } tr := rules[state][i] state = tr.state } fmt.Println("\n" + "Closed WebSocket Connection" + "\n") } |
標準入力から入力する値によって、WebSocketの接続状態を変更できるプログラムを実装しました。
実行してみましょう。
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 |
$ go run main.go #####WEB SOCKETS##### Select a trigger: 0 < Call > 1 < Close > 2 < ReadyState > 0 <<< Callを選択 #####WEB SOCKETS##### Select a trigger: 0 < OnHook > 1 < OffHook > 2 < ReadyState > 2 <<< ReadyStateで状態確認 WebSockets Connection State is "Connecting" #####WEB SOCKETS##### Select a trigger: 0 < OnHook > 1 < OffHook > 2 < ReadyState > 0 <<< OnHookを選択 #####WEB SOCKETS##### Select a trigger: 0 < OffHook > 1 < Message > 2 < ReadyState > 2 <<< ReadyStateで状態確認 WebSockets Connection State is "Open" #####WEB SOCKETS##### Select a trigger: 0 < OffHook > 1 < Message > 2 < ReadyState > 0 <<< OffHookを選択 #####WEB SOCKETS##### Select a trigger: 0 < Close > 1 < ReadyState > 0 <<< Closeを確認 Closed WebSocket Connection |
OKですね。入力するトリガーによって、WebSocketの状態が変化していくことがわかると思います。
※Stateの状態をReadyStateで確認できるようにしてありますが、「// fmt.Println(“WebSockets Connection State is”, state)」のコメントを外せば、入力時に状態が把握できるようにしてます。
まとめ
今回のプログラムは、実践向きではないかもしれませんが、結構面白い実装方法ですよね。
Stateを定数で定義し、入力パラメータをトリガーにその状態を変えていく…こんな実装の方法もあるのだと勉強になりました^^
少し混乱するかもしれませんが、ページの最後の方に、全てのソースコードを記載しているので、一つずつ手で入力していくと、わかりやすいかもしれません。
次回
次回は、Stateパターンで数当てゲームを作ってみましょう。
Go言語まとめ
ソースコード
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 |
package main import ( "bufio" "fmt" "os" "strconv" ) type WebSocketState int const ( // ソケット準備 Prepare WebSocketState = iota // ソケットは作成されているが、まだコネクションが開いていない状態 Connecting // コネクションが開き、通信の準備ができている状態 Open // コネクションが閉じる過程にある状態 Closing // コネクションが閉じられたか、もしくは開けていなかった状態 Closed ) func (s WebSocketState) String() string { switch s { case Prepare: return "Prepare" case Connecting: return "Connecting" case Open: return "Open" case Closing: return "Closing" case Closed: return "Closed" } return "Unknown" } type Trigger int const ( // WebSocketオブジェクト作成 Create Trigger = iota // 接続呼び出し Call // 接続確立 OnHook // 接続切断移行 OffHook // 接続切断 Close // 状態確認 ReadyState // メッセージ送受信 Message ) func (t Trigger) String() string { switch t { case Create: return "Create" case Call: return "Call" case OnHook: return "OnHook" case OffHook: return "OffHook" case Close: return "Close" case ReadyState: return "ReadyState" case Message: return "Message" } return "Unknown" } type TriggerRule struct { trigger Trigger state WebSocketState } // Create -> Connecting -> Open -> Closing -> Close var rules = map[WebSocketState][]TriggerRule{ Prepare: { {Call, Connecting}, {Close, Closed}, {ReadyState, Connecting}, }, Connecting: { {OnHook, Open}, {OffHook, Closing}, {ReadyState, Connecting}, }, Open: { {OffHook, Closing}, {Message, Open}, {ReadyState, Open}, }, Closing: { {Close, Closed}, {ReadyState, Closing}, }, Closed: { {Create, Prepare}, {ReadyState, Closed}, }, } func main() { state, exitState := Prepare, Closed // StateがClosedになるまでループ for ok := true; ok; ok = state != exitState { // fmt.Println("WebSockets Connection State is", state) fmt.Println("#####WEB SOCKETS#####") fmt.Println("Select a trigger:") for i := 0; i < len(rules[state]); i++ { tr := rules[state][i] fmt.Println(strconv.Itoa(i), "<", tr.trigger, ">") } // 標準入力からデータを読み込む input, _, _ := bufio.NewReader(os.Stdin).ReadLine() i, _ := strconv.Atoi(string(input)) nowTrigger := rules[state][i].trigger if nowTrigger == ReadyState { s := fmt.Sprintf("WebSockets Connection State is \"%s\"", state) fmt.Println("\n" + s + "\n") } else if nowTrigger == Message { fmt.Println("\n" + "Receive and Send Messages!!!" + "\n") } tr := rules[state][i] state = tr.state } fmt.Println("\n" + "Closed WebSocket Connection" + "\n") } |
コメントを残す
コメントを投稿するにはログインしてください。