├── future ├── future.jpg ├── future.go └── future_test.go ├── memento ├── State.go ├── with_command_facade │ ├── memento.go │ ├── originator.go │ ├── with_command_facade.go │ ├── memento_facade.go │ └── careTaker.go ├── memento.go ├── originator.go ├── careTaker.go └── memento_test.go ├── abstract_factory ├── car.go ├── motorbike.go ├── vihicle.go ├── family_car.go ├── luxury_car.go ├── sport_motorbike.go ├── cruise_motorbike.go ├── abstract_factory_test.go └── vehicle_factory.go ├── strategy ├── strategy.jpg ├── example2 │ └── output_strategy.go └── example1 │ └── main.go ├── barrier_concurrency ├── image.jpg └── barrier_test.go ├── state ├── state_test.go ├── GameContext.go ├── FinishState.go ├── StateState.go └── AskState.go ├── singleton ├── singleton_test.go └── singleton.go ├── facade ├── facade_test.go └── facade.go ├── object_pool └── object_pool.go ├── worker_pool2 ├── simple_worker_pool_test.go └── simple_worker_pool.go ├── go.mod ├── adapter ├── adapter_test.go └── adapter.go ├── worker_pool ├── request.go ├── dispatcher.go ├── worker_pool_test.go └── worker.go ├── command ├── command.go ├── command_example2.go └── command_test.go ├── or_done └── or_done.go ├── decorator ├── middleware.go ├── decorator.go └── decorator_test.go ├── factory_method ├── factory_method_test.go └── factory_method.go ├── pipeline ├── pipeline_test.go └── pipeline.go ├── observer ├── observer.go └── observer_test.go ├── tee └── tee.go ├── composite └── composite.go ├── prototype ├── prototype_test.go └── prototype.go ├── visitor ├── visitor.go ├── visitor_test.go └── another_example │ └── productInfoRetriever.go ├── template ├── template_test.go └── template.go ├── proxy ├── proxy.go └── proxy_test.go ├── chain_responsibility ├── chain_responsibility_test.go └── chain_responsibility.go ├── flyweight └── flyweight.go ├── .gitignore ├── bridge ├── bridge.go └── bridge_test.go └── README.md /future/future.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobenpark/go-design-pattern/HEAD/future/future.jpg -------------------------------------------------------------------------------- /memento/State.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | type State struct { 4 | Description string 5 | } 6 | -------------------------------------------------------------------------------- /abstract_factory/car.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type Car interface { 4 | NumDoors() int 5 | } 6 | -------------------------------------------------------------------------------- /strategy/strategy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobenpark/go-design-pattern/HEAD/strategy/strategy.jpg -------------------------------------------------------------------------------- /barrier_concurrency/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobenpark/go-design-pattern/HEAD/barrier_concurrency/image.jpg -------------------------------------------------------------------------------- /abstract_factory/motorbike.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type Motorbike interface { 4 | GetMotorbikeType() int 5 | } 6 | -------------------------------------------------------------------------------- /memento/with_command_facade/memento.go: -------------------------------------------------------------------------------- 1 | package with_command_facade 2 | 3 | type Memento struct { 4 | memento Command 5 | } 6 | -------------------------------------------------------------------------------- /abstract_factory/vihicle.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type Vehicle interface { 4 | NumWheels() int 5 | NumSeats() int 6 | } 7 | -------------------------------------------------------------------------------- /memento/memento.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | //객체의 상태 정보를 저장 및 복원하는 패턴 4 | 5 | //Memento: state를 저장하는 유형 6 | type memento struct { 7 | state State 8 | } 9 | -------------------------------------------------------------------------------- /state/state_test.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import "testing" 4 | 5 | func TestState(t *testing.T) { 6 | start := StartState{} 7 | game := GameContext{ 8 | Next: &start, 9 | } 10 | for game.Next.executeState(&game) { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /state/GameContext.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | type GameState interface { 4 | executeState(*GameContext) bool 5 | } 6 | 7 | type GameContext struct { 8 | SecretNumber int 9 | Retires int 10 | Won bool 11 | Next GameState 12 | } 13 | -------------------------------------------------------------------------------- /singleton/singleton_test.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | func TestSingleTon(t *testing.T) { 9 | s := GetInstance() 10 | if s.AddOne() != 1 { 11 | t.Error(errors.New("s count is not 1")) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /abstract_factory/family_car.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type FamilyCar struct{} 4 | 5 | func (*FamilyCar) NumWheels() int { 6 | return 4 7 | } 8 | 9 | func (*FamilyCar) NumSeats() int { 10 | return 5 11 | } 12 | 13 | func (*FamilyCar) NumDoors() int { 14 | return 5 15 | } 16 | -------------------------------------------------------------------------------- /abstract_factory/luxury_car.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type LuxuryCar struct{} 4 | 5 | func (*LuxuryCar) NumDoors() int { 6 | return 4 7 | } 8 | 9 | func (*LuxuryCar) NumWheels() int { 10 | return 4 11 | } 12 | 13 | func (*LuxuryCar) NumSeats() int { 14 | return 5 15 | } 16 | -------------------------------------------------------------------------------- /state/FinishState.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import "fmt" 4 | 5 | type FinishState struct{} 6 | 7 | func (f *FinishState) executeState(c *GameContext) bool { 8 | if c.Won { 9 | fmt.Println("congrates, you won") 10 | } else { 11 | fmt.Println("You lose") 12 | } 13 | return false 14 | } 15 | -------------------------------------------------------------------------------- /memento/with_command_facade/originator.go: -------------------------------------------------------------------------------- 1 | package with_command_facade 2 | 3 | type originator struct { 4 | Command Command 5 | } 6 | 7 | func (o *originator) NewMemento() Memento { 8 | return Memento{o.Command} 9 | } 10 | 11 | func (o *originator) ExtractAndStoreCommand(m Memento) { 12 | o.Command = m.memento 13 | } 14 | -------------------------------------------------------------------------------- /memento/originator.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | //Originator: Memento를 만들고 현재 활성 state를 저장 4 | type originator struct { 5 | state State 6 | } 7 | 8 | func (o *originator) NewMemento() memento { 9 | return memento{o.state} 10 | } 11 | 12 | func (o *originator) ExtractAndStoreState(m memento) { 13 | o.state = m.state 14 | } 15 | -------------------------------------------------------------------------------- /abstract_factory/sport_motorbike.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type SportMotorbike struct{} 4 | 5 | func (s *SportMotorbike) NumWheels() int { 6 | return 2 7 | } 8 | func (s *SportMotorbike) NumSeats() int { 9 | return 1 10 | } 11 | func (s *SportMotorbike) GetMotorbikeType() int { 12 | return SportMotorbikeType 13 | } 14 | -------------------------------------------------------------------------------- /abstract_factory/cruise_motorbike.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | type CruiseMotorbike struct{} 4 | 5 | func (c *CruiseMotorbike) NumWheels() int { 6 | return 2 7 | } 8 | func (c *CruiseMotorbike) NumSeats() int { 9 | return 2 10 | } 11 | func (c *CruiseMotorbike) GetMotorbikeType() int { 12 | return CruiseMotorbikeType 13 | } 14 | -------------------------------------------------------------------------------- /memento/with_command_facade/with_command_facade.go: -------------------------------------------------------------------------------- 1 | package with_command_facade 2 | 3 | type Command interface { 4 | GetValue() interface{} 5 | } 6 | 7 | type Volume byte 8 | 9 | func (v Volume) GetValue() interface{} { 10 | return v 11 | } 12 | 13 | type Mute bool 14 | 15 | func (m Mute) GetValue() interface{} { 16 | return m 17 | } 18 | -------------------------------------------------------------------------------- /singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | type Singleton interface { 4 | AddOne() int 5 | } 6 | 7 | type singleton struct { 8 | count int 9 | } 10 | 11 | var instance *singleton 12 | 13 | func GetInstance() Singleton { 14 | if instance == nil { 15 | instance = new(singleton) 16 | } 17 | return instance 18 | } 19 | 20 | func (s *singleton) AddOne() int { 21 | s.count++ 22 | return s.count 23 | } 24 | -------------------------------------------------------------------------------- /facade/facade_test.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import "testing" 4 | 5 | func TestOpenWeatherMap_responseParser(t *testing.T) { 6 | r := getMockData() 7 | openWeatherMap := CurrentWeatherData{APIkey: ""} 8 | weather, err := openWeatherMap.responseParser(r) 9 | if err != nil { 10 | t.Fatal(err) 11 | } 12 | 13 | if weather.ID != 3117735 { 14 | t.Errorf("Madrid id is 3117735, not %d\n", weather.ID) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /object_pool/object_pool.go: -------------------------------------------------------------------------------- 1 | package object_pool 2 | 3 | type Messenger struct{} 4 | 5 | func (Messenger) Do() {} 6 | 7 | type Pool chan *Messenger 8 | 9 | func New(total int) *Pool { 10 | p := make(Pool, total) 11 | for i := 0; i < total; i++ { 12 | p <- new(Messenger) 13 | } 14 | return &p 15 | } 16 | 17 | func Start() { 18 | p := New(4) 19 | 20 | select { 21 | case obj := <-*p: 22 | obj.Do() 23 | *p <- obj 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /strategy/example2/output_strategy.go: -------------------------------------------------------------------------------- 1 | package example2 2 | 3 | import "io" 4 | 5 | type Output interface { 6 | Draw() error 7 | SetLog(io.Writer) 8 | SetWriter(io.Writer) 9 | } 10 | 11 | type DrawOutput struct { 12 | Writer io.Writer 13 | LogWriter io.Writer 14 | } 15 | 16 | func (d *DrawOutput) SetLog(w io.Writer) { 17 | d.LogWriter = w 18 | } 19 | 20 | func (d *DrawOutput) SetWriter(w io.Writer) { 21 | d.Writer = w 22 | } 23 | -------------------------------------------------------------------------------- /memento/with_command_facade/memento_facade.go: -------------------------------------------------------------------------------- 1 | package with_command_facade 2 | 3 | type MementoFacade struct { 4 | originator originator 5 | careTaker careTaker 6 | } 7 | 8 | func (m *MementoFacade) SaveSettings(s Command) { 9 | m.originator.Command = s 10 | m.careTaker.Add(m.originator.NewMemento()) 11 | } 12 | 13 | func (m *MementoFacade) RestoreSettings(i int) Command { 14 | m.originator.ExtractAndStoreCommand(m.careTaker.Pop()) 15 | return m.originator.Command 16 | } 17 | -------------------------------------------------------------------------------- /state/StateState.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "time" 8 | ) 9 | 10 | type StartState struct{} 11 | 12 | func (s *StartState) executeState(c *GameContext) bool { 13 | c.Next = &AskState{} 14 | 15 | rand.Seed(time.Now().UnixNano()) 16 | c.SecretNumber = rand.Intn(10) 17 | 18 | fmt.Println("Introduce a number a number of retries to set the difficulty") 19 | fmt.Fscanf(os.Stdin, "%d\n", &c.Retires) 20 | 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /worker_pool2/simple_worker_pool_test.go: -------------------------------------------------------------------------------- 1 | package worker_pool2 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRunningWorker(t *testing.T) { 8 | 9 | w := &WorkerPool{ 10 | job: make(chan func() string, 100), 11 | result: make(chan string, 100), 12 | } 13 | 14 | w.Start(4) 15 | 16 | for i := 0; i < 100; i++ { 17 | w.job <- func() string { 18 | return "가즈아" 19 | } 20 | } 21 | 22 | close(w.job) 23 | 24 | for i := 0; i < 100; i++ { 25 | <-w.result 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /memento/with_command_facade/careTaker.go: -------------------------------------------------------------------------------- 1 | package with_command_facade 2 | 3 | type careTaker struct { 4 | mementoList []Memento 5 | } 6 | 7 | func (c *careTaker) Add(m Memento) { 8 | c.mementoList = append(c.mementoList, m) 9 | } 10 | 11 | func (c *careTaker) Pop() Memento { 12 | if len(c.mementoList) > 0 { 13 | tempMemento := c.mementoList[len(c.mementoList)-1] 14 | c.mementoList = c.mementoList[0 : len(c.mementoList)-1] 15 | return tempMemento 16 | } 17 | 18 | return Memento{} 19 | } 20 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go-design-pattern 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/labstack/gommon v0.2.8 7 | github.com/mattn/go-colorable v0.1.0 // indirect 8 | github.com/mattn/go-isatty v0.0.4 // indirect 9 | github.com/pkg/errors v0.8.1 10 | github.com/stretchr/testify v1.3.0 // indirect 11 | github.com/valyala/bytebufferpool v1.0.0 // indirect 12 | github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect 13 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /memento/careTaker.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | import "fmt" 4 | 5 | //Care Taker: 메멘토를 저장하고 디비에 저장할건지 지정된 수를 초과해서 저장하지 않을 건지에 대한 로직을 갖는다 6 | type careTaker struct { 7 | mementoList []memento 8 | } 9 | 10 | func (c *careTaker) Add(m memento) { 11 | c.mementoList = append(c.mementoList, m) 12 | } 13 | 14 | func (c *careTaker) Memento(i int) (memento, error) { 15 | 16 | if len(c.mementoList) < i || i < 0 { 17 | return memento{}, fmt.Errorf("Index net found\n") 18 | } 19 | return c.mementoList[i], nil 20 | } 21 | -------------------------------------------------------------------------------- /worker_pool2/simple_worker_pool.go: -------------------------------------------------------------------------------- 1 | package worker_pool2 2 | 3 | import "fmt" 4 | 5 | func RunningWorker(workerNum int, jobs <-chan func() string, result chan<- string) { 6 | for job := range jobs { 7 | fmt.Printf("%d 디스패치\n", workerNum) 8 | result <- job() 9 | } 10 | } 11 | 12 | type WorkerPool struct { 13 | job chan func() string 14 | result chan string 15 | } 16 | 17 | func (w *WorkerPool) Start(count int) { 18 | for i := 0; i < count; i++ { 19 | go RunningWorker(i, w.job, w.result) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /adapter/adapter_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestAdapter(t *testing.T) { 6 | msg := "Hello World!" 7 | 8 | adapter := PrinterAdapter{ 9 | OldPrinter: &MyLegacyPrinter{}, 10 | Msg: msg, 11 | } 12 | 13 | //adapter := PrinterAdapter{ 14 | // OldPrinter: nil, 15 | // Msg: msg, 16 | //} 17 | returnedMsg := adapter.PrintStored() 18 | 19 | if returnedMsg != "Legacy Printer: Adapter: Hello World!\n" { 20 | t.Errorf("Message didn't match: %s\n", returnedMsg) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /state/AskState.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | type AskState struct{} 9 | 10 | func (a *AskState) executeState(c *GameContext) bool { 11 | fmt.Printf("Introduce a number between 0 and 10, you have %d tries left \n", c.Retires) 12 | 13 | var n int 14 | fmt.Fscanf(os.Stdin, "%d", &n) 15 | c.Retires = c.Retires - 1 16 | 17 | if n == c.SecretNumber { 18 | c.Won = true 19 | c.Next = &FinishState{} 20 | } 21 | 22 | if c.Retires == 0 { 23 | c.Next = &FinishState{} 24 | } 25 | 26 | return true 27 | } 28 | -------------------------------------------------------------------------------- /worker_pool/request.go: -------------------------------------------------------------------------------- 1 | package worker_pool 2 | 3 | import ( 4 | "fmt" 5 | "github.com/labstack/gommon/log" 6 | "sync" 7 | ) 8 | 9 | type RequestHandler func(interface{}) 10 | 11 | type Request struct { 12 | Data interface{} 13 | Handler RequestHandler 14 | } 15 | 16 | func NewStringRequest(s string, wg *sync.WaitGroup) Request { 17 | myRequest := Request{ 18 | Data: s, 19 | Handler: func(i interface{}) { 20 | defer wg.Done() 21 | s, ok := i.(string) 22 | if !ok { 23 | log.Fatal("Invalid casting to string") 24 | } 25 | fmt.Println(s) 26 | }, 27 | } 28 | return myRequest 29 | } 30 | -------------------------------------------------------------------------------- /worker_pool/dispatcher.go: -------------------------------------------------------------------------------- 1 | package worker_pool 2 | 3 | import "time" 4 | 5 | type Dispatcher interface { 6 | LaunchWorker(w WorkerLauncher) 7 | MakeRequest(Request) 8 | Stop() 9 | } 10 | 11 | type dispatcher struct { 12 | inCh chan Request 13 | } 14 | 15 | func (d *dispatcher) LaunchWorker(w WorkerLauncher) { 16 | w.LaunchWorker(d.inCh) 17 | } 18 | 19 | func (d *dispatcher) MakeRequest(r Request) { 20 | select { 21 | case d.inCh <- r: 22 | case <-time.After(time.Second * 5): 23 | return 24 | } 25 | } 26 | 27 | func (d *dispatcher) Stop() { 28 | close(d.inCh) 29 | } 30 | 31 | func NewDispatcher(b int) Dispatcher { 32 | return &dispatcher{ 33 | inCh: make(chan Request, b), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /command/command.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "fmt" 4 | 5 | type Command interface { 6 | Execute() 7 | } 8 | 9 | type ConsoleOutput struct { 10 | message string 11 | } 12 | 13 | func (c *ConsoleOutput) Execute() { 14 | fmt.Println(c.message) 15 | } 16 | 17 | func CreateCommand(s string) Command { 18 | fmt.Println("Creating command") 19 | 20 | return &ConsoleOutput{ 21 | message: s, 22 | } 23 | } 24 | 25 | type CommandQueue struct { 26 | queue []Command 27 | } 28 | 29 | func (p *CommandQueue) AddCommand(c Command) { 30 | p.queue = append(p.queue, c) 31 | 32 | if len(p.queue) == 3 { 33 | for _, command := range p.queue { 34 | command.Execute() 35 | } 36 | 37 | p.queue = make([]Command, 3) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /or_done/or_done.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | orDone := func(done, c <-chan interface{}) <-chan interface{} { 7 | valStream := make(chan interface{}) 8 | go func() { 9 | defer close(valStream) 10 | for { 11 | select { 12 | case <-done: 13 | return 14 | case v, ok := <-c: 15 | if ok == false { 16 | return 17 | } 18 | // select로 감싼이유는 valStream에 대한 수신하는 부분이 없는경우 block 이 되기때문 19 | select { 20 | case valStream <- v: 21 | case <-done: 22 | } 23 | } 24 | }() 25 | return valStream 26 | } 27 | 28 | done := make(chan interface{}) 29 | myChan := make(chan interface{}) 30 | 31 | for val := range orDone(done, myChan) { 32 | fmt.Println(val) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type LegacyPrinter interface { 8 | Print(s string) string 9 | } 10 | 11 | type MyLegacyPrinter struct { 12 | } 13 | 14 | func (l *MyLegacyPrinter) Print(s string) (newMsg string) { 15 | newMsg = fmt.Sprintf("Legacy Printer: %s\n", s) 16 | println(newMsg) 17 | return 18 | } 19 | 20 | type ModernPrinter interface { 21 | PrintStored() string 22 | } 23 | 24 | type PrinterAdapter struct { 25 | OldPrinter LegacyPrinter 26 | Msg string 27 | } 28 | 29 | func (p *PrinterAdapter) PrintStored() (newMsg string) { 30 | if p.OldPrinter != nil { 31 | newMsg = fmt.Sprintf("Adapter: %s", p.Msg) 32 | newMsg = p.OldPrinter.Print(newMsg) 33 | } else { 34 | newMsg = p.Msg 35 | } 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /decorator/middleware.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | type MyServer struct{} 10 | 11 | func (m *MyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { 12 | fmt.Fprintln(w, "Hello Decorator!") 13 | } 14 | 15 | type LoggerServer struct { 16 | Handler http.Handler 17 | LogWriter io.Writer 18 | } 19 | 20 | func (s *LoggerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { 21 | fmt.Fprintf(s.LogWriter, "Request URI: %s\n", r.RequestURI) 22 | fmt.Fprintf(s.LogWriter, "Host: %s\n", r.Host) 23 | fmt.Fprintf(s.LogWriter, "Content Length: %d\n", r.ContentLength) 24 | fmt.Fprintf(s.LogWriter, "Method: %s\n", r.Method) 25 | fmt.Fprintf(s.LogWriter, "==========================\n") 26 | s.Handler.ServeHTTP(w, r) 27 | } 28 | -------------------------------------------------------------------------------- /worker_pool/worker_pool_test.go: -------------------------------------------------------------------------------- 1 | package worker_pool 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func TestWorkerPool(t *testing.T) { 10 | bufferSize := 100 11 | var dispatcher Dispatcher = NewDispatcher(bufferSize) 12 | 13 | workers := 3 14 | for i := 0; i < workers; i++ { 15 | var w WorkerLauncher = &PreffixSuffixWorker{ 16 | id: i, 17 | prefixS: fmt.Sprintf("WorkerID: %d ->", i), 18 | suffixS: " World", 19 | } 20 | dispatcher.LaunchWorker(w) 21 | } 22 | 23 | requests := 10 24 | 25 | var wg sync.WaitGroup 26 | wg.Add(requests) 27 | 28 | for i := 0; i < requests; i++ { 29 | req := NewStringRequest(fmt.Sprintf("(Msg_id: %d) -> Hello", i), &wg) 30 | dispatcher.MakeRequest(req) 31 | } 32 | 33 | dispatcher.Stop() 34 | wg.Wait() 35 | 36 | } 37 | -------------------------------------------------------------------------------- /command/command_example2.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Command2 interface { 9 | Info() string 10 | } 11 | 12 | type TimePassed struct { 13 | start time.Time 14 | } 15 | 16 | func (t *TimePassed) Info() string { 17 | return time.Since(t.start).String() 18 | } 19 | 20 | type HelloMessage struct{} 21 | 22 | func (h HelloMessage) Info() string { 23 | return "Hello world!" 24 | } 25 | 26 | // with chain of responsibility 27 | 28 | type ChainLogger interface { 29 | Next(Command2) 30 | } 31 | 32 | type Logger struct { 33 | NextChain ChainLogger 34 | } 35 | 36 | func (f *Logger) Next(c Command2) { 37 | time.Sleep(time.Second) 38 | 39 | fmt.Printf("Elapsed time from creation: %s\n", c.Info()) 40 | 41 | if f.NextChain != nil { 42 | f.NextChain.Next(c) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /factory_method/factory_method_test.go: -------------------------------------------------------------------------------- 1 | package factory_method 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestFactoryMethodCash(t *testing.T) { 9 | 10 | payment, err := GetPaymentMethod(Cash) 11 | 12 | if err != nil { 13 | t.Fatal("A payment method of type 'Cash' must exist") 14 | } 15 | 16 | msg := payment.Pay(10.30) 17 | if !strings.Contains(msg, "paid using cash") { 18 | t.Error("The cash payment method message wasn't correct") 19 | } 20 | 21 | t.Log("LOG:", msg) 22 | 23 | payment, err = GetPaymentMethod(DebitCard) 24 | 25 | if err != nil { 26 | t.Error("A payment method of type 'DebitCard' must exist") 27 | } 28 | 29 | msg = payment.Pay(22.30) 30 | 31 | if !strings.Contains(msg, "paid using debit card") { 32 | t.Error("The debit card payment method message wasn't correct") 33 | } 34 | 35 | t.Log("LOG:", msg) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /future/future.go: -------------------------------------------------------------------------------- 1 | package future 2 | 3 | import "fmt" 4 | 5 | type SuccessFunc func(string) 6 | type FailFunc func(error) 7 | type ExecuteStringFunc func() (string, error) 8 | 9 | type MaybeString struct { 10 | successFunc SuccessFunc 11 | failFunc FailFunc 12 | } 13 | 14 | func (s *MaybeString) Success(f SuccessFunc) *MaybeString { 15 | s.successFunc = f 16 | return s 17 | } 18 | 19 | func (s *MaybeString) Fail(f FailFunc) *MaybeString { 20 | s.failFunc = f 21 | return s 22 | } 23 | 24 | func (s *MaybeString) Execute(f ExecuteStringFunc) { 25 | go func(s *MaybeString) { 26 | str, err := f() 27 | if err != nil { 28 | s.failFunc(err) 29 | } else { 30 | s.successFunc(str) 31 | } 32 | }(s) 33 | } 34 | 35 | func setContext(msg string) ExecuteStringFunc { 36 | msg = fmt.Sprintf("%s Closure!\n", msg) 37 | 38 | return func() (string, error) { 39 | return msg, nil 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pipeline/pipeline_test.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestPipeLine(t *testing.T) { 9 | cities := []string{"44418", "2358820", "2471217", "2459115", "4118", "2372071", "615702", "968019", "727232", "650272"} 10 | 11 | fmt.Printf("Gathering weather information for %d cities\n", len(cities)) 12 | fmt.Printf("\n#####################\n\n") 13 | data := convertWeatherData(getWeatherData(cities...)) 14 | for data := range data { 15 | if data.Error != nil { 16 | fmt.Printf("Error fetching weather data for city id: %s\n", data.ID) 17 | continue 18 | } 19 | fmt.Printf("Weather Forcast for %s, %s\n", data.City, data.Country.Name) 20 | 21 | for _, day := range data.Forecast { 22 | fmt.Printf("\tDate: %s\n", day.Date) 23 | fmt.Printf("\t\t%s, High of %.2f℉, Low of %.2f℉\n\n", day.Type, day.MaxTemp, day.MinTemp) 24 | } 25 | fmt.Printf("\n#######################\n\n") 26 | } 27 | 28 | fmt.Println("Data fetch complete!") 29 | 30 | } 31 | -------------------------------------------------------------------------------- /observer/observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "fmt" 4 | 5 | type Observer interface { 6 | Notify(string) 7 | } 8 | 9 | type Publisher struct { 10 | ObserversList []Observer 11 | } 12 | 13 | func (s *Publisher) AddObserver(o Observer) { 14 | s.ObserversList = append(s.ObserversList, o) 15 | } 16 | 17 | func (s *Publisher) RemoveObserver(o Observer) { 18 | var indexToRemove int 19 | 20 | for i, observer := range s.ObserversList { 21 | if observer == o { 22 | indexToRemove = i 23 | break 24 | } 25 | } 26 | s.ObserversList = append(s.ObserversList[:indexToRemove], s.ObserversList[indexToRemove+1:]...) 27 | } 28 | 29 | func (s *Publisher) NotifyObservers(m string) { 30 | fmt.Printf("Publisher received message '%s' to notify observers\n", m) 31 | for _, observer := range s.ObserversList { 32 | observer.Notify(m) 33 | } 34 | } 35 | 36 | type TestObserver struct { 37 | ID int 38 | Message string 39 | } 40 | 41 | func (p *TestObserver) Notify(m string) { 42 | fmt.Printf("Observer %d: ,essage '%s' received \n", p.ID, m) 43 | p.Message = m 44 | } 45 | -------------------------------------------------------------------------------- /command/command_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestCommandQueue_AddCommand(t *testing.T) { 10 | queue := CommandQueue{} 11 | 12 | queue.AddCommand(CreateCommand("First Message")) 13 | queue.AddCommand(CreateCommand("Second Message")) 14 | queue.AddCommand(CreateCommand("Third message")) 15 | 16 | queue.AddCommand(CreateCommand("Fourth message")) 17 | queue.AddCommand(CreateCommand("Fifth message")) 18 | 19 | t.Run("Command Example2", func(t *testing.T) { 20 | var timeCommand Command2 21 | timeCommand = &TimePassed{ 22 | start: time.Now(), 23 | } 24 | 25 | var helloCommand Command2 26 | helloCommand = &HelloMessage{} 27 | 28 | time.Sleep(time.Second) 29 | 30 | fmt.Println(timeCommand.Info()) 31 | fmt.Println(helloCommand.Info()) 32 | 33 | }) 34 | 35 | t.Run("with chain of responsibility ", func(t *testing.T) { 36 | second := new(Logger) 37 | first := Logger{NextChain: second} 38 | 39 | command := &TimePassed{start: time.Now()} 40 | 41 | first.Next(command) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /tee/tee.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | orDone := func(done, c <-chan interface{}) <-chan interface{} { 6 | valStream := make(chan interface{}) 7 | go func() { 8 | defer close(valStream) 9 | for { 10 | select { 11 | case <-done: 12 | return 13 | case v, ok := <-c: 14 | if ok == false { 15 | return 16 | } 17 | select { 18 | case valStream <- v: 19 | case <-done: 20 | } 21 | } 22 | } 23 | }() 24 | return valStream 25 | } 26 | 27 | tee := func(done <-chan interface{}, in <-chan interface{}) (_, _ <-chan interface{}) { 28 | out1 := make(chan interface{}) 29 | out2 := make(chan interface{}) 30 | 31 | go func() { 32 | defer close(out1) 33 | defer close(out2) 34 | 35 | for val := range orDone(done, in) { 36 | var out1, out2 = out1, out2 37 | for i := 0; i < 2; i++ { 38 | select { 39 | case <-done: 40 | case out1 <- val: 41 | out1 = nil 42 | case out2 <- val: 43 | out2 = nil 44 | } 45 | } 46 | } 47 | }() 48 | return out1, out2 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /composite/composite.go: -------------------------------------------------------------------------------- 1 | package composite 2 | 3 | import "fmt" 4 | 5 | type Athlete struct{} 6 | 7 | func (a *Athlete) Train() { 8 | fmt.Println("Training") 9 | } 10 | 11 | type CompositeSwimmerA struct { 12 | MyAthlete Athlete 13 | MySwim func() 14 | } 15 | 16 | func Swim() { 17 | fmt.Println("Swimming!") 18 | } 19 | 20 | type Animal struct{} 21 | 22 | func (r *Animal) Eat() { 23 | println("Eating") 24 | } 25 | 26 | type Shark struct { 27 | Animal 28 | Swim func() 29 | } 30 | 31 | // 인터페이스를 사용한 컴포지트 패턴 32 | type Swimmer interface { 33 | Swim() 34 | } 35 | type Trainer interface { 36 | Train() 37 | } 38 | 39 | type SwimmerImpl struct{} 40 | 41 | func (s *SwimmerImpl) Swim() { 42 | println("Swimming!") 43 | } 44 | 45 | type CompositeSwimmerB struct { 46 | Trainer // 필드명을 넣지 않을경우 go에서는 상속과 같이 보이도록 객체내에 객체를 포함 47 | Swimmer 48 | } 49 | 50 | func main() { 51 | swimmer := CompositeSwimmerA{ 52 | MySwim: Swim, 53 | } 54 | swimmer.MyAthlete.Train() 55 | swimmer.MySwim() 56 | Shark{}.Eat() 57 | Shark{}.Swim() 58 | CompositeSwimmerB{}.Train() 59 | CompositeSwimmerB{}.Swim() 60 | } 61 | -------------------------------------------------------------------------------- /prototype/prototype_test.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import "testing" 4 | 5 | func TestClone(t *testing.T) { 6 | shirtCache := new(ShirtsCache) 7 | if shirtCache == nil { 8 | t.Fatal("Received cache was nil") 9 | } 10 | 11 | item1, err := shirtCache.GetClone(White) 12 | if err != nil { 13 | t.Error(err) 14 | } 15 | 16 | if item1 == whitePrototype { 17 | t.Error("item1 cannot be equal to the white prototype") 18 | } 19 | 20 | shirt1, ok := item1.(*Shirt) 21 | if !ok { 22 | t.Fatal("Type assertion for shirt1 couldn't be done successfully") 23 | } 24 | shirt1.SKU = "abbcc" 25 | 26 | item2, err := shirtCache.GetClone(White) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | shirt2, ok := item2.(*Shirt) 32 | if !ok { 33 | t.Fatal("Type assertion for shirt1 couldn't be done successfully") 34 | } 35 | 36 | if shirt1.SKU == shirt2.SKU { 37 | t.Error("SKU's of shirt1 and shirt2 must be different") 38 | } 39 | t.Logf("LOG: %s", shirt1.GetInfo()) 40 | t.Logf("LOG: %s", shirt2.GetInfo()) 41 | t.Logf("LOG: The memory positions of the shirts are different %p != %p \n\n", &shirt1, &shirt2) 42 | } 43 | -------------------------------------------------------------------------------- /visitor/visitor.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | type Visitable interface { 10 | Accept(Visitor) 11 | } 12 | 13 | type MessageA struct { 14 | Msg string 15 | Output io.Writer 16 | } 17 | 18 | type MessageB struct { 19 | Msg string 20 | Output io.Writer 21 | } 22 | 23 | func (m *MessageA) Accept(v Visitor) { 24 | v.VisitA(m) 25 | } 26 | 27 | func (m *MessageB) Accept(v Visitor) { 28 | v.VisitB(m) 29 | } 30 | 31 | func (m *MessageA) Print() { 32 | if m.Output == nil { 33 | m.Output = os.Stdout 34 | } 35 | 36 | fmt.Fprintf(m.Output, "A: %s", m.Msg) 37 | } 38 | 39 | func (m *MessageB) Print() { 40 | if m.Output == nil { 41 | m.Output = os.Stdout 42 | } 43 | fmt.Fprintf(m.Output, "B: %s", m.Msg) 44 | } 45 | 46 | type Visitor interface { 47 | VisitA(*MessageA) 48 | VisitB(*MessageB) 49 | } 50 | 51 | type MessageVisitor struct { 52 | } 53 | 54 | func (mf *MessageVisitor) VisitA(m *MessageA) { 55 | m.Msg = fmt.Sprintf("%s %s", m.Msg, "(Visited A)") 56 | } 57 | func (mf *MessageVisitor) VisitB(m *MessageB) { 58 | m.Msg = fmt.Sprintf("%s %s", m.Msg, "(Visited B)") 59 | } 60 | -------------------------------------------------------------------------------- /abstract_factory/abstract_factory_test.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | import "testing" 4 | 5 | func TestMotorbikeFactory(t *testing.T) { 6 | motorbikeF, err := BuildFactory(MotorbikeFactoryType) 7 | if err != nil { 8 | t.Fatal(err) 9 | } 10 | 11 | motorbikeVehicle, err := motorbikeF.Build(SportMotorbikeType) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | t.Logf("Motorbike vehicle has %d wheels\n", motorbikeVehicle.NumWheels()) 17 | 18 | sportBike, ok := motorbikeVehicle.(Motorbike) 19 | if !ok { 20 | t.Fatal("Struct assertion has failed") 21 | } 22 | t.Logf("Sport motorbike has type %d\n", sportBike.GetMotorbikeType()) 23 | 24 | } 25 | 26 | func TestCarFactory(t *testing.T) { 27 | carF, err := BuildFactory(CarFactoryType) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | carVehicle, err := carF.Build(LuxuryCarType) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | t.Logf("Car vehicle has %d seats\n", carVehicle.NumWheels()) 38 | 39 | luxuryCar, ok := carVehicle.(Car) 40 | if !ok { 41 | t.Fatal("Struct assertion has failed") 42 | } 43 | t.Logf("Luxury car has %d doors.\n", luxuryCar.NumDoors()) 44 | } 45 | -------------------------------------------------------------------------------- /visitor/visitor_test.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import "testing" 4 | 5 | type TestHelper struct { 6 | Received string 7 | } 8 | 9 | func (t *TestHelper) Write(p []byte) (int, error) { 10 | t.Received = string(p) 11 | return len(p), nil 12 | } 13 | 14 | func Test_Overall(t *testing.T) { 15 | testHelper := &TestHelper{} 16 | visitor := &MessageVisitor{} 17 | 18 | t.Run("MessageA test", func(t *testing.T) { 19 | msg := MessageA{ 20 | Msg: "Hello World", 21 | Output: testHelper, 22 | } 23 | 24 | msg.Accept(visitor) 25 | msg.Print() 26 | expected := "A: Hello World (Visited A)" 27 | if testHelper.Received != expected { 28 | t.Errorf("Expected result was incorrect. %s != %s", 29 | testHelper.Received, expected) 30 | } 31 | }) 32 | 33 | t.Run("MessageB test", func(t *testing.T) { 34 | msg := MessageB{ 35 | Msg: "Hello World", 36 | Output: testHelper, 37 | } 38 | 39 | msg.Accept(visitor) 40 | msg.Print() 41 | 42 | expected := "B: Hello World (Visited B)" 43 | if testHelper.Received != expected { 44 | t.Errorf("Expected result was incorrect. %s != %s", 45 | testHelper.Received, expected) 46 | } 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | type IngredientAdd interface { 9 | AddIngredient() (string, error) 10 | } 11 | 12 | type PizzaDecorator struct { 13 | Ingredient IngredientAdd 14 | } 15 | 16 | func (p *PizzaDecorator) AddIngredient() (string, error) { 17 | return "Pizza with the following ingredients:", nil 18 | } 19 | 20 | type Meat struct { 21 | Ingredient IngredientAdd 22 | } 23 | 24 | func (m *Meat) AddIngredient() (string, error) { 25 | if m.Ingredient == nil { 26 | return "", errors.New("An IngredientAdd is needed in the Ingredient field of the Meat") 27 | } 28 | s, err := m.Ingredient.AddIngredient() 29 | if err != nil { 30 | return "", err 31 | } 32 | return fmt.Sprintf("%s %s,", s, "meat"), nil 33 | } 34 | 35 | type Onion struct { 36 | Ingredient IngredientAdd 37 | } 38 | 39 | func (o *Onion) AddIngredient() (string, error) { 40 | if o.Ingredient == nil { 41 | return "", errors.New("An IngredientAdd is needed in the Ingredient field of the Onion") 42 | } 43 | s, err := o.Ingredient.AddIngredient() 44 | if err != nil { 45 | return "", err 46 | } 47 | return fmt.Sprintf("%s %s,", s, "onion"), nil 48 | } 49 | -------------------------------------------------------------------------------- /factory_method/factory_method.go: -------------------------------------------------------------------------------- 1 | package factory_method 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type PaymentMethod interface { 9 | Pay(amount float32) string 10 | } 11 | 12 | const ( 13 | Cash = 1 14 | DebitCard = 2 15 | ) 16 | 17 | func GetPaymentMethod(m int) (PaymentMethod, error) { 18 | switch m { 19 | case Cash: 20 | return new(CashPM), nil 21 | case DebitCard: 22 | 23 | //reutnr new(DebitCardPM), nil 24 | 25 | // 크레딧 카드로 바꿈 26 | // 메시지 내용이 다르다고 테스트를 수정하면 안됨 테스트코드 커플링을 만들수 있기때문에 27 | return new(CreditCardPM), nil 28 | default: 29 | return nil, errors.New(fmt.Sprintf("Payment method %d not recognized\n", m)) 30 | } 31 | } 32 | 33 | type CashPM struct{} 34 | type DebitCardPM struct{} 35 | 36 | func (c *CashPM) Pay(amount float32) string { 37 | return fmt.Sprintf("%0.2f paid using cash\n", amount) 38 | } 39 | 40 | func (c *DebitCardPM) Pay(amount float32) string { 41 | return fmt.Sprintf("%0.2f paid using debit card\n", amount) 42 | } 43 | 44 | type CreditCardPM struct{} 45 | 46 | func (c *CreditCardPM) Pay(amount float32) string { 47 | 48 | //return fmt.Sprintf("%#0.2f paid using new credit card implementation\n", amount) 49 | //메시지의 내용을 바꿈 50 | return fmt.Sprintf("%#0.2f paid using debit card (new)\n", amount) 51 | } 52 | -------------------------------------------------------------------------------- /template/template_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestTemplateImpl_ExecuteAlgorithm(t *testing.T) { 9 | t.Run("Using Interfaces", func(t *testing.T) { 10 | s := &TestStruct{&TemplateImpl{}} 11 | 12 | res := s.ExecuteAlgorithm(s) 13 | expected := "world" 14 | 15 | if !strings.Contains(res, expected) { 16 | t.Errorf("Expected string '%s' wasn't found on returned string\n", expected) 17 | } 18 | }) 19 | 20 | t.Run("Using anonymous functions", func(t *testing.T) { 21 | m := new(AnonymousTemplate) 22 | 23 | res := m.ExecuteAlgorithm(func() string { 24 | return "world" 25 | }) 26 | 27 | expectedOrError(res, " world ", t) 28 | }) 29 | 30 | t.Run("Using anonymous functions adapted to an interface", func(t *testing.T) { 31 | messageRetriever := MessageRetrieverAdapter(func() string { 32 | return "world" 33 | }) 34 | 35 | if messageRetriever == nil { 36 | t.Fatal("can not continue with a nil MessageRetriever") 37 | } 38 | 39 | template := Template() 40 | res := template.ExecuteAlgorithm(messageRetriever) 41 | 42 | expectedOrError(res, " world ", t) 43 | 44 | }) 45 | } 46 | 47 | func expectedOrError(res string, expected string, t *testing.T) { 48 | if !strings.Contains(res, expected) { 49 | t.Errorf("Expected string '%s' was not found on returned string\n", expected) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /memento/memento_test.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | import "testing" 4 | 5 | func TestCareTaker_Add(t *testing.T) { 6 | originator := originator{} 7 | originator.state = State{Description: "Idle"} 8 | 9 | careTaker := careTaker{} 10 | mem := originator.NewMemento() 11 | 12 | if mem.state.Description != "Idle" { 13 | t.Errorf("Expected state was not found") 14 | } 15 | 16 | currentLen := len(careTaker.mementoList) 17 | careTaker.Add(mem) 18 | 19 | if len(careTaker.mementoList) != currentLen+1 { 20 | t.Errorf("No new elements were added on the list") 21 | } 22 | } 23 | 24 | func TestCareTaker_Memento(t *testing.T) { 25 | originator := originator{} 26 | careTaker := careTaker{} 27 | 28 | originator.state = State{"Idle"} 29 | careTaker.Add(originator.NewMemento()) 30 | 31 | mem, err := careTaker.Memento(0) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | if mem.state.Description != "Idle" { 37 | t.Error("Unexpected state") 38 | } 39 | 40 | mem, err = careTaker.Memento(-1) 41 | if err == nil { 42 | t.Fatal("An error is expected when asking for a negative number but no error was found") 43 | } 44 | } 45 | 46 | func TestOriginator_ExtractAndStoreState(t *testing.T) { 47 | originator := originator{State{"Idle"}} 48 | idleMemento := originator.NewMemento() 49 | 50 | originator.ExtractAndStoreState(idleMemento) 51 | if originator.state.Description != "Idle" { 52 | t.Errorf("Unexpected state found") 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /prototype/prototype.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type ShirtCloner interface { 9 | GetClone(s int) (ItemInfoGetter, error) 10 | } 11 | 12 | const ( 13 | White = 1 14 | Black = 2 15 | Blue = 3 16 | ) 17 | 18 | type ShirtsCache struct{} 19 | 20 | func (s *ShirtsCache) GetClone(m int) (ItemInfoGetter, error) { 21 | switch m { 22 | case White: 23 | newItem := *whitePrototype 24 | return &newItem, nil 25 | case Black: 26 | newItem := *blackPrototype 27 | return &newItem, nil 28 | case Blue: 29 | newItem := *bluePrototype 30 | return &newItem, nil 31 | default: 32 | return nil, errors.New("Shirt model not recognized") 33 | } 34 | } 35 | 36 | type ItemInfoGetter interface { 37 | GetInfo() string 38 | } 39 | 40 | type ShirtColor byte 41 | 42 | type Shirt struct { 43 | Price float32 44 | SKU string 45 | Color ShirtColor 46 | } 47 | 48 | func (s *Shirt) GetInfo() string { 49 | return fmt.Sprintf("Shirt with SKU '%s' and Color id %d that costs %f\n", s.SKU, s.Color, s.Price) 50 | } 51 | 52 | var whitePrototype *Shirt = &Shirt{ 53 | Price: 15.00, 54 | SKU: "empty", 55 | Color: White, 56 | } 57 | 58 | func (i *Shirt) GetPrice() float32 { 59 | return i.Price 60 | } 61 | 62 | var blackPrototype *Shirt = &Shirt{ 63 | Price: 16.00, 64 | SKU: "empty", 65 | Color: Black, 66 | } 67 | 68 | var bluePrototype *Shirt = &Shirt{ 69 | Price: 17.00, 70 | SKU: "empty", 71 | Color: Blue, 72 | } 73 | -------------------------------------------------------------------------------- /proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type UserFinder interface { 8 | FindUser(id int32) (User, error) 9 | } 10 | 11 | type User struct { 12 | ID int32 13 | } 14 | 15 | type UserList []User 16 | 17 | func (t *UserList) FindUser(id int32) (User, error) { 18 | for i := 0; i < len(*t); i++ { 19 | if (*t)[i].ID == id { 20 | return (*t)[i], nil 21 | } 22 | } 23 | return User{}, fmt.Errorf("User %d could not be found\n", id) 24 | } 25 | 26 | type UserListProxy struct { 27 | SomeDatabase UserList 28 | StackCache UserList 29 | StackCapacity int 30 | DidLastSearchUsedCache bool 31 | } 32 | 33 | func (u *UserListProxy) FindUser(id int32) (User, error) { 34 | user, err := u.StackCache.FindUser(id) 35 | if err == nil { 36 | fmt.Println("Returning user from cache") 37 | u.DidLastSearchUsedCache = true 38 | return user, nil 39 | } 40 | user, err = u.SomeDatabase.FindUser(id) 41 | if err != nil { 42 | return User{}, err 43 | } 44 | 45 | u.addUserToStack(user) 46 | fmt.Println("Returning user from database") 47 | u.DidLastSearchUsedCache = false 48 | return user, nil 49 | } 50 | 51 | func (u *UserListProxy) addUserToStack(user User) { 52 | if len(u.StackCache) >= u.StackCapacity { 53 | u.StackCache = append(u.StackCache[1:], user) 54 | } else { 55 | u.StackCache.addUser(user) 56 | } 57 | } 58 | 59 | func (t *UserList) addUser(newUser User) { 60 | *t = append(*t, newUser) 61 | } 62 | -------------------------------------------------------------------------------- /abstract_factory/vehicle_factory.go: -------------------------------------------------------------------------------- 1 | package abstract_factory 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type VehicleFactory interface { 9 | Build(v int) (Vehicle, error) 10 | } 11 | 12 | const ( 13 | CarFactoryType = 1 14 | MotorbikeFactoryType = 2 15 | ) 16 | 17 | func BuildFactory(f int) (VehicleFactory, error) { 18 | switch f { 19 | case CarFactoryType: 20 | return new(CarFactory), nil 21 | case MotorbikeFactoryType: 22 | return new(MotorbikeFactory), nil 23 | default: 24 | return nil, errors.New(fmt.Sprintf("Factory with id %d not recognized\n", f)) 25 | } 26 | } 27 | 28 | const ( 29 | LuxuryCarType = 1 30 | FamilyCarType = 2 31 | ) 32 | 33 | type CarFactory struct{} 34 | 35 | func (c *CarFactory) Build(v int) (Vehicle, error) { 36 | switch v { 37 | case LuxuryCarType: 38 | return new(LuxuryCar), nil 39 | case FamilyCarType: 40 | return new(FamilyCar), nil 41 | default: 42 | return nil, errors.New(fmt.Sprintf("Vehicle of type %d not recognized\n", v)) 43 | } 44 | } 45 | 46 | const ( 47 | SportMotorbikeType = 1 48 | CruiseMotorbikeType = 2 49 | ) 50 | 51 | type MotorbikeFactory struct{} 52 | 53 | func (m *MotorbikeFactory) Build(v int) (Vehicle, error) { 54 | switch v { 55 | case SportMotorbikeType: 56 | return new(SportMotorbike), nil 57 | case CruiseMotorbikeType: 58 | return new(CruiseMotorbike), nil 59 | default: 60 | return nil, errors.New(fmt.Sprintf("Vehicle of type %d not recognized\n", v)) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /chain_responsibility/chain_responsibility_test.go: -------------------------------------------------------------------------------- 1 | package chain_responsibility 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestCreateDefaultChain(t *testing.T) { 10 | myWriter := myTestWriter{} 11 | 12 | writerLogger := WriterLogger{Writer: &myWriter} 13 | second := SecondLogger{NextChain: &writerLogger} 14 | chain := FirstLogger{NextChain: &second} 15 | 16 | t.Run("3 loggers, 2 of them writes to console, second only if it founds "+ 17 | "the word 'hello', third writes to some variable if second found 'hello'", func(t *testing.T) { 18 | chain.Next("Message that breaks the chain\n") 19 | 20 | if myWriter.receivedMessage != nil { 21 | t.Fatal("Last link should not receive any message") 22 | } 23 | 24 | chain.Next("Hello\n") 25 | 26 | if myWriter.receivedMessage == nil || !strings.Contains(*myWriter.receivedMessage, "Hello") { 27 | t.Fatal("Last link didn't reveived expected message") 28 | } 29 | }) 30 | 31 | t.Run("2 loggers, second uses the closure implementation", func(t *testing.T) { 32 | myWriter := myTestWriter{} 33 | 34 | closureLogger := ClosureChain{ 35 | Closure: func(s string) { 36 | fmt.Printf("My closure logger! Message: %s\n", s) 37 | myWriter.receivedMessage = &s 38 | }, 39 | } 40 | writerLogger.NextChain = &closureLogger 41 | 42 | chain.Next("Hello closure logger") 43 | if *myWriter.receivedMessage != "Hello closure logger" { 44 | t.Fatal("Expected message wasn't reveive in myWriter") 45 | } 46 | 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /future/future_test.go: -------------------------------------------------------------------------------- 1 | package future 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func timeout(t *testing.T, wg *sync.WaitGroup) { 11 | time.Sleep(time.Second) 12 | t.Log("Timeout!") 13 | 14 | t.Fail() 15 | wg.Done() 16 | } 17 | 18 | func TestStrinbgOrError(t *testing.T) { 19 | future := &MaybeString{} 20 | t.Run("Success result", func(t *testing.T) { 21 | var wg sync.WaitGroup 22 | wg.Add(1) 23 | 24 | go timeout(t, &wg) 25 | future.Success(func(s string) { 26 | t.Log(s) 27 | wg.Done() 28 | }).Fail(func(e error) { 29 | t.Fail() 30 | wg.Done() 31 | }) 32 | 33 | future.Execute(func() (string, error) { 34 | 35 | return "Hello World!", nil 36 | 37 | }) 38 | wg.Wait() 39 | }) 40 | 41 | t.Run("Error result", func(t *testing.T) { 42 | var wg sync.WaitGroup 43 | 44 | wg.Add(1) 45 | go timeout(t, &wg) 46 | future.Success(func(s string) { 47 | 48 | t.Fail() 49 | 50 | wg.Done() 51 | 52 | }).Fail(func(e error) { 53 | 54 | t.Log(e.Error()) 55 | 56 | wg.Done() 57 | 58 | }) 59 | 60 | future.Execute(func() (string, error) { 61 | 62 | return "", errors.New("Error ocurred") 63 | 64 | }) 65 | 66 | wg.Wait() 67 | }) 68 | 69 | t.Run("Closure Success result", func(t *testing.T) { 70 | var wg sync.WaitGroup 71 | wg.Add(1) 72 | //Timeout! 73 | go timeout(t, &wg) 74 | 75 | future.Success(func(s string) { 76 | t.Log(s) 77 | wg.Done() 78 | }).Fail(func(e error) { 79 | t.Fail() 80 | wg.Done() 81 | }) 82 | future.Execute(setContext("Hello")) 83 | wg.Wait() 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /worker_pool/worker.go: -------------------------------------------------------------------------------- 1 | package worker_pool 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type WorkerLauncher interface { 9 | LaunchWorker(in chan Request) 10 | } 11 | 12 | type PreffixSuffixWorker struct { 13 | id int 14 | prefixS string 15 | suffixS string 16 | } 17 | 18 | func (w *PreffixSuffixWorker) LaunchWorker(in chan Request) { 19 | w.prefix(w.append(w.uppercase(in))) 20 | } 21 | 22 | func (w *PreffixSuffixWorker) prefix(in <-chan Request) { 23 | go func() { 24 | for msg := range in { 25 | uppercasedStringWithSuffix, ok := msg.Data.(string) 26 | 27 | if !ok { 28 | msg.Handler(nil) 29 | continue 30 | } 31 | 32 | msg.Handler(fmt.Sprintf("%s%s", w.prefixS, uppercasedStringWithSuffix)) 33 | } 34 | }() 35 | } 36 | 37 | func (w *PreffixSuffixWorker) append(in <-chan Request) <-chan Request { 38 | out := make(chan Request) 39 | go func() { 40 | for msg := range in { 41 | uppercaseString, ok := msg.Data.(string) 42 | 43 | if !ok { 44 | msg.Handler(nil) 45 | continue 46 | } 47 | msg.Data = fmt.Sprintf("%s%s", uppercaseString, w.suffixS) 48 | out <- msg 49 | } 50 | close(out) 51 | }() 52 | return out 53 | } 54 | 55 | func (w *PreffixSuffixWorker) uppercase(in <-chan Request) <-chan Request { 56 | out := make(chan Request) 57 | 58 | go func() { 59 | for msg := range in { 60 | s, ok := msg.Data.(string) 61 | 62 | if !ok { 63 | msg.Handler(nil) 64 | continue 65 | } 66 | 67 | msg.Data = strings.ToUpper(s) 68 | 69 | out <- msg 70 | } 71 | 72 | close(out) 73 | }() 74 | 75 | return out 76 | } 77 | -------------------------------------------------------------------------------- /flyweight/flyweight.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | import "time" 4 | 5 | type Team struct { 6 | ID uint64 7 | Name string 8 | Shield []byte 9 | Players []Player 10 | HistoricalData []HistoricalData 11 | } 12 | 13 | const ( 14 | TEAM_A = iota 15 | TEAM_B 16 | ) 17 | 18 | type Player struct { 19 | Name string 20 | Surname string 21 | PreviousTeam uint64 22 | Photo []byte 23 | } 24 | 25 | type HistoricalData struct { 26 | Year uint8 27 | LeagueResults []Match 28 | } 29 | 30 | type Match struct { 31 | Data time.Time 32 | VisitorID uint64 33 | LocalID uint64 34 | LocalScore byte 35 | VisitorScore byte 36 | LocalShoots uint16 37 | VisitorShoots uint16 38 | } 39 | 40 | type teamFlyweightFactory struct { 41 | createdTeams map[int]*Team 42 | } 43 | 44 | func NewTeamFactory() teamFlyweightFactory { 45 | return teamFlyweightFactory{ 46 | createdTeams: make(map[int]*Team), 47 | } 48 | } 49 | 50 | func (t *teamFlyweightFactory) GetTeam(teamID int) *Team { 51 | if t.createdTeams[teamID] != nil { 52 | return t.createdTeams[teamID] 53 | } 54 | 55 | team := getTeamFactory(teamID) 56 | t.createdTeams[teamID] = &team 57 | 58 | return t.createdTeams[teamID] 59 | } 60 | 61 | func (t *teamFlyweightFactory) GetNumberofObjects() int { 62 | return len(t.createdTeams) 63 | } 64 | 65 | func getTeamFactory(team int) Team { 66 | switch team { 67 | case TEAM_B: 68 | return Team{ 69 | ID: 2, 70 | Name: "TEAM_B", 71 | } 72 | default: 73 | return Team{ 74 | ID: 1, 75 | Name: "TEAM_A", 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /chain_responsibility/chain_responsibility.go: -------------------------------------------------------------------------------- 1 | package chain_responsibility 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | type ChainLogger interface { 10 | Next(string) 11 | } 12 | 13 | type FirstLogger struct { 14 | NextChain ChainLogger 15 | } 16 | 17 | func (f *FirstLogger) Next(s string) { 18 | fmt.Printf("First logger: %s\n", s) 19 | if f.NextChain != nil { 20 | f.NextChain.Next(s) 21 | } 22 | } 23 | 24 | type SecondLogger struct { 25 | NextChain ChainLogger 26 | } 27 | 28 | func (se *SecondLogger) Next(s string) { 29 | if strings.Contains(strings.ToLower(s), "hello") { 30 | fmt.Printf("Second logger: %s\n", s) 31 | 32 | if se.NextChain != nil { 33 | se.NextChain.Next(s) 34 | } 35 | return 36 | } 37 | fmt.Print("finishing in second logging\n\n") 38 | } 39 | 40 | type WriterLogger struct { 41 | NextChain ChainLogger 42 | Writer io.Writer 43 | } 44 | 45 | func (w *WriterLogger) Next(s string) { 46 | if w.Writer != nil { 47 | w.Writer.Write([]byte("writerLogger: " + s)) 48 | } 49 | 50 | if w.NextChain != nil { 51 | w.NextChain.Next(s) 52 | } 53 | } 54 | 55 | type myTestWriter struct { 56 | receivedMessage *string 57 | } 58 | 59 | func (m *myTestWriter) Write(p []byte) (int, error) { 60 | if m.receivedMessage == nil { 61 | m.receivedMessage = new(string) 62 | } 63 | 64 | tempMessage := fmt.Sprintf("%s%s", *m.receivedMessage, p) 65 | m.receivedMessage = &tempMessage 66 | return len(p), nil 67 | } 68 | 69 | func (m *myTestWriter) Next(s string) { 70 | m.Write([]byte(s)) 71 | } 72 | 73 | type ClosureChain struct { 74 | NextChain ChainLogger 75 | Closure func(string) 76 | } 77 | 78 | func (c *ClosureChain) Next(s string) { 79 | if c.Closure != nil { 80 | c.Closure(s) 81 | } 82 | 83 | if c.NextChain != nil { 84 | c.Next(s) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /visitor/another_example/productInfoRetriever.go: -------------------------------------------------------------------------------- 1 | package another_example 2 | 3 | import "fmt" 4 | 5 | type ProductInfoRetriever interface { 6 | GetPrice() float32 7 | GetName() string 8 | } 9 | 10 | type Visitor interface { 11 | Visit(ProductInfoRetriever) 12 | } 13 | 14 | type Visitable interface { 15 | Accept(Visitor) 16 | } 17 | 18 | type Product struct { 19 | Price float32 20 | Name string 21 | } 22 | 23 | func (p *Product) GetPrice() float32 { 24 | return p.Price 25 | } 26 | 27 | func (p *Product) GetName() string { 28 | return p.Name 29 | } 30 | 31 | func (p *Product) Accept(v Visitor) { 32 | v.Visit(p) 33 | } 34 | 35 | // ========================== 36 | type Rice struct { 37 | Product 38 | } 39 | 40 | type Pasta struct { 41 | Product 42 | } 43 | 44 | type PriceVisitor struct { 45 | Sum float32 46 | } 47 | 48 | func (pv *PriceVisitor) Visit(p ProductInfoRetriever) { 49 | pv.Sum += p.GetPrice() 50 | } 51 | 52 | type NamePrinter struct { 53 | ProductList string 54 | } 55 | 56 | func (n *NamePrinter) Visit(p ProductInfoRetriever) { 57 | n.ProductList = fmt.Sprintf("%s\n%s", p.GetName(), n.ProductList) 58 | } 59 | 60 | func main() { 61 | products := make([]Visitable, 2) 62 | products[0] = &Rice{ 63 | Product: Product{ 64 | Price: 32.0, 65 | Name: "Some rice", 66 | }, 67 | } 68 | products[1] = &Pasta{ 69 | Product: Product{ 70 | Price: 40.0, 71 | Name: "Some pasta", 72 | }, 73 | } 74 | 75 | //Print the sum of prices 76 | priceVisitor := &PriceVisitor{} 77 | 78 | for _, p := range products { 79 | p.Accept(priceVisitor) 80 | } 81 | 82 | fmt.Printf("Total: %f\n", priceVisitor.Sum) 83 | 84 | //Print the products list 85 | nameVisitor := &NamePrinter{} 86 | 87 | for _, p := range products { 88 | p.Accept(nameVisitor) 89 | } 90 | 91 | fmt.Printf("\nProduct list:\n-------------\n%s", nameVisitor.ProductList) 92 | } 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Go template 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.exe~ 6 | *.dll 7 | *.so 8 | *.dylib 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | ### JetBrains template 16 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 17 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 18 | 19 | # User-specific stuff 20 | .idea/**/workspace.xml 21 | .idea/**/tasks.xml 22 | .idea/**/usage.statistics.xml 23 | .idea/**/dictionaries 24 | .idea/**/shelf 25 | 26 | # Sensitive or high-churn files 27 | .idea/**/dataSources/ 28 | .idea/**/dataSources.ids 29 | .idea/**/dataSources.local.xml 30 | .idea/**/sqlDataSources.xml 31 | .idea/**/dynamic.xml 32 | .idea/**/uiDesigner.xml 33 | .idea/**/dbnavigator.xml 34 | 35 | # Gradle 36 | .idea/**/gradle.xml 37 | .idea/**/libraries 38 | 39 | # Gradle and Maven with auto-import 40 | # When using Gradle or Maven with auto-import, you should exclude module files, 41 | # since they will be recreated, and may cause churn. Uncomment if using 42 | # auto-import. 43 | # .idea/modules.xml 44 | # .idea/*.iml 45 | # .idea/modules 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | .idea/ -------------------------------------------------------------------------------- /observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "testing" 4 | 5 | func TestSubject(t *testing.T) { 6 | testObserver1 := &TestObserver{1, ""} 7 | testObserver2 := &TestObserver{2, ""} 8 | testObserver3 := &TestObserver{3, ""} 9 | publisher := Publisher{} 10 | 11 | t.Run("AddObserver", func(t *testing.T) { 12 | publisher.AddObserver(testObserver1) 13 | publisher.AddObserver(testObserver2) 14 | publisher.AddObserver(testObserver3) 15 | 16 | if len(publisher.ObserversList) != 3 { 17 | t.Fail() 18 | } 19 | }) 20 | 21 | t.Run("RemoveObserver", func(t *testing.T) { 22 | publisher.RemoveObserver(testObserver2) 23 | 24 | if len(publisher.ObserversList) != 2 { 25 | t.Errorf("The size of the observer list is not the "+ 26 | "expected. 3 != %d\n", len(publisher.ObserversList)) 27 | } 28 | 29 | for _, observer := range publisher.ObserversList { 30 | testObserver, ok := observer.(*TestObserver) 31 | if !ok { 32 | t.Fail() 33 | } 34 | 35 | if testObserver.ID == 2 { 36 | t.Fail() 37 | } 38 | } 39 | }) 40 | 41 | t.Run("Notify", func(t *testing.T) { 42 | for _, observer := range publisher.ObserversList { 43 | printObserver, ok := observer.(*TestObserver) 44 | if !ok { 45 | t.Fail() 46 | break 47 | } 48 | 49 | if printObserver.Message != "" { 50 | t.Errorf("The observer's Message field weren't "+" empty: %s\n", printObserver.Message) 51 | } 52 | } 53 | 54 | message := "Hello World!" 55 | publisher.NotifyObservers(message) 56 | 57 | for _, observer := range publisher.ObserversList { 58 | printObserver, ok := observer.(*TestObserver) 59 | if !ok { 60 | t.Fail() 61 | break 62 | } 63 | 64 | if printObserver.Message != message { 65 | t.Errorf("Expected message on observer %d was"+ 66 | "not expected: '%s' != '%s'\n", printObserver.ID, printObserver.Message, message) 67 | } 68 | } 69 | 70 | }) 71 | 72 | } 73 | -------------------------------------------------------------------------------- /template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "strings" 4 | 5 | type MessageRetriever interface { 6 | Message() string 7 | } 8 | 9 | type Template interface { 10 | first() string 11 | third() string 12 | ExecuteAlgorithm(MessageRetriever) string 13 | } 14 | 15 | 16 | 17 | type TemplateImpl struct {} 18 | 19 | func (t *TemplateImpl) first() string{ 20 | return "hello" 21 | } 22 | 23 | func (t *TemplateImpl) third() string { 24 | return "template" 25 | } 26 | 27 | func (t *TemplateImpl) ExecuteAlgorithm(m MessageRetriever) string{ 28 | return strings.Join([]string{t.first(),m.Message(),t.third()}," ") 29 | } 30 | 31 | type TestStruct struct { 32 | Template 33 | } 34 | 35 | func (m *TestStruct) Message() string{ 36 | return "world" 37 | } 38 | 39 | 40 | //익명함수를 이용하였지만 인터페이스의 변경이 없었다 이를 해결하려면 ?? 어댑터 패턴 41 | 42 | type AnonymousTemplate struct { 43 | } 44 | 45 | func (a *AnonymousTemplate) first() string { 46 | return "hello" 47 | } 48 | 49 | func (a *AnonymousTemplate) third() string { 50 | return "template" 51 | } 52 | 53 | func (a *AnonymousTemplate) ExecuteAlgorithm(f func() string) string { 54 | return strings.Join([]string{a.first(),f(),a.third()}," ") 55 | } 56 | 57 | 58 | // adapter 패턴 적용 59 | 60 | 61 | func MessageRetrieverAdapter(f func() string) MessageRetriever { 62 | return &adapter{myFunc: f} 63 | } 64 | 65 | 66 | type adapter struct { 67 | myFunc func() string 68 | } 69 | 70 | func (a *adapter) Message() string { 71 | if a.myFunc != nil { 72 | return a.myFunc() 73 | } 74 | 75 | return "" 76 | } 77 | 78 | 79 | // ============ in Go Source Code 80 | 81 | type MyList []int 82 | 83 | func (m MyList) Len() int { 84 | return len(m) 85 | } 86 | 87 | func (m MyList) Swap(i, j int) { 88 | m[i], m[j] = m[j], m[i] 89 | } 90 | 91 | func (m MyList) Less(i, j int) bool { 92 | return m[i] < m[j] 93 | } 94 | 95 | var myList MyList = []int{6,4,2,8,1} 96 | 97 | fmt.Println(myList) 98 | sort.Sort(myList) 99 | fmt.Println(myList) 100 | 101 | gluster volume set galmal_vol auth.allow 192.168.1.* -------------------------------------------------------------------------------- /bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "io" 7 | ) 8 | 9 | /* 10 | 1. 콘솔에 print할 프린터 생성 11 | 2. io.Writer인터페이스를 사용해서 print할 프린터 생성 12 | 13 | 요구사항 14 | 1. 메시지를 받아들여 출력하는 `PrinterAPI` 15 | 2. 메시지를 콘솔에 출력하는 API 구현 16 | 3. 메시지를 io.Writer인터페이스에 출력하는 API 구현 17 | 4. printing type을 구현하는 print메소드를 사용하는 Printer 추상화 18 | 5. Printer 와 PrinterAPI인터페이스를 구현하는 normal 생성 19 | 6. normal프린터는 메시지를 구현체에 전달 20 | 7. Printer 추상화 및 PrinterAPI 인터페이스를 구현하는 Packt 프린터 21 | 8. Packt 프린터는 Message from Packt:를 프린트 할때마다 출력 22 | */ 23 | 24 | // 1 메시지를 받아들여 출력하는 `PrinterAPI` 25 | type PrinterAPI interface { 26 | PrintMessage(string) error 27 | } 28 | 29 | // 2 메시지를 콘솔에 출력하는 API 구현 30 | type PrinterImpl1 struct{} 31 | 32 | func (p *PrinterImpl1) PrintMessage(msg string) error { 33 | fmt.Printf("%s\n", msg) 34 | return nil 35 | } 36 | 37 | // 3 메시지를 io.Writer인터페이스에 출력하는 API 구현 38 | type PrinterImpl2 struct { 39 | Writer io.Writer 40 | } 41 | 42 | func (d *PrinterImpl2) PrintMessage(msg string) error { 43 | if d.Writer == nil { 44 | return errors.New("You need to pass an io.Writer to PrinterImpl2") 45 | } 46 | fmt.Fprintf(d.Writer, "%s", msg) 47 | return nil 48 | } 49 | 50 | type TestWriter struct { 51 | Msg string 52 | } 53 | 54 | func (t *TestWriter) Write(p []byte) (n int, err error) { 55 | n = len(p) 56 | if n > 0 { 57 | t.Msg = string(p) 58 | return n, nil 59 | } 60 | err = errors.New("Content received on Writer was empty") 61 | return 62 | } 63 | 64 | //4 printing type을 구현하는 print메소드를 사용하는 Printer 추상화 65 | type PrinterAbstraction interface { 66 | Print() error 67 | } 68 | 69 | //5 Printer 와 PrinterAPI인터페이스를 구현하는 normal 생성 70 | type NormalPrinter struct { 71 | Msg string 72 | Printer PrinterAPI 73 | } 74 | 75 | func (c *NormalPrinter) Print() error { 76 | c.Printer.PrintMessage(c.Msg) 77 | return nil 78 | } 79 | 80 | // 7 Printer 추상화 및 PrinterAPI 인터페이스를 구현하는 Packt 프린터 81 | type PacktPrinter struct { 82 | Msg string 83 | Printer PrinterAPI 84 | } 85 | 86 | func (c *PacktPrinter) Print() error { 87 | c.Printer.PrintMessage(fmt.Sprintf("Message from Packt: %s", c.Msg)) 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /strategy/example1/main.go: -------------------------------------------------------------------------------- 1 | package example1 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "image" 7 | "image/color" 8 | "image/draw" 9 | "image/jpeg" 10 | "log" 11 | "os" 12 | ) 13 | 14 | type OutputStrategy interface { 15 | Draw() error 16 | } 17 | 18 | type TextSquare struct{} 19 | 20 | func (t *TextSquare) Draw() error { 21 | println("Circle") 22 | return nil 23 | } 24 | 25 | type ImageSquare struct { 26 | DestinationFilePath string 27 | } 28 | 29 | func (t *ImageSquare) Draw() error { 30 | width := 800 31 | height := 600 32 | 33 | bgColor := image.Uniform{color.RGBA{R: 70, G: 70, B: 70, A: 0}} 34 | origin := image.Point{0, 0} 35 | quality := &jpeg.Options{Quality: 75} 36 | 37 | bgRectangle := image.NewRGBA(image.Rectangle{ 38 | Min: origin, 39 | Max: image.Point{X: width, Y: height}, 40 | }) 41 | 42 | draw.Draw(bgRectangle, bgRectangle.Bounds(), &bgColor, origin, draw.Src) 43 | 44 | squareWidth := 200 45 | squareHeight := 200 46 | squareColor := image.Uniform{color.RGBA{R: 255, G: 0, B: 0, A: 1}} 47 | square := image.Rect(0, 0, squareWidth, squareHeight) 48 | square = square.Add(image.Point{ 49 | X: (width / 2) - (squareWidth / 2), 50 | Y: (height / 2) - (squareHeight / 2), 51 | }) 52 | squareImg := image.NewRGBA(square) 53 | 54 | draw.Draw(bgRectangle, squareImg.Bounds(), &squareColor, origin, draw.Src) 55 | 56 | w, err := os.Create(t.DestinationFilePath) 57 | if err != nil { 58 | return fmt.Errorf("Error opening image") 59 | } 60 | defer w.Close() 61 | 62 | if err = jpeg.Encode(w, bgRectangle, quality); err != nil { 63 | return fmt.Errorf("Error writing image to disk") 64 | } 65 | 66 | return nil 67 | } 68 | 69 | var output = flag.String("output", "console", "The output to use between 'console' and 'image' file") 70 | 71 | func main() { 72 | flag.Parse() 73 | 74 | var activeStrategy OutputStrategy 75 | 76 | switch *output { 77 | case "console": 78 | activeStrategy = &TextSquare{} 79 | case "image": 80 | activeStrategy = &ImageSquare{"/tmp/image.jpg"} 81 | default: 82 | activeStrategy = &TextSquare{} 83 | } 84 | 85 | err := activeStrategy.Draw() 86 | if err != nil { 87 | log.Fatal(err) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /bridge/bridge_test.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPrintAPI1(t *testing.T) { 8 | api1 := PrinterImpl1{} 9 | 10 | err := api1.PrintMessage("Hello") 11 | if err != nil { 12 | t.Errorf("Error trying to use the API1 implementation: Message: %s\n", err.Error()) 13 | } 14 | } 15 | 16 | func TestPrintAPI2(t *testing.T) { 17 | 18 | testWriter := TestWriter{} 19 | api2 := PrinterImpl2{ 20 | Writer: &testWriter, 21 | } 22 | 23 | expectedMessage := "Hello" 24 | err := api2.PrintMessage(expectedMessage) 25 | if err != nil { 26 | t.Errorf("Error trying to use the API2 implementation: %s\n", err.Error()) 27 | } 28 | 29 | if testWriter.Msg != expectedMessage { 30 | t.Fatalf("API2 did not write correctly on the io.Writer. \n Actual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage) 31 | } 32 | } 33 | 34 | func TestNormalPrinter_Print(t *testing.T) { 35 | expectedMessage := "Hello io.Writer" 36 | 37 | normal := NormalPrinter{ 38 | Msg: expectedMessage, 39 | Printer: &PrinterImpl1{}, 40 | } 41 | 42 | err := normal.Print() 43 | if err != nil { 44 | t.Errorf(err.Error()) 45 | } 46 | testWriter := TestWriter{} 47 | normal = NormalPrinter{ 48 | Msg: expectedMessage, 49 | Printer: &PrinterImpl2{ 50 | Writer: &testWriter, 51 | }, 52 | } 53 | 54 | err = normal.Print() 55 | if err != nil { 56 | t.Error(err.Error()) 57 | } 58 | 59 | if testWriter.Msg != expectedMessage { 60 | t.Errorf("The expected message on the io.Writer doesn't match actual.\n Actual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage) 61 | } 62 | } 63 | 64 | func TestPacktPrinter_Print(t *testing.T) { 65 | passedMessage := "Hello io.Writer" 66 | expectedMessage := "Message from Packt: Hello io.Writer" 67 | 68 | packt := PacktPrinter{ 69 | Msg: passedMessage, 70 | Printer: &PrinterImpl1{}, 71 | } 72 | 73 | err := packt.Print() 74 | if err != nil { 75 | t.Errorf(err.Error()) 76 | } 77 | 78 | testWriter := TestWriter{} 79 | packt = PacktPrinter{ 80 | Msg: passedMessage, 81 | Printer: &PrinterImpl2{ 82 | Writer: &testWriter, 83 | }, 84 | } 85 | 86 | err = packt.Print() 87 | if err != nil { 88 | t.Error(err.Error()) 89 | } 90 | 91 | if testWriter.Msg != expectedMessage { 92 | t.Errorf("The expected message on the io.Writer doesn't match actual.\n Actual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "github.com/labstack/gommon/log" 5 | "net/http" 6 | "os" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestPizzaDecorator_AddIngredient(t *testing.T) { 12 | pizza := &PizzaDecorator{} 13 | pizzaResult, _ := pizza.AddIngredient() 14 | expectedText := "Pizza with the following ingredients:" 15 | if !strings.Contains(pizzaResult, expectedText) { 16 | t.Errorf("When calling the add ingredient of the pizza decorator it must return the text %s the expected text, not '%s'", pizzaResult, expectedText) 17 | } 18 | } 19 | 20 | func TestOnion_AddIngredient(t *testing.T) { 21 | onion := &Onion{&PizzaDecorator{}} 22 | onionResult, err := onion.AddIngredient() 23 | if err != nil { 24 | t.Error(err) 25 | } 26 | if !strings.Contains(onionResult, "onion") { 27 | t.Errorf("When calling the add ingredient of the onion decorator it "+"must return a text with the word 'onion', not '%s'", onionResult) 28 | } 29 | } 30 | 31 | func TestMeat_AddIngredient(t *testing.T) { 32 | meat := &Meat{} 33 | meatResult, err := meat.AddIngredient() 34 | if err == nil { 35 | t.Errorf("When calling AddIngredient on the meat decorator without"+"an IngredientAdd in its Ingredient field must returne an error,"+"not a string with '%s'", meatResult) 36 | } 37 | 38 | meat = &Meat{&PizzaDecorator{}} 39 | meatResult, err = meat.AddIngredient() 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | 44 | if !strings.Contains(meatResult, "meat") { 45 | t.Errorf("When calling the add ingredient of the meat decorator it"+"must return a text with the word 'meat', not '%s'", meatResult) 46 | } 47 | } 48 | 49 | func TestPizzaDecorator_FullStack(t *testing.T) { 50 | pizza := &Onion{&Meat{&PizzaDecorator{}}} 51 | pizzaResult, err := pizza.AddIngredient() 52 | if err != nil { 53 | t.Error(err) 54 | } 55 | 56 | expectedText := "Pizza with the following ingredients: meat, onion" 57 | if !strings.Contains(pizzaResult, expectedText) { 58 | t.Errorf("When asking for a pizza with onion and meat the returned "+"string must contain the text '%s' but '%s' didn't have it", expectedText, pizzaResult) 59 | } 60 | t.Log(pizzaResult) 61 | } 62 | 63 | func TestMyServer_ServeHTTP(t *testing.T) { 64 | http.Handle("/", &MyServer{}) 65 | 66 | log.Fatal(http.ListenAndServe(":8080", nil)) 67 | } 68 | 69 | func TestLoggerServer_ServeHTTP(t *testing.T) { 70 | http.Handle("/", &LoggerServer{ 71 | LogWriter: os.Stdout, 72 | Handler: &MyServer{}, 73 | }) 74 | 75 | log.Fatal(http.ListenAndServe(":8080", nil)) 76 | } 77 | -------------------------------------------------------------------------------- /pipeline/pipeline.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "net/http" 8 | "sync" 9 | ) 10 | 11 | type WeatherData struct { 12 | ID string 13 | Country CountryData `json:"parent"` 14 | City string `json:"title"` 15 | Forecast []*WeatherDay `json:"consolidated_weather"` 16 | Error error 17 | } 18 | 19 | type WeatherDay struct { 20 | Type string `json:"weather_state_name"` 21 | Date string `json:"applicable_date"` 22 | MinTemp float64 `json:"min_temp"` 23 | MaxTemp float64 `json:"max_temp"` 24 | } 25 | 26 | type CountryData struct { 27 | Name string `json:"title"` 28 | } 29 | 30 | const BASE_URL = "https://www.metaweather.com/api/location/%s/" 31 | 32 | func getWeatherData(ids ...string) <-chan WeatherData { 33 | var wg sync.WaitGroup 34 | 35 | out := make(chan WeatherData) 36 | wg.Add(len(ids)) 37 | 38 | for _, id := range ids { 39 | go func(cityId string) { 40 | var data WeatherData 41 | data.ID = cityId 42 | 43 | url := fmt.Sprintf(BASE_URL, cityId) 44 | req, err := http.NewRequest("GET", url, nil) 45 | if err != nil { 46 | data.Error = errors.New("Error making request") 47 | out <- data 48 | wg.Done() 49 | return 50 | } 51 | 52 | client := &http.Client{} 53 | 54 | resp, err := client.Do(req) 55 | if err != nil { 56 | data.Error = errors.New("Error calling client") 57 | out <- data 58 | wg.Done() 59 | return 60 | } 61 | 62 | defer resp.Body.Close() 63 | 64 | if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { 65 | data.Error = errors.New("error decoding message") 66 | out <- data 67 | wg.Done() 68 | return 69 | } 70 | out <- data 71 | wg.Done() 72 | }(id) 73 | } 74 | 75 | go func() { 76 | wg.Wait() 77 | close(out) 78 | }() 79 | return out 80 | 81 | } 82 | 83 | func convertWeatherData(weatherData <-chan WeatherData) <-chan WeatherData { 84 | out := make(chan WeatherData) 85 | 86 | go func() { 87 | for data := range weatherData { 88 | for _, day := range data.Forecast { 89 | day.MaxTemp = conversionCtoF(day.MaxTemp) 90 | day.MinTemp = conversionCtoF(day.MinTemp) 91 | } 92 | out <- data 93 | } 94 | 95 | fmt.Println("out close") 96 | close(out) 97 | }() 98 | 99 | fmt.Println("out !!!!") 100 | return out 101 | } 102 | 103 | func conversionCtoF(temp float64) float64 { 104 | return temp*1.8 + 32 105 | } 106 | -------------------------------------------------------------------------------- /proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func Test_UserListProxy(t *testing.T) { 9 | someDatabase := UserList{} 10 | 11 | rand.Seed(2342342) 12 | for i := 0; i < 1000000; i++ { 13 | n := rand.Int31() 14 | someDatabase = append(someDatabase, User{ID: n}) 15 | } 16 | 17 | proxy := UserListProxy{ 18 | SomeDatabase: someDatabase, 19 | StackCache: UserList{}, 20 | StackCapacity: 2, 21 | } 22 | 23 | knownIDs := [3]int32{someDatabase[3].ID, someDatabase[4].ID, someDatabase[5].ID} 24 | 25 | t.Run("FindUser - Empty cache", func(t *testing.T) { 26 | user, err := proxy.FindUser(knownIDs[0]) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | if user.ID != knownIDs[0] { 31 | t.Error("Returned user name doesn't match with expected") 32 | } 33 | 34 | if len(proxy.StackCache) != 1 { 35 | t.Error("After one successful search in an empty cache, the size of it must be one") 36 | } 37 | 38 | if proxy.DidLastSearchUsedCache { 39 | t.Error("No user can be returned from an empty cache") 40 | } 41 | }) 42 | 43 | t.Run("FindUser - One user, ask for the same user", func(t *testing.T) { 44 | user, err := proxy.FindUser(knownIDs[0]) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | if user.ID != knownIDs[0] { 50 | t.Error("Returned user name doesn't match with expected") 51 | } 52 | 53 | if len(proxy.StackCache) != 1 { 54 | t.Error("Cache must not grow if we asked for an object that is stored on it") 55 | } 56 | 57 | if !proxy.DidLastSearchUsedCache { 58 | t.Error("The user should have been returned from the cache") 59 | } 60 | }) 61 | 62 | t.Run("overflowing the stack", func(t *testing.T) { 63 | user1, err := proxy.FindUser(knownIDs[0]) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | user2, _ := proxy.FindUser(knownIDs[1]) 69 | if proxy.DidLastSearchUsedCache { 70 | t.Error("The user wasn't stored on the proxy cache yet") 71 | } 72 | 73 | user3, _ := proxy.FindUser(knownIDs[2]) 74 | if proxy.DidLastSearchUsedCache { 75 | t.Error("The user wasn't stored on the proxy cache yet") 76 | } 77 | 78 | for i := 0; i < len(proxy.StackCache); i++ { 79 | if proxy.StackCache[i].ID == user1.ID { 80 | t.Error("User that should be gone was found") 81 | } 82 | } 83 | 84 | if len(proxy.StackCache) != 2 { 85 | t.Error("After inserting 3 users the cache should not grow" + 86 | " more than to two") 87 | } 88 | 89 | for _, v := range proxy.StackCache { 90 | if v != user2 && v != user3 { 91 | t.Error("A non expected user was found on the cache") 92 | } 93 | } 94 | }) 95 | 96 | } 97 | -------------------------------------------------------------------------------- /barrier_concurrency/barrier_test.go: -------------------------------------------------------------------------------- 1 | package barrier_concurrency 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "strings" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | var timeoutMilliseconds int = 5000 16 | 17 | type barrierResp struct { 18 | Err error 19 | Resp string 20 | } 21 | 22 | func TestBarrier(t *testing.T) { 23 | t.Run("Correct endpoints", func(t *testing.T) { 24 | endpoints := []string{"http://httpbin.org/headers", "http://httpbin.org/User-Agent"} 25 | 26 | result := captureBarrierOutput(endpoints...) 27 | if !strings.Contains(result, "Accept-Encoding") || strings.Contains(result, "User-Agent") { 28 | t.Fail() 29 | } 30 | 31 | t.Log(result) 32 | }) 33 | 34 | t.Run("One endpoint incorrect", func(t *testing.T) { 35 | endpoints := []string{"http://malformed-url", "http://httpbin.org/User-Agent"} 36 | result := captureBarrierOutput(endpoints...) 37 | 38 | if !strings.Contains(result, "ERROR") { 39 | t.Fail() 40 | } 41 | 42 | t.Log(result) 43 | }) 44 | 45 | t.Run("Very short timeout", func(t *testing.T) { 46 | endpoints := []string{"http://httpbin.org/headers", "http://httpbin.org/User-Agent"} 47 | 48 | timeoutMilliseconds = 1 49 | result := captureBarrierOutput(endpoints...) 50 | if !strings.Contains(result, "Timeout") { 51 | t.Fail() 52 | } 53 | t.Log(result) 54 | }) 55 | 56 | } 57 | 58 | func barrier(endpoints ...string) { 59 | requestNumber := len(endpoints) 60 | 61 | in := make(chan barrierResp, requestNumber) 62 | defer close(in) 63 | 64 | responses := make([]barrierResp, requestNumber) 65 | 66 | for _, endpoint := range endpoints { 67 | go makeRequest(in, endpoint) 68 | } 69 | 70 | var hasError bool 71 | for i := 0; i < requestNumber; i++ { 72 | resp := <-in 73 | if resp.Err != nil { 74 | fmt.Println("ERROR: ", resp.Err) 75 | hasError = true 76 | } 77 | responses[i] = resp 78 | } 79 | 80 | if !hasError { 81 | for _, resp := range responses { 82 | fmt.Println(resp.Resp) 83 | } 84 | } 85 | } 86 | 87 | func captureBarrierOutput(endpoints ...string) string { 88 | reader, writer, _ := os.Pipe() 89 | 90 | os.Stdout = writer 91 | out := make(chan string) 92 | 93 | go func() { 94 | var buf bytes.Buffer 95 | io.Copy(&buf, reader) 96 | out <- buf.String() 97 | }() 98 | 99 | barrier(endpoints...) 100 | writer.Close() 101 | 102 | temp := <-out 103 | return temp 104 | } 105 | 106 | func makeRequest(out chan<- barrierResp, url string) { 107 | res := barrierResp{} 108 | client := http.Client{ 109 | Timeout: time.Duration(time.Duration(timeoutMilliseconds) * time.Millisecond), 110 | } 111 | 112 | resp, err := client.Get(url) 113 | if err != nil { 114 | res.Err = err 115 | out <- res 116 | return 117 | } 118 | 119 | byt, err := ioutil.ReadAll(resp.Body) 120 | if err != nil { 121 | res.Err = err 122 | out <- res 123 | return 124 | } 125 | 126 | res.Resp = string(byt) 127 | out <- res 128 | } 129 | -------------------------------------------------------------------------------- /facade/facade.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | ) 11 | 12 | type CurrentWeatherDataRetriever interface { 13 | GetByCityAndCountryCode(city, countryCode string) (Weather, error) 14 | GetByGeoCoordinates(lat, lon float32) (Weather, error) 15 | } 16 | 17 | type Weather struct { 18 | ID int `json:"id"` 19 | Name string `json:"name"` 20 | Cod int `json:"cod"` 21 | Coord struct { 22 | Lon float32 `json:"lon"` 23 | Lat float32 `json:"lat"` 24 | } `json:"coord"` 25 | 26 | Weather []struct { 27 | Id int `json:"id"` 28 | Main string `json:"main"` 29 | Description string `json:"description"` 30 | Icon string `json:"icon"` 31 | } `json:"weather"` 32 | 33 | Base string `json:"base"` 34 | Main struct { 35 | Temp float32 `json:"temp"` 36 | Pressure float32 `json:"pressure"` 37 | Humidity float32 `json:"humidity"` 38 | TempMin float32 `json:"temp_min"` 39 | TempMax float32 `json:"temp_max"` 40 | } `json:"main"` 41 | 42 | Wind struct { 43 | Speed float32 `json:"speed"` 44 | Deg float32 `json:"deg"` 45 | } `json:"wind"` 46 | 47 | Clouds struct { 48 | All int `json:"all"` 49 | } `json:"clouds"` 50 | 51 | Rain struct { 52 | ThreeHours float32 `json:"3h"` 53 | } `json:"rain"` 54 | 55 | Dt uint32 `json:"dt"` 56 | Sys struct { 57 | Type int `json:"type"` 58 | ID int `json:"id"` 59 | Message float32 `json:"message"` 60 | Country string `json:"country"` 61 | Sunrise int `json:"sunrise"` 62 | Sunset int `json:"sunset"` 63 | } `json:"sys"` 64 | } 65 | 66 | type CurrentWeatherData struct { 67 | APIkey string 68 | } 69 | 70 | func (c *CurrentWeatherData) GetByGeoCoordinates(lat, lon float32) (weather *Weather, err error) { 71 | return c.doRequest( 72 | fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather q=%f,%f&APPID=%s", lat, lon, c.APIkey)) 73 | } 74 | 75 | func (c *CurrentWeatherData) GetByCityAndCountryCode(city, countryCode string) (weather *Weather, err error) { 76 | return c.doRequest( 77 | fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&APPID=%s", city, countryCode, c.APIkey)) 78 | } 79 | 80 | func getMockData() io.Reader { 81 | response := `{ 82 | "coord":{"lon":-3.7,"lat":40.42},"weather" : [{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":303.56,"pressure":1016.46,"humidity":26.8,"temp_min":300.95,"temp_max":305.93},"wind":{"speed":3.17,"deg":151.001},"rain":{"3h":0.0075},"clouds":{"all":68},"dt":1471295823,"sys":{"type":3,"id":1442829648,"message":0.0278,"country":"ES","sunrise":1471238808,"sunset":1471288232},"id":3117735,"name":"Madrid","cod":200}` 83 | 84 | r := bytes.NewReader([]byte(response)) 85 | return r 86 | } 87 | 88 | func (p *CurrentWeatherData) responseParser(body io.Reader) (*Weather, error) { 89 | w := new(Weather) 90 | err := json.NewDecoder(body).Decode(w) 91 | if err != nil { 92 | return nil, err 93 | } 94 | 95 | return w, nil 96 | } 97 | 98 | func (o *CurrentWeatherData) doRequest(uri string) (weather *Weather, err error) { 99 | client := &http.Client{} 100 | req, err := http.NewRequest("GET", uri, nil) 101 | if err != nil { 102 | return 103 | } 104 | req.Header.Set("Content-Type", "application/json") 105 | resp, err := client.Do(req) 106 | if err != nil { 107 | return 108 | } 109 | 110 | if resp.StatusCode != 200 { 111 | byt, errMsg := ioutil.ReadAll(resp.Body) 112 | if errMsg == nil { 113 | errMsg = fmt.Errorf("%s", string(byt)) 114 | } 115 | err = fmt.Errorf("Status code was %d, aborting. Error message was:\n%s\n", resp.StatusCode, errMsg) 116 | 117 | return 118 | } 119 | weather, err = o.responseParser(resp.Body) 120 | resp.Body.Close() 121 | 122 | return 123 | } 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GO Design Patterns 2 | 3 | 4 | ## Creational Patterns 5 | 6 | 1. [SingleTon](https://github.com/BumwooPark/go-design-pattern/tree/master/singleton) 7 | : 글로벌하게 하나의 오브젝트에 대한 접근이 필요할 경우 8 | 9 | 2. [Builder](https://github.com/BumwooPark/go-design-pattern/tree/master/builder) 10 | : 빌더 패턴은 일반적인 구성 알고리즘을 사용하여 예측이 불가능한 object의 수를 유지하는데 도움이 됨 11 | 인터페이스의 변경이 있다면 모든 빌더에 영향을 미치고 메소드가 추가 될경우 어색할수 있기에 해당 알고리즘이 안정될 것을 확신하지 못한다면 이패턴을 피하자 12 | 13 | 3. [Factory Method](https://github.com/BumwooPark/go-design-pattern/tree/master/factory_method) 14 | :객체 생성 처리를 서브 클래스로 분리 해 처리하도록 캡슐화하는 패턴 15 | 즉, 객체의 생성 코드를 별도의 클래스/메서드로 분리함으로써 객체 생성의 변화에 대비하는 데 유용하다. 16 | 17 | 18 | 4. [Abstract Factory](https://github.com/BumwooPark/go-design-pattern/tree/master/abstract_factory) 19 | : factory method는 `Factory Method`보다 좀더 큰 범주에 속함 `Factory Method` 가 객체를 반환한다면 20 | `Abstract Factory`는 `factory` 자체를 반환하고 그 `factory`를 통해 객체를 받아서 처리를 한다. 21 | 22 | 5. [Object Pool](https://github.com/BumwooPark/go-design-pattern/tree/master/object_pool) 23 | :객체를 필요로 할때 풀에 요청을 하고, 반환하고 일련의 작업을 수행하는 패턴. 24 | 많은 수의 인스턴스를 생성할때 혹은 무거운 오브젝트를 매번 인스턴스화 할때 성능 향상을 가져오기도 합니다. 25 | 예를들어, 데이터베이스에 접속하는 여러 객체를 만들때 매번 새로 생성하는 것보단, 26 | 미리 생성된 풀에서 객체를 반환받아오는 것이 더 이득 입니다. 27 | 이런 문제로 JDBC 에서는 JDBC Connection Pool 을 제공하고 있으며 Thread Pool 역시 오브젝트 풀이 기본 원리 입니다. 28 | 29 | 30 | 출처: https://creatordev.tistory.com/73 [Creator Developer] 31 | 32 | 6. [ProtoType Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/prototype) 33 | 34 | 35 | ## Structural Patterns 36 | 37 | 1. [Composite Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/composite) 38 | : 트리구조의 객체 계층구조를 만듬 상속과 다중상속의 문제를 해결함 즉 열차를 타고 수영하는 수영선수를 만들경우 39 | 40 | 2. [Adapter Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/adapter) 41 | : 공개/폐쇄 원칙을 유지 하며 코드가 새로운 기능에는 개방 되어있고 수정에는 폐쇄되어있는 방식 레거시 코드를 새로운코드로 연결 하는 방식 42 | 대표적으로 Go 에서는 http.HandlerFunc , ServeHTTP 등을 찾아보고 HandlerFunc의 경우 함수로 interface를 구현하는 신선한 방식 43 | 두개의 호환되지 않는 44 | 45 | 3. [Bridge Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/bridge) 46 | : 브릿지 패턴의 목적은 자주 변경되는 구조체에 유연성을 가져오는 것 , 메서드의 입력과 출력을 알면 코드를 많이 모르는 상태에서 코드를 쉽게 수정이 가능하다 47 | 48 | 4. [Proxy Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/proxy) 49 | : 사용자에게 권한을 부여하거나 데이터베이스에 대한 액세스 권한을 부여하는 것과 같은 중간 작업이 필요한 유형을 중심으로 프록시를 래핑하는것이 좋음 (캐시 구현) 50 | 51 | 5. [Decorator Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/decorator) 52 | : 런타임시 추가적인 기능을 붙일때 주로 쓰이는 부분이 서버의 미들웨어 부분 53 | 54 | 6. [Flyweight Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/flyweight) 55 | : 데이터를 공유하여 메모리를 절약하는 패턴으로 공통으로 사용되는 객체는 한 번만 생성되고 공유를 통해 풀(Pool)에 의해 관리, 사용된다. 56 | 57 | 58 | ## Behavioral Patterns 59 | 60 | 1. [Strategy Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/strategy) 61 | : 특정 기능을 구현하기 위한 몇 가지 알고리즘 제공 , 모든 유형이 동일한 기능을 다른 방식으로 실행하지만 클라이언트는 영향을 받지 않는다. 62 | factory 패턴과 go의 struct 임베딩 + 인터페이스 덕타이핑 63 | a 구조체에 b 구조체를 임베딩 할 경우 a 에 속해있는 것처럼 b필드에 엑세스가 가능하다 이부분이 상속과 비슷 하지만 차이점이 있는데 64 | b에 해당하는 함수가 필요할경우 a를 전달할 수 없다 하지만 b가 구현하는 인터페이스를 받아들이는 함수가 있다면 a를 전달 할 수 있다. 65 | ![image](https://github.com/BumwooPark/go-design-pattern/blob/master/strategy/strategy.jpg?raw=true) 66 | 67 | 2. [Chain Responsibility Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/chain_responsibility) 68 | : 각 채인은 단일책임 원칙을 준수한다, 해당 패턴은 로깅체인을 구현하는것으로 프로그램의 출력을 둘 이상의 io.Writer인터페이스에 쓰는 것이다. 69 | 로깅을 할때 콘솔/파일/remote 서버에 남기는 기능이 있을때 3번의 함수콜을 하는것보다 체이닝을 통해서 한번에 처리하는게 더 유용하다 70 | behavior와 state의 처리를 런타임에 하는 것으로 FSM(Finite State Machine)을 만드는데 널리 사용이 된다. 71 | 주의할점은 체이닝중 중간에 실패할경우에 대한 처리가 필요 72 | 73 | 3. [Command Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/command) 74 | : 커맨드 패턴(Command pattern)이란 요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다. 75 | 커맨드 패턴에는 명령(command), 수신자(receiver), 발동자(invoker), 클라이언트(client)의 네개의 용어가 항상 따른다. 커맨드 객체는 수신자 객체를 가지고 있으며, 수신자의 메서드를 호출하고, 76 | 이에 수신자는 자신에게 정의된 메서드를 수행한다. 커맨드 객체는 별도로 발동자 객체에 전달되어 명령을 발동하게 한다. 발동자 객체는 필요에 따라 명령 발동에 대한 기록을 남길 수 있다. 77 | 한 발동자 객체에 다수의 커맨드 객체가 전달될 수 있다. 클라이언트 객체는 발동자 객체와 하나 이상의 커맨드 객체를 보유한다. 78 | 클라이언트 객체는 어느 시점에서 어떤 명령을 수행할지를 결정한다. 명령을 수행하려면, 클라이언트 객체는 발동자 객체로 커맨드 객체를 전달한다. 79 | 80 | 4. [Template Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/template) 81 | : 템플릿 패턴은 라이브러리와 프레임 웍을 개발할때 많이 쓰이는 패턴으로 사용자에게 해당 인터페이스를 제공하고(탬플릿) 인터페이스만 구현하면 82 | 뒤의 알고리즘실행시 라이브러리에서 해당 인터페이스를 콜하는 방식으로 83 | swift에서의 정렬시에 Equatable 프로토콜을 적용해야하는것과 golang에서의 sort 함수를 쓸때 Len/Swap/Less 가 포함 되어있는 소트 인터페이스를 84 | 구현하고 구현된 객체를 sort.Sort()함수로 넘겨주면 알아서 해주는것이 관건 85 | 86 | 5. [Memento Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/memento) 87 | : 객체의 상태를 저장해두었다가 복원해야 될 경우 사용 Memento는 state의 추상화 객체이고 Originator는 매멘토를 생성 및 추출 작업을 한다 88 | careTaker는 메멘토를 저장해두는 객체로 트랜젝션등을 사용 할 수있다. 89 | 90 | 91 | 6. [Visitor Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/visitor) 92 | : 알고리즘을 객체 구조에서 분리를 시키는 디자인 패턴이다, 구조는 변하지 않고 기능만 따로 추가되거나 확장 되어야 할 경우 사용되는 패턴 93 | 객체의 accept 인터페이스를 적용하고 accept내부에서 visitable 인터페이스의 visit 함수에 해당 객체를 주입함으로 알고리즘을 실행 94 | 개방 폐쇄 원칙을 적용한 디자인패턴이다 95 | 96 | 7. [State Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/state) 97 | : 객체의 내부 상태가 바뀜에 따라 객체의 행동을 바꾼다 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있음 98 | context객체내부에 state관련 인터페이스를 저장후에 해당 state의 execute하는것으로 state를 변경한다 상태관련 중요성이 있을경우 99 | state 패턴을 해보는게 좋아보인다. 100 | 101 | 102 | ## Concurrency Patterns 103 | 104 | 1. [Barrier Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/barrier_concurrency) 105 | : 예로써 한서비스에서 3가지의 마이크로 서비스의 응답을 종합하여 응답을 해주어야 할 경우 3가지 모두가 완료가 되어야 리턴이 가능하다 106 | 이때 이패턴이 유용하게 작용할 수 있다. 즉 blocking을 하는것 모든 응답이 반환될때까지 107 | 108 | ![image](https://github.com/BumwooPark/go-design-pattern/blob/master/barrier_concurrency/image.jpg?raw=true) 109 | 110 | 2. [Future Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/future) 111 | : 비동기 프로그래밍을 수행하는 좋은 방법으로 success와 fail 콜백을 받아 놓고 execute를 실행하여 execute내에서 success와 에러를 판단하여 112 | 해당 콜백을 실행시키는 방법이다 또한 success function내에 또다른 future를 놓고 이후에 실행이 가능하도록 구성이 가능하다 113 | ![image](https://github.com/BumwooPark/go-design-pattern/blob/master/future/future.jpg?raw=true) 114 | 115 | 3. [PipeLine Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/pipeline) 116 | : 채널을 사용하여 동기화가 수행이됨 117 | 118 | 4. [Worker pool Pattern](https://github.com/BumwooPark/go-design-pattern/tree/master/worker_pool) 119 | : 정해진 갯수의 워커를 생성해서 dispatcher에 할당을 하고 채널로써 업무를 받는다. 이때 파이프라인패턴과 연동하여 사용하면 좋다 120 | 121 | 122 | ## Concurrency Channel Patterns 123 | 124 | 1. [or-done channel]() 125 | 126 | 2. [tee channel]() --------------------------------------------------------------------------------