반응형
SMALL
중재인(Mediator)
중재인 디자인 패턴은 이름에서 알 수 있듯이, 정보를 교환하기 위해 두 가지 유형 사이에 있는 패턴입니다.
package main
import "fmt"
type One struct{}
type Two struct{}
type Three struct{}
type Four struct{}
func Sum(a, b interface{}) interface{} {
switch a := a.(type) {
case One:
switch b := b.(type) {
case One:
return &Two{}
case Two:
return &Three{}
case int:
return b + 1
default:
return fmt.Errorf("number not found")
}
case Two:
switch b.(type) {
case One:
return &Three{}
case Two:
return &Four{}
default:
return fmt.Errorf("number not found")
}
case int:
switch b := b.(type) {
case One:
return &Three{}
case Two:
return &Four{}
case int:
return a + b
default:
return fmt.Errorf("number not found")
}
default:
return fmt.Errorf("number not found")
}
}
func main() {
fmt.Printf("%#v\n", Sum(One{}, Two{}))
fmt.Printf("%d\n", Sum(1, 2))
fmt.Printf("%d\n", Sum(One{}, 2))
}
관찰자(Observer)
관찰자 디자인 패턴은 게시/구독자 또는 게시/청취자라고도 합니다. 상태 패턴을 사용하여 최초의 이벤트 중심 아키텍처를 정의했지만, 관찰자 패턴을 사용하면 새로운 수준의 추상화에 도달할 수 있습니다.
package observer
import "fmt"
type Observer interface {
Notify(string)
}
type Publisher struct {
ObserversList []Observer
}
func (s *Publisher) AddObserver(o Observer) {
s.ObserversList = append(s.ObserversList, o)
}
func (s *Publisher) RemoveObserver(o Observer) {
var indexToRemove int
for i, observer := range s.ObserversList {
if observer == o {
indexToRemove = i
break
}
}
s.ObserversList = append(s.ObserversList[:indexToRemove], s.ObserversList[indexToRemove+1:]...)
}
func (s *Publisher) NotifyObservers(m string) {
fmt.Printf("Publisher received message '%s' to notify observers\n", m)
for _, observer := range s.ObserversList {
observer.Notify(m)
}
}
Test Code
package observer
import (
"fmt"
"testing"
)
type TestObserver struct {
ID int
Message string
}
func (p *TestObserver) Notify(m string) {
fmt.Printf("Observer %d: message '%s' received \n", p.ID, m)
p.Message = m
}
func TestSubject(t *testing.T) {
testObserver1 := &TestObserver{1, ""}
testObserver2 := &TestObserver{2, ""}
testObserver3 := &TestObserver{3, ""}
publisher := Publisher{}
t.Run("AddObserver", func(t *testing.T) {
publisher.AddObserver(testObserver1)
publisher.AddObserver(testObserver2)
publisher.AddObserver(testObserver3)
if len(publisher.ObserversList) != 3 {
t.Fail()
}
})
t.Run("RemoveObserver", func(t *testing.T) {
publisher.RemoveObserver(testObserver2)
if len(publisher.ObserversList) != 2 {
t.Errorf("The size of the observer list is not the "+
"expected. 3 != %d\n", len(publisher.ObserversList))
}
for _, observer := range publisher.ObserversList {
testObserver, ok := observer.(*TestObserver)
if !ok {
t.Fail()
}
if testObserver.ID == 2 {
t.Fail()
}
}
})
t.Run("Notify", func(t *testing.T) {
for _, observer := range publisher.ObserversList {
printObserver, ok := observer.(*TestObserver)
if !ok {
t.Fail()
break
}
if printObserver.Message != "" {
t.Errorf("The observer's Message field weren't "+" empty: %s\n",
printObserver.Message)
}
}
message := "Hello World!"
publisher.NotifyObservers(message)
for _, observer := range publisher.ObserversList {
printObserver, ok := observer.(*TestObserver)
if !ok {
t.Fail()
break
}
if printObserver.Message != message {
t.Errorf("Expected message on observer %d was "+
"not expected: '%s' != '%s'\n", printObserver.ID,
printObserver.Message, message)
}
}
})
if len(publisher.ObserversList) == 0 {
t.Errorf("The list is empty. Nothing to test\n")
}
}
장벽(Barrier)
장벽 디자인 패턴은 매우 일반적인 패턴입니다. 특히 프로그램을 계속하기 전에 서로 다른 고루틴의 응답을 두 번 이상 기다려야 할 때 그렇습니다.
package barrier
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
)
var timeoutMilliseconds int = 5000
type barrierResp struct {
Err error
Resp string
}
func barrier(endpoints ...string) {
requestNumber := len(endpoints)
in := make(chan barrierResp, requestNumber)
defer close(in)
responses := make([]barrierResp, requestNumber)
for _, endpoint := range endpoints {
go makeRequest(in, endpoint)
}
var hasError bool
for i := 0; i < requestNumber; i++ {
resp := <-in
if resp.Err != nil {
fmt.Println("ERROR: ", resp.Err)
hasError = true
}
responses[i] = resp
}
if !hasError {
for _, resp := range responses {
fmt.Println(resp.Resp)
}
}
}
func makeRequest(out chan<- barrierResp, url string) {
res := barrierResp{}
client := http.Client{
Timeout: time.Duration(time.Duration(timeoutMilliseconds) *
time.Millisecond),
}
resp, err := client.Get(url)
if err != nil {
res.Err = err
out <- res
return
}
byt, err := ioutil.ReadAll(resp.Body)
if err != nil {
res.Err = err
out <- res
return
}
res.Resp = string(byt)
out <- res
}
func captureBarrierOutput(endpoints ...string) string {
reader, writer, _ := os.Pipe()
os.Stdout = writer
out := make(chan string)
go func() {
var buf bytes.Buffer
io.Copy(&buf, reader)
out <- buf.String()
}()
barrier(endpoints...)
writer.Close()
temp := <-out
return temp
}
Test Code
package barrier
import (
"strings"
"testing"
)
func TestBarrier(t *testing.T) {
t.Run("Correct endpoints", func(t *testing.T) {
endpoints := []string{"http://httpbin.org/headers", "http://httpbin.org/User-Agent"}
result := captureBarrierOutput(endpoints...)
if !strings.Contains(result, "Accept-Encoding") || !strings.Contains(result, "User-Agent") {
t.Fail()
}
t.Log(result)
})
t.Run("One endpoint incorrect", func(t *testing.T) {
endpoints := []string{"http://malformed-url", "http://httpbin.org/User-Agent"}
result := captureBarrierOutput(endpoints...)
if !strings.Contains(result, "ERROR") {
t.Fail()
}
t.Log(result)
})
t.Run("Very short timeout", func(t *testing.T) {
endpoints := []string{"http://httpbin.org/headers", "http://httpbin.org/User-Agent"}
timeoutMilliseconds = 1
result := captureBarrierOutput(endpoints...)
if !strings.Contains(result, "Timeout") {
t.Fail()
}
t.Log(result)
})
}
반응형
LIST
'Go' 카테고리의 다른 글
[Algorithms] 탐색(Search) (0) | 2022.12.06 |
---|---|
[Design Pattern] 미래(Future), 파이프라인(Pipeline), 워커 풀(Worker Pool) 패턴 (0) | 2022.12.01 |
[Design Pattern] 인터프리터(Interpreter), 방문자(Visitor), 상태(State) 패턴 (0) | 2022.11.30 |
[Design Pattern] 커맨드(Command), 템플릿(Template), 메멘토(Memento) 패턴 (0) | 2022.11.29 |
[Design Pattern] 플라이웨이트(Flyweight), 전략(Strategy), 역할 사슬(Chain of Responsibility) 패턴 (0) | 2022.11.25 |