diff --git a/state/README.md b/state/README.md new file mode 100644 index 0000000..c86c318 --- /dev/null +++ b/state/README.md @@ -0,0 +1,32 @@ +# State +内部状態が変化したときにオブジェクトの動作を変更できるようにする動作設計パターン + +現在の状態に応じて動作が異なるオブジェクトがあり、状態の数が膨大で、状態固有のコードが頻繁に変更される場合 + +### メリット +- 単一責任の原則。特定の状態に関連するコードを個別のクラスに編成します。 +- オープン/クローズド原則。既存の状態クラスやコンテキストを変更せずに、新しい状態を導入します。 +- かさばるステートマシンの条件を排除することにより、コンテキストのコードを簡素化します。 + +### デメリット +- ステートマシンの状態が少ないか、ほとんど変更されない場合、やりすぎになる可能性 + +### 他パターンとの関係性 +- Bridge、State、Strategy(およびある程度Adapter)の構造は非常に似ているが、他のオブジェクトに作業を委任する構成に基づいてる +- StateはStrategyの延長 + +### 例題 +- 自動販売機 +- 4つの状態があるとする + - hasItem + - noItem + - itemRequested + - hasMoney + +- また4つのアクションがある + - アイテムを選択 + - アイテムを追加 + - お金を挿入する + - ディスペンスアイテム + +- Stateはオブジェクトがさまざまな状態になる可能性があり、に次の状態に切り替わる \ No newline at end of file diff --git a/state/hasItemState.go b/state/hasItemState.go new file mode 100644 index 0000000..af3b0af --- /dev/null +++ b/state/hasItemState.go @@ -0,0 +1,30 @@ +package state + +import "fmt" + +type hasItemState struct { + vendingMachine *vendingMachine +} + +func (i *hasItemState) requestItem() error { + if i.vendingMachine.itemCount == 0 { + i.vendingMachine.setState(i.vendingMachine.noItem) + return fmt.Errorf("No item present") + } + fmt.Printf("Item requestd\n") + i.vendingMachine.setState(i.vendingMachine.itemRequested) + return nil +} + +func (i *hasItemState) addItem(count int) error { + fmt.Printf("%d items added\n", count) + i.vendingMachine.incrementItemCount(count) + return nil +} + +func (i *hasItemState) insertMoney(money int) error { + return fmt.Errorf("Please select item first") +} +func (i *hasItemState) dispenseItem() error { + return fmt.Errorf("Please select item first") +} diff --git a/state/hasMoneyState.go b/state/hasMoneyState.go new file mode 100644 index 0000000..867a784 --- /dev/null +++ b/state/hasMoneyState.go @@ -0,0 +1,29 @@ +package state + +import "fmt" + +type hasMoneyState struct { + vendingMachine *vendingMachine +} + +func (i *hasMoneyState) requestItem() error { + return fmt.Errorf("Item dispense in progress") +} + +func (i *hasMoneyState) addItem(count int) error { + return fmt.Errorf("Item dispense in progress") +} + +func (i *hasMoneyState) insertMoney(money int) error { + return fmt.Errorf("Item out of stock") +} +func (i *hasMoneyState) dispenseItem() error { + fmt.Println("Dispensing Item") + i.vendingMachine.itemCount = i.vendingMachine.itemCount - 1 + if i.vendingMachine.itemCount == 0 { + i.vendingMachine.setState(i.vendingMachine.noItem) + } else { + i.vendingMachine.setState(i.vendingMachine.hasItem) + } + return nil +} diff --git a/state/itemRequestedState.go b/state/itemRequestedState.go new file mode 100644 index 0000000..c07c5be --- /dev/null +++ b/state/itemRequestedState.go @@ -0,0 +1,27 @@ +package state + +import "fmt" + +type itemRequestedState struct { + vendingMachine *vendingMachine +} + +func (i *itemRequestedState) requestItem() error { + return fmt.Errorf("Item already requested") +} + +func (i *itemRequestedState) addItem(count int) error { + return fmt.Errorf("Item Dispense in progress") +} + +func (i *itemRequestedState) insertMoney(money int) error { + if money < i.vendingMachine.itemPrice { + fmt.Errorf("Inserted money is less. Please insert %d", i.vendingMachine.itemPrice) + } + fmt.Println("Money entered is ok") + i.vendingMachine.setState(i.vendingMachine.hasMoney) + return nil +} +func (i *itemRequestedState) dispenseItem() error { + return fmt.Errorf("Please insert money first") +} diff --git a/state/noItemState.go b/state/noItemState.go new file mode 100644 index 0000000..f6989d8 --- /dev/null +++ b/state/noItemState.go @@ -0,0 +1,24 @@ +package state + +import "fmt" + +type noItemState struct { + vendingMachine *vendingMachine +} + +func (i *noItemState) requestItem() error { + return fmt.Errorf("Item out of stock") +} + +func (i *noItemState) addItem(count int) error { + i.vendingMachine.incrementItemCount(count) + i.vendingMachine.setState(i.vendingMachine.hasItem) + return nil +} + +func (i *noItemState) insertMoney(money int) error { + return fmt.Errorf("Item out of stock") +} +func (i *noItemState) dispenseItem() error { + return fmt.Errorf("Item out of stock") +} diff --git a/state/state.go b/state/state.go new file mode 100644 index 0000000..c1ddafb --- /dev/null +++ b/state/state.go @@ -0,0 +1,56 @@ +package state + +import ( + "fmt" + "log" +) + +type state interface { + addItem(int) error + requestItem() error + insertMoney(money int) error + dispenseItem() error +} + +func main() { + vendingMachine := newVendingMachine(1, 10) + + err := vendingMachine.requestItem() + if err != nil { + log.Fatalf(err.Error()) + } + + err = vendingMachine.insertMoney(10) + if err != nil { + log.Fatalf(err.Error()) + } + + err = vendingMachine.dispenseItem() + if err != nil { + log.Fatalf(err.Error()) + } + + fmt.Println() + + err = vendingMachine.addItem(2) + if err != nil { + log.Fatalf(err.Error()) + } + + fmt.Println() + + err = vendingMachine.requestItem() + if err != nil { + log.Fatalf(err.Error()) + } + + err = vendingMachine.insertMoney(10) + if err != nil { + log.Fatalf(err.Error()) + } + + err = vendingMachine.dispenseItem() + if err != nil { + log.Fatalf(err.Error()) + } +} diff --git a/state/state_test.go b/state/state_test.go new file mode 100644 index 0000000..16b0642 --- /dev/null +++ b/state/state_test.go @@ -0,0 +1,7 @@ +package state + +import "testing" + +func TestState(t *testing.T) { + main() +} diff --git a/state/vendingMachine.go b/state/vendingMachine.go new file mode 100644 index 0000000..d5d03a4 --- /dev/null +++ b/state/vendingMachine.go @@ -0,0 +1,66 @@ +package state + +import "fmt" + +type vendingMachine struct { + hasItem state + itemRequested state + hasMoney state + noItem state + + currentState state + + itemCount int + itemPrice int +} + +func newVendingMachine(itemCount, itemPrice int) *vendingMachine { + v := &vendingMachine{ + itemCount: itemCount, + itemPrice: itemPrice, + } + hasItemState := &hasItemState{ + vendingMachine: v, + } + itemRequestedState := &itemRequestedState{ + vendingMachine: v, + } + hasMoneyState := &hasMoneyState{ + vendingMachine: v, + } + noItemState := &noItemState{ + vendingMachine: v, + } + + v.setState(hasItemState) + v.hasItem = hasItemState + v.itemRequested = itemRequestedState + v.hasMoney = hasMoneyState + v.noItem = noItemState + return v +} + +func (v *vendingMachine) requestItem() error { + return v.currentState.requestItem() +} + +func (v *vendingMachine) addItem(count int) error { + return v.currentState.addItem(count) +} + +func (v *vendingMachine) insertMoney(money int) error { + return v.currentState.insertMoney(money) +} + +func (v *vendingMachine) dispenseItem() error { + return v.currentState.dispenseItem() +} + +func (v *vendingMachine) setState(s state) { + v.currentState = s +} + +func (v *vendingMachine) incrementItemCount(count int) { + fmt.Printf("Adding %d items\n", count) + v.itemCount = v.itemCount + count +}