Skip to content

Commit

Permalink
add state
Browse files Browse the repository at this point in the history
  • Loading branch information
sioncojp committed Nov 10, 2021
1 parent e5315a8 commit e83d21c
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 0 deletions.
32 changes: 32 additions & 0 deletions state/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# State
内部状態が変化したときにオブジェクトの動作を変更できるようにする動作設計パターン

現在の状態に応じて動作が異なるオブジェクトがあり、状態の数が膨大で、状態固有のコードが頻繁に変更される場合

### メリット
- 単一責任の原則。特定の状態に関連するコードを個別のクラスに編成します。
- オープン/クローズド原則。既存の状態クラスやコンテキストを変更せずに、新しい状態を導入します。
- かさばるステートマシンの条件を排除することにより、コンテキストのコードを簡素化します。

### デメリット
- ステートマシンの状態が少ないか、ほとんど変更されない場合、やりすぎになる可能性

### 他パターンとの関係性
- Bridge、State、Strategy(およびある程度Adapter)の構造は非常に似ているが、他のオブジェクトに作業を委任する構成に基づいてる
- StateはStrategyの延長

### 例題
- 自動販売機
- 4つの状態があるとする
- hasItem
- noItem
- itemRequested
- hasMoney

- また4つのアクションがある
- アイテムを選択
- アイテムを追加
- お金を挿入する
- ディスペンスアイテム

- Stateはオブジェクトがさまざまな状態になる可能性があり、に次の状態に切り替わる
30 changes: 30 additions & 0 deletions state/hasItemState.go
Original file line number Diff line number Diff line change
@@ -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")
}
29 changes: 29 additions & 0 deletions state/hasMoneyState.go
Original file line number Diff line number Diff line change
@@ -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
}
27 changes: 27 additions & 0 deletions state/itemRequestedState.go
Original file line number Diff line number Diff line change
@@ -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")
}
24 changes: 24 additions & 0 deletions state/noItemState.go
Original file line number Diff line number Diff line change
@@ -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")
}
56 changes: 56 additions & 0 deletions state/state.go
Original file line number Diff line number Diff line change
@@ -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())
}
}
7 changes: 7 additions & 0 deletions state/state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package state

import "testing"

func TestState(t *testing.T) {
main()
}
66 changes: 66 additions & 0 deletions state/vendingMachine.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit e83d21c

Please sign in to comment.