반응형
SMALL
인터프리터(Interpreter)
인터프리터 디자인 패턴은 실제로 인터프리터 패턴은 일반적인 작업을 수행하기 위한 언어를 갖는 것이 유용한 비즈니스 사례를 해결하는 데 널리 사용됩니다.
package interpreter
import (
"strconv"
"strings"
)
const (
SUM = "sum"
SUB = "sub"
MUL = "mul"
DIV = "div"
)
type polishNotationStack []int
func (p *polishNotationStack) Push(s int) {
*p = append(*p, s)
}
func (p *polishNotationStack) Pop() int {
length := len(*p)
if length > 0 {
temp := (*p)[length-1]
*p = (*p)[:length-1]
return temp
}
return 0
}
func Calculate(o string) (int, error) {
stack := polishNotationStack{}
operators := strings.Split(o, " ")
for _, operatorString := range operators {
if isOperator(operatorString) {
right := stack.Pop()
left := stack.Pop()
mathFunc := getOperationFunc(operatorString)
res := mathFunc(left, right)
stack.Push(res)
} else {
val, err := strconv.Atoi(operatorString)
if err != nil {
return 0, err
}
stack.Push(val)
}
}
return int(stack.Pop()), nil
}
func isOperator(o string) bool {
if o == SUM || o == SUB || o == MUL || o == DIV {
return true
}
return false
}
func getOperationFunc(o string) func(a, b int) int {
switch o {
case SUM:
return func(a, b int) int {
return a + b
}
case SUB:
return func(a, b int) int {
return a - b
}
case MUL:
return func(a, b int) int {
return a * b
}
case DIV:
return func(a, b int) int {
return a / b
}
}
return nil
}
Test Code
package interpreter
import "testing"
func TestCalculate(t *testing.T) {
tempOperation := "3 4 sum 2 sub"
res, err := Calculate(tempOperation)
if err != nil {
t.Error(err)
}
if res != 5 {
t.Errorf("Expected result not found: %d != %d\n", 5, res)
}
tempOperation = "5 3 sub 8 mul 4 sum 5 div"
res, err = Calculate(tempOperation)
if err != nil {
t.Error(err)
}
if res != 4 {
t.Errorf("Expected result not found: %d != %d\n", 4, res)
}
}
방문자(Visitor)
방문자 디자인 패턴은 개체 유형의 일부 논리를 개체에 작업을 수행하기 위해 방문하는 방문자라는 외부 유형에 위임합니다.
package visitor
import (
"fmt"
"io"
"os"
)
type MessageA struct {
Msg string
Output io.Writer
}
func (m *MessageA) Accept(v Visitor) {
v.VisitA(m)
}
func (m *MessageA) Print() {
if m.Output == nil {
m.Output = os.Stdout
}
fmt.Fprintf(m.Output, "A: %s", m.Msg)
}
type MessageB struct {
Msg string
Output io.Writer
}
func (m *MessageB) Accept(v Visitor) {
v.VisitB(m)
}
func (m *MessageB) Print() {
if m.Output == nil {
m.Output = os.Stdout
}
fmt.Fprintf(m.Output, "B: %s", m.Msg)
}
type Visitor interface {
VisitA(*MessageA)
VisitB(*MessageB)
}
type Visitable interface {
Accept(Visitor)
}
type MessageVisitor struct{}
func (mf *MessageVisitor) VisitA(m *MessageA) {
m.Msg = fmt.Sprintf("%s %s", m.Msg, "(Visited A)")
}
func (mf *MessageVisitor) VisitB(m *MessageB) {
m.Msg = fmt.Sprintf("%s %s", m.Msg, "(Visited B)")
}
Test Code
package visitor
import "testing"
type TestHelper struct {
Received string
}
func (t *TestHelper) Write(p []byte) (int, error) {
t.Received = string(p)
return len(p), nil
}
func Test_Overall(t *testing.T) {
testHelper := &TestHelper{}
visitor := &MessageVisitor{}
t.Run("MessageA test", func(t *testing.T) {
msg := MessageA{
Msg: "Hello World",
Output: testHelper,
}
msg.Accept(visitor)
msg.Print()
expected := "A: Hello World (Visited A)"
if testHelper.Received != expected {
t.Errorf("Expected result was incorrect. %s != %s",
testHelper.Received, expected)
}
})
t.Run("MessageB test", func(t *testing.T) {
msg := MessageB{
Msg: "Hello World",
Output: testHelper,
}
msg.Accept(visitor)
msg.Print()
expected := "B: Hello World (Visited B)"
if testHelper.Received != expected {
t.Errorf("Expected result was incorrect. %s != %s",
testHelper.Received, expected)
}
})
}
상태(State)
상태 디자인 패턴은 FSM(Finite State Machines)과 직접적으로 관련이 있습니다. 매우 간단한 용어로 FSM은 하나 이상의 상태를 가지고 있으며 일부 동작을 실행하기 위해 그 사이를 이동하는 것입니다. 상태 패턴이 FSM을 정의하는 데 어떻게 도움이 되는지 알아보겠습니다.
package main
import (
"fmt"
"math/rand"
"os"
"time"
)
type GameState interface {
executeState(*GameContext) bool
}
type GameContext struct {
SecretNumber int
Retries int
Won bool
Next GameState
}
type StartState struct{}
func (s *StartState) executeState(c *GameContext) bool {
c.Next = &AskState{}
rand.Seed(time.Now().UnixNano())
c.SecretNumber = rand.Intn(10)
fmt.Println("Introduce a number a number of retries to set the difficulty:")
fmt.Fscanf(os.Stdin, "%d\n", &c.Retries)
return true
}
type AskState struct{}
func (a *AskState) executeState(c *GameContext) bool {
fmt.Printf("Introduce a number between 0 and 10, you have %d tries left\n", c.Retries)
var n int
fmt.Fscanf(os.Stdin, "%d", &n)
c.Retries = c.Retries - 1
if n == c.SecretNumber {
c.Won = true
c.Next = &FinishState{}
}
if c.Retries == 0 {
c.Next = &FinishState{}
}
return true
}
type FinishState struct{}
func (f *FinishState) executeState(c *GameContext) bool {
if c.Won {
c.Next = &WinState{}
} else {
c.Next = &LoseState{}
}
return true
}
type WinState struct{}
func (w *WinState) executeState(c *GameContext) bool {
fmt.Println("Congrats, you won")
return false
}
type LoseState struct{}
func (l *LoseState) executeState(c *GameContext) bool {
fmt.Printf("You lose. The correct number was: %d\n", c.SecretNumber)
return false
}
func main() {
start := StartState{}
game := GameContext{
Next: &start,
}
for game.Next.executeState(&game) {
}
}
반응형
LIST