├── LICENSE ├── Makefile ├── README.md ├── antipattern ├── README.md └── singleton │ ├── README.md │ ├── singleton.go │ └── singleton_test.go ├── behavioral ├── README.md ├── chain │ ├── README.md │ ├── chain.go │ └── chain_test.go ├── command │ ├── README.md │ ├── command.go │ ├── command_test.go │ └── main.go ├── interpreter │ ├── README.md │ ├── interpreter.go │ └── interpreter_test.go ├── mediator │ ├── README.md │ ├── classmate.go │ ├── interfaces.go │ ├── mediator.go │ ├── mediator_test.go │ └── teacher.go ├── memento │ ├── README.md │ ├── memento.go │ └── memento_test.go ├── observer │ ├── README.md │ └── observer_test.go ├── state │ ├── README.md │ ├── main.go │ ├── state.go │ └── state_test.go ├── strategy │ ├── README.md │ ├── main.go │ ├── strategy.go │ └── strategy_test.go ├── template │ ├── README.md │ ├── template.go │ └── template_test.go └── visitor │ ├── README.md │ ├── visitor.go │ └── visitor_test.go ├── concurrency ├── README.md ├── barrier │ ├── README.md │ ├── barrier.go │ └── barrier_test.go ├── future │ ├── README.md │ └── future.go └── pipeline │ ├── README.md │ └── pipeline.go ├── creational ├── README.md ├── abstract_factory │ ├── README.md │ ├── factory.go │ └── factory_test.go ├── builder │ ├── README.md │ ├── builder.go │ └── builder_test.go ├── factory │ ├── README.md │ ├── factory.go │ └── factory_test.go ├── pool │ ├── README.md │ ├── pool.go │ └── pool_test.go └── prototype │ ├── README.md │ ├── prototype.go │ └── prototype_test.go ├── go.mod └── structural ├── README.md ├── adapter ├── README.md ├── adapter.go └── adapter_test.go ├── binary-tree-compositions ├── README.md └── main.go ├── bridge ├── README.md ├── bridge.go └── bridge_test.go ├── composite ├── README.md └── composite.go ├── decorator ├── README.md ├── decorator.go └── decorator_test.go ├── flyweight ├── README.md ├── flyweight.go └── flyweight_test.go └── proxy ├── README.md ├── proxy.go └── proxy_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Simone Gentili 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | go test ./antipattern/... 3 | go test ./behavioral/... 4 | go test ./concurrency/... 5 | go test ./creational/... 6 | go test ./structural/... 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Design Patterns 2 | 3 | ## [Antipattern](antipattern) :no_entry: 4 | 5 | * [Singleton](antipattern/singleton) [:notebook:](http://en.wikipedia.org/wiki/Singleton_pattern) 6 | 7 | ## [Behavioral](behavioral) 8 | 9 | * [Chain of responsiblity](behavioral/chain) [:notebook:](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) 10 | * [Command](behavioral/command) [:notebook:](https://en.wikipedia.org/wiki/Command_pattern) 11 | * [Interpreter](behavioral/interpreter) [:notebook:](https://en.wikipedia.org/wiki/Interpreter_pattern) 12 | * [Mediator](behavioral/mediator) [:notebook:](https://en.wikipedia.org/wiki/Mediator_pattern) 13 | * [Memento](behavioral/memento) [:notebook:](https://en.wikipedia.org/wiki/Memento_pattern) 14 | * [Observer](behavioral/observer) [:notebook:](https://en.wikipedia.org/wiki/Observer_pattern) 15 | * [State](behavioral/state) [:notebook:](https://en.wikipedia.org/wiki/State_pattern) 16 | * [Strategy](behavioral/strategy) [:notebook:](https://en.wikipedia.org/wiki/Strategy_pattern) 17 | * [Template](behavioral/template) [:notebook:](https://en.wikipedia.org/wiki/Template_pattern) 18 | * [Visitor](behavioral/visitor) [:notebook:](https://en.wikipedia.org/wiki/Visitor_pattern) 19 | 20 | ## [Concurrency](concurrency) 21 | 22 | * [Barrier](concurrency/barrier) [:notebook:](https://en.wikipedia.org/wiki/Barrier_(computer_science)) 23 | * [Future](concurrency/future) [:notebook:](https://en.wikipedia.org/wiki/Futures_and_promises) 24 | * [Pipeline](concurrency/pipeline) [:notebook:](https://en.wikipedia.org/wiki/Pipeline_(software)) 25 | 26 | ## [Creational](creational) 27 | 28 | * [Abstract Factory method](creational/abstract_factory) [:notebook:](http://en.wikipedia.org/wiki/Abstract_Factory_pattern) 29 | * [Builder](creational/builder) [:notebook:](http://en.wikipedia.org/wiki/Builder_pattern) 30 | * [Factory method](creational/factory) [:notebook:](http://en.wikipedia.org/wiki/Factory_pattern) 31 | * [Object Pool](creational/pool) [:notebook:](http://en.wikipedia.org/wiki/Object_Pool_pattern) 32 | * [Prototype](creational/prototype) [:notebook:](http://en.wikipedia.org/wiki/Prototype_pattern) 33 | 34 | ## [Structural](structural) 35 | 36 | * [Adapter](structural/adapter) [:notebook:](https://en.wikipedia.org/wiki/Adapter_pattern) 37 | * [Binary Tree compositions](structural/binary-tree-compositions) [:notebook:](https://en.wikipedia.org/wiki/Binary_tree) 38 | * [Bridge](structural/bridge) [:notebook:](https://en.wikipedia.org/wiki/Bridge_pattern) 39 | * [Composite](structural/composite) [:notebook:](http://en.wikipedia.org/wiki/Composite_pattern) 40 | * [Decorator](structural/decorator) [:notebook:](https://en.wikipedia.org/wiki/Decorator_pattern) 41 | * [Flyweight](structural/flyweight) [:notebook:](https://en.wikipedia.org/wiki/Flyweight_pattern) 42 | * [Proxy](structural/proxy) [:notebook:](https://en.wikipedia.org/wiki/Proxy_pattern) 43 | -------------------------------------------------------------------------------- /antipattern/README.md: -------------------------------------------------------------------------------- 1 | # Antipattern :no_entry: 2 | 3 | An anti-pattern is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive. 4 | 5 | ## Creational 6 | 7 | * [Singleton](singleton) [:notebook:](http://en.wikipedia.org/wiki/Singleton_pattern) 8 | -------------------------------------------------------------------------------- /antipattern/singleton/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Singleton 2 | 3 | ## Description 4 | 5 | The Singleton pattern aims to provide same instance of an object and guarantee 6 | that there are no duplicates. At the first call the instance is created. 7 | Following calls return first instance. 8 | 9 | ```go 10 | firstInstance := GetInstance() 11 | 12 | secondInstance := GetInstance() 13 | if firstInstance != secondInstance { 14 | t.Error("expected same instance") 15 | } 16 | ``` 17 | 18 | Any following calls, update first instance. 19 | 20 | ```go 21 | thirdInstance := GetInstance() 22 | if thirdInstance.NumberOfCalls() != 3 { 23 | t.Error("expected three calls") 24 | } 25 | ``` 26 | 27 | ## Few words … 28 | 29 | Is not good pattern if used to bring the state of the application and can 30 | change during its life cycle. Making something global to avoid passing it 31 | around is a code smell. But use it to read configuration is good. Used to load 32 | a resource just first time is requested and to provide that resource every were 33 | is a good way to use this pattern. 34 | -------------------------------------------------------------------------------- /antipattern/singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | type singleton struct { 4 | calls int 5 | creations int 6 | } 7 | 8 | var instance *singleton 9 | 10 | func GetInstance() *singleton { 11 | if instance == nil { 12 | instance = new(singleton) 13 | instance.creations = 1 14 | } 15 | instance.calls++ 16 | return instance 17 | } 18 | 19 | func (s *singleton) NumberOfCalls() int { 20 | return s.calls 21 | } 22 | 23 | func (s *singleton) NumberOfCreations() int { 24 | return s.creations 25 | } 26 | -------------------------------------------------------------------------------- /antipattern/singleton/singleton_test.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "testing" 4 | 5 | func TestGetInstance(t *testing.T) { 6 | firstInstance := GetInstance() 7 | 8 | if firstInstance.NumberOfCreations() != 1 { 9 | t.Error("expected just one number of creations") 10 | } 11 | 12 | secondInstance := GetInstance() 13 | if firstInstance != secondInstance { 14 | t.Error("expected same instance") 15 | } 16 | 17 | thirdInstance := GetInstance() 18 | if thirdInstance.NumberOfCalls() != 3 { 19 | t.Error("expected three calls") 20 | } 21 | 22 | if firstInstance.NumberOfCreations() != 1 { 23 | t.Error("expected just one number of creations") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral patterns 2 | 3 | We are going to deal with behaviors instead of define structures or encapsulate object creation. 4 | 5 | ## Behavioral 6 | 7 | * [Chain of responsiblity](chain) [:notebook:](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) 8 | * [Command](command) [:notebook:](https://en.wikipedia.org/wiki/Command_pattern) 9 | * [Interpreter](interpreter) [:notebook:](https://en.wikipedia.org/wiki/Interpreter_pattern) 10 | * [Mediator](mediator) [:notebook:](https://en.wikipedia.org/wiki/Mediator_pattern) 11 | * [Memento](memento) [:notebook:](https://en.wikipedia.org/wiki/Memento_pattern) 12 | * [Observer](observer) [:notebook:](https://en.wikipedia.org/wiki/Observer_pattern) 13 | * [State](state) [:notebook:](https://en.wikipedia.org/wiki/State_pattern) 14 | * [Strategy](strategy) [:notebook:](https://en.wikipedia.org/wiki/Strategy_pattern) 15 | * [Template](template) [:notebook:](https://en.wikipedia.org/wiki/Template_pattern) 16 | * [Visitor](visitor) [:notebook:](https://en.wikipedia.org/wiki/Visitor_pattern) 17 | -------------------------------------------------------------------------------- /behavioral/chain/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Chain of responsibility 2 | 3 | ## Elements 4 | 5 | - Handler - an interface for requests handling 6 | - RequestHandler - handles requests it is responsible for 7 | - Client - sends commands to the first object in the chain that may handle the command 8 | 9 | ## Description 10 | 11 | It consists of a source of command objects and a series of processing objects. 12 | Each processing object contains logic that defines the types of command objects 13 | that it can handle. The rest are passed to the next processing object in the 14 | chain. 15 | 16 | Is an object oriented version of the if ... else if ... else if ....... else 17 | ... endif idiom, with the benefit that the condition–action blocks can be 18 | dynamically rearranged and reconfigured at runtime. 19 | 20 | ## Implementation 21 | 22 | In this example the program aims to find right translation for a particular 23 | word. Languages available are English, French and Spanish. The chain is 24 | represented by the list of translator. Each translator knows who's next 25 | translator. Provide Next Translator in list. Expose a method to tell if it 26 | knows or not a word. Finally, … provide translation of a known word. 27 | 28 | Here the interface of each translator: EnglishTranslator, SpanishTranslator and 29 | FrenchTranslator. 30 | 31 | ```go 32 | type TranslatorRing interface { 33 | Next(TranslatorRing) 34 | GetNext() TranslatorRing 35 | KnowsWord(s string) bool 36 | TranslationOf(s string) string 37 | } 38 | ``` 39 | 40 | Here an example of concrete translator. We have a struct that known who's next. 41 | Assign next translator. Provide next translator and translated word. As 42 | example, EnglishTranslator could be the following. 43 | 44 | ```go 45 | type EnglishTranslator struct { 46 | next TranslatorRing 47 | } 48 | 49 | func (fr *EnglishTranslator) Next(r TranslatorRing) { 50 | fr.next = r 51 | } 52 | 53 | func (fr *EnglishTranslator) GetNext() TranslatorRing { 54 | return fr.next 55 | } 56 | 57 | func (fr *EnglishTranslator) KnowsWord(s string) bool { 58 | dict := NewEnglishDict() 59 | if _, ok := dict[s]; ok { 60 | return true 61 | } 62 | return false 63 | } 64 | 65 | func (fr *EnglishTranslator) TranslationOf(s string) string { 66 | dict := NewEnglishDict() 67 | if val, ok := dict[s]; ok { 68 | return val 69 | } 70 | panic("Oops!") 71 | } 72 | 73 | func NewEnglishDict() map[string]string { 74 | dict := map[string]string{} 75 | dict["topo"] = "mouse" 76 | dict["cocomero"] = "watermelon" 77 | return dict 78 | } 79 | ``` 80 | 81 | Finally, we have a client that works with the entire chain. 82 | 83 | ```go 84 | type TranslatorChain struct { 85 | Start TranslatorRing 86 | } 87 | 88 | func (h *TranslatorChain) Translate(s string) string { 89 | r := h.Start 90 | for { 91 | if r.KnowsWord(s) { 92 | return r.TranslationOf(s) 93 | } 94 | if r.GetNext() != nil { 95 | r = r.GetNext() 96 | } else { 97 | log.Fatal("No translation found") 98 | } 99 | } 100 | } 101 | 102 | func (h *TranslatorChain) CountRings() int { 103 | numOfRings := 0 104 | if h.Start != nil { 105 | numOfRings++ 106 | } 107 | r := h.Start 108 | for r.GetNext() != nil { 109 | r = r.GetNext() 110 | numOfRings++ 111 | } 112 | return numOfRings 113 | 114 | } 115 | ``` 116 | 117 | A concrete example could be the following. Only spanish dictionary known 118 | translation of "ciao". Only french translator knows translation of word 119 | "casa". But what if both english and spanish vocabulary knowns word 120 | "cocomero"? Because of the EnglishTranslator have more priority in the chain, 121 | .. `chain.Translate("cocomero")` will return english word "watermelon" and 122 | not spanish "sandía". 123 | 124 | ```go 125 | func main() { 126 | chain := TranslatorChain{ 127 | Start: &EnglishTranslator{ 128 | &FrenchTranslator{ 129 | &SpanishTranslator{}, 130 | }, 131 | }, 132 | } 133 | 134 | fmt.Println("vim-go") 135 | 136 | fmt.Println(strconv.Itoa(chain.CountRings())) // 3 137 | fmt.Println(chain.Translate("ciao")) // spanish: hola 138 | fmt.Println(chain.Translate("casa")) // french: maison 139 | fmt.Println(chain.Translate("topo")) // english mouse 140 | fmt.Println(chain.Translate("cocomero")) // english mouse 141 | } 142 | ``` 143 | -------------------------------------------------------------------------------- /behavioral/chain/chain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type TranslatorRing interface { 9 | Next(TranslatorRing) 10 | GetNext() TranslatorRing 11 | KnowsWord(s string) bool 12 | TranslationOf(s string) string 13 | } 14 | 15 | type EnglishTranslator struct { 16 | next TranslatorRing 17 | } 18 | 19 | func (fr EnglishTranslator) Next(r TranslatorRing) { 20 | fr.next = r 21 | } 22 | 23 | func (fr EnglishTranslator) GetNext() TranslatorRing { 24 | return fr.next 25 | } 26 | 27 | func (fr EnglishTranslator) KnowsWord(s string) bool { 28 | dict := ItalianToEnglishDictionary() 29 | if _, ok := dict[s]; ok { 30 | return true 31 | } 32 | return false 33 | } 34 | 35 | func (fr EnglishTranslator) TranslationOf(s string) string { 36 | dict := ItalianToEnglishDictionary() 37 | if val, ok := dict[s]; ok { 38 | return val 39 | } 40 | panic("Oops! Wrong translation requested") 41 | } 42 | 43 | func ItalianToEnglishDictionary() map[string]string { 44 | dict := map[string]string{} 45 | dict["topo"] = "mouse" 46 | dict["cocomero"] = "watermelon" 47 | return dict 48 | } 49 | 50 | type FrenchTranslator struct { 51 | next TranslatorRing 52 | } 53 | 54 | func (sr *FrenchTranslator) Next(r TranslatorRing) { 55 | sr.next = r 56 | } 57 | 58 | func (fr *FrenchTranslator) GetNext() TranslatorRing { 59 | return fr.next 60 | } 61 | func (fr *FrenchTranslator) KnowsWord(s string) bool { 62 | dict := ItalianToFrancaisDictionary() 63 | if _, ok := dict[s]; ok { 64 | return true 65 | } 66 | return false 67 | } 68 | 69 | func (fr *FrenchTranslator) TranslationOf(s string) string { 70 | dict := ItalianToFrancaisDictionary() 71 | if val, ok := dict[s]; ok { 72 | return val 73 | } 74 | panic("Oops! Wrong translation requested") 75 | } 76 | 77 | func ItalianToFrancaisDictionary() map[string]string { 78 | dict := map[string]string{} 79 | dict["casa"] = "maison" 80 | return dict 81 | } 82 | 83 | type SpanishTranslator struct { 84 | next TranslatorRing 85 | } 86 | 87 | func (sr *SpanishTranslator) Next(r TranslatorRing) { 88 | sr.next = r 89 | } 90 | 91 | func (fr *SpanishTranslator) GetNext() TranslatorRing { 92 | return fr.next 93 | } 94 | func (fr *SpanishTranslator) KnowsWord(s string) bool { 95 | dict := ItalianToSpanishDictionary() 96 | if _, ok := dict[s]; ok { 97 | return true 98 | } 99 | panic("Oops! Wrong translation requested") 100 | } 101 | 102 | func (fr *SpanishTranslator) TranslationOf(s string) string { 103 | dict := ItalianToSpanishDictionary() 104 | if val, ok := dict[s]; ok { 105 | return val 106 | } 107 | panic("Oops! Wrong translation requested") 108 | } 109 | 110 | func ItalianToSpanishDictionary() map[string]string { 111 | dict := map[string]string{} 112 | dict["ciao"] = "hola" 113 | dict["cocomero"] = "sandía" 114 | return dict 115 | } 116 | 117 | type TranslatorChain struct { 118 | Start TranslatorRing 119 | } 120 | 121 | func (h *TranslatorChain) Translate(s string) (string, error) { 122 | r := h.Start 123 | for { 124 | if r.KnowsWord(s) { 125 | return r.TranslationOf(s), nil 126 | } 127 | if r.GetNext() != nil { 128 | r = r.GetNext() 129 | } else { 130 | panic("Oops! Missing translation") 131 | } 132 | } 133 | } 134 | 135 | func (h *TranslatorChain) CountRings() int { 136 | numOfRings := 0 137 | if h.Start != nil { 138 | numOfRings++ 139 | } 140 | r := h.Start 141 | for r.GetNext() != nil { 142 | r = r.GetNext() 143 | numOfRings++ 144 | } 145 | return numOfRings 146 | 147 | } 148 | 149 | func main() { 150 | chain := TranslatorChain{ 151 | Start: &EnglishTranslator{ 152 | &FrenchTranslator{ 153 | &SpanishTranslator{}, 154 | }, 155 | }, 156 | } 157 | 158 | fmt.Println("vim-go") 159 | 160 | fmt.Println(strconv.Itoa(chain.CountRings())) // 3 161 | fmt.Println(chain.Translate("ciao")) // spanish: hola 162 | fmt.Println(chain.Translate("casa")) // french: maison 163 | fmt.Println(chain.Translate("topo")) // english mouse 164 | fmt.Println(chain.Translate("cocomero")) // english watermelon 165 | } 166 | -------------------------------------------------------------------------------- /behavioral/chain/chain_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | import "fmt" 5 | 6 | func TestFoo(t *testing.T) { 7 | chain := TranslatorChain{ 8 | Start: &EnglishTranslator{ 9 | &FrenchTranslator{ 10 | &SpanishTranslator{}, 11 | }, 12 | }, 13 | } 14 | 15 | fmt.Println("vim-go") 16 | 17 | if chain.CountRings() != 3 { 18 | t.Error("There should be three rings") 19 | } 20 | 21 | str, _ := chain.Translate("ciao") 22 | if str != "hola" { 23 | t.Error("Translation should be kept from spanish ring") 24 | } 25 | 26 | str, _ = chain.Translate("casa") 27 | if str != "maison" { 28 | t.Error("Translation should be kept from french ring") 29 | } 30 | 31 | str, _ = chain.Translate("topo") 32 | if str != "mouse" { 33 | t.Error("Translation should be kept from english ring") 34 | } 35 | 36 | str, _ = chain.Translate("cocomero") 37 | if str != "watermelon" { 38 | t.Error("Translation should be kept from english ring") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /behavioral/command/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Command 2 | 3 | ## Elements 4 | 5 | - Command - an interface for executing operation; 6 | - ConcreteCommand - implement the Execute method; 7 | - Client - creates a ConcreteCommand; 8 | - Invoker - asks the command to carry out the request; 9 | 10 | ## Description 11 | 12 | While strategy pattern is focused on changing algorithm, in Command pattern the 13 | focus is on invocation of something or on the abstraction of some type. A command 14 | pattern is commonly seen as a container. 15 | 16 | ## Implementation 17 | 18 | This pattern requires an interface for all commands that could be executed in a 19 | certain moment. 20 | 21 | ```go 22 | type Command interface { 23 | Execute() 24 | } 25 | ``` 26 | 27 | Here first example of command: 28 | 29 | ```go 30 | type SomeCommand struct { 31 | message string 32 | } 33 | 34 | func (s *SomeCommand) Execute() { 35 | fmt.Println(s.message) 36 | } 37 | ``` 38 | 39 | And here a second one: 40 | 41 | 42 | ```go 43 | type SomeSpecialCommand struct { 44 | message string 45 | } 46 | 47 | func (s *SomeSpecialCommand) Execute() { 48 | message := "@" + s.message 49 | fmt.Println(message) 50 | } 51 | ``` 52 | 53 | In the `SomeCommand` there is a simple `Println` output. Instead, using another 54 | `SomeSpecialCommand` we also add a `@` symbol at the beginning of the string. 55 | 56 | Finally we have an invoker, a component of this pattern that: 57 | 58 | - append commands into queue; 59 | - process entire queue; 60 | 61 | ```go 62 | type CommandInvoker struct { 63 | queue []Command 64 | } 65 | 66 | func (c *CommandInvoker) processQueue() { 67 | for i := range c.queue { 68 | c.queue[i].Execute() 69 | } 70 | } 71 | 72 | func (c *CommandInvoker) addToQueue(i Command) { 73 | fmt.Println("Appending command") 74 | c.queue = append(c.queue, i) 75 | if len(c.queue) == 3 { 76 | c.processQueue() 77 | c.queue = []Command{} 78 | } 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /behavioral/command/command.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Command interface { 8 | Execute() 9 | } 10 | 11 | type SomeCommand struct { 12 | message string 13 | } 14 | 15 | func (s *SomeCommand) Execute() { 16 | fmt.Println(s.message) 17 | } 18 | 19 | type SomeSpecialCommand struct { 20 | message string 21 | } 22 | 23 | func (s *SomeSpecialCommand) Execute() { 24 | message := "@" + s.message 25 | fmt.Println(message) 26 | } 27 | 28 | type CommandInvoker struct { 29 | queue []Command 30 | processedItems int 31 | } 32 | 33 | func (c *CommandInvoker) processQueue() { 34 | for i := range c.queue { 35 | c.queue[i].Execute() 36 | c.processedItems++ 37 | } 38 | } 39 | 40 | func (c *CommandInvoker) ProcessedItems() int { 41 | return c.processedItems 42 | } 43 | 44 | func (c *CommandInvoker) addToQueue(i Command) { 45 | fmt.Println("Appending command") 46 | c.queue = append(c.queue, i) 47 | if len(c.queue) == 3 { 48 | c.processQueue() 49 | c.queue = []Command{} 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /behavioral/command/command_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestProcessZeroItemsWheneverQueueIsEmpty(t *testing.T) { 6 | c := CommandInvoker{} 7 | c.processQueue() 8 | if c.ProcessedItems() != 0 { 9 | t.Error("no items should be processed with empty queue") 10 | } 11 | } 12 | 13 | func TestCountProcessedItems(t *testing.T) { 14 | c := CommandInvoker{} 15 | c.addToQueue(&SomeCommand{"foo"}) 16 | c.processQueue() 17 | if c.ProcessedItems() != 1 { 18 | t.Error("no items should be processed with empty queue") 19 | } 20 | } 21 | 22 | func TestQueueIsProcessedWheneverContainsThreeItems(t *testing.T) { 23 | c := CommandInvoker{} 24 | 25 | c.addToQueue(&SomeCommand{"foo"}) 26 | c.addToQueue(&SomeCommand{"foo"}) 27 | if c.ProcessedItems() != 0 { 28 | t.Error("no items should be processed") 29 | } 30 | 31 | c.addToQueue(&SomeCommand{"foo"}) 32 | if c.ProcessedItems() != 3 { 33 | t.Error("three items should be processed") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /behavioral/command/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | c := CommandInvoker{} 5 | c.addToQueue(&SomeCommand{"Simone"}) 6 | c.addToQueue(&SomeCommand{"Gentili"}) 7 | c.addToQueue(&SomeSpecialCommand{"sensorario"}) 8 | } 9 | -------------------------------------------------------------------------------- /behavioral/interpreter/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Interpreter 2 | 3 | ## Elements 4 | 5 | - Interpreter: the one who interpret the language; 6 | - Language: the whole pattern we need to be interpret; 7 | 8 | ## Description 9 | 10 | The interpreter design pattern is used to solve business cases where it's 11 | useful to have a language to perform common operations. The most famous 12 | interpreter we can talk about is probably SQL. 13 | 14 | ## Implementation 15 | 16 | The implementation is trivial. In the following examples I'll show a very 17 | simple usage of interpreter. In this case we use interpreter to calculate some 18 | mathematical operations. For example the sum and the multiplication operations. 19 | 20 | ```go 21 | func TestSumOperator(t *testing.T) { 22 | sentence := "5 + 3" 23 | i := interpreter{} 24 | _ = i.of(sentence) 25 | if i.exec() != 8 { 26 | t.Error([]string{ 27 | "La somma di 5 con 3", 28 | "non dovrebbe essere", 29 | strconv.Itoa(i.exec()), 30 | }) 31 | } 32 | } 33 | ``` 34 | 35 | As you can see, given a sentence like `5 + 3` parsed, it is possible to exec 36 | the string and calculate the result. 37 | 38 | ```go 39 | func TestMulOperator(t *testing.T) { 40 | sentence := "5 * 3" 41 | i := interpreter{} 42 | _ = i.of(sentence) 43 | if i.exec() != 15 { 44 | t.Error([]string{ 45 | "Multiplication between 5 and 3", 46 | "shouldnt be", 47 | strconv.Itoa(i.exec()), 48 | }) 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /behavioral/interpreter/interpreter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type interpreter struct { 11 | sentence string 12 | } 13 | 14 | func (i *interpreter) tokens() []string { 15 | return strings.Split(i.sentence, " ") 16 | } 17 | 18 | func (i *interpreter) exec() int { 19 | sum := 0 20 | tokens := i.tokens() 21 | for k, item := range tokens { 22 | if item == "*" { 23 | fmt.Println(i.tokens()) 24 | a, _ := strconv.Atoi(string(tokens[k-1])) 25 | b, _ := strconv.Atoi(string(tokens[k+1])) 26 | return a * b 27 | } 28 | 29 | if item != "+" { 30 | number, _ := strconv.Atoi(item) 31 | sum += number 32 | } 33 | } 34 | return sum 35 | } 36 | 37 | func dict() map[string]string { 38 | var m map[string]string 39 | m = make(map[string]string) 40 | m["+"] = "plus" 41 | return m 42 | } 43 | 44 | func (i *interpreter) contains(s string) bool { 45 | m := dict() 46 | if _, ok := m[s]; ok { 47 | return true 48 | } 49 | 50 | return false 51 | } 52 | 53 | func (i *interpreter) of(s string) error { 54 | if s == "normal" { 55 | return errors.New("non va") 56 | } 57 | i.sentence = s 58 | return nil 59 | } 60 | 61 | func (i *interpreter) numberOfWords() int { 62 | s := i.tokens() 63 | return len(s) 64 | } 65 | -------------------------------------------------------------------------------- /behavioral/interpreter/interpreter_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | func TestNodesCantBeNormalString(t *testing.T) { 9 | sentence := "normal" 10 | i := interpreter{} 11 | err := i.of(sentence) 12 | if err == nil { 13 | t.Error("Si doveva spaccare") 14 | } 15 | } 16 | 17 | func TestFoo(t *testing.T) { 18 | sentence := "do something" 19 | i := interpreter{} 20 | i.of(sentence) 21 | if i.numberOfWords() != 2 { 22 | t.Error("Guarda che ti stai sbagliando") 23 | } 24 | } 25 | 26 | func TestPlusOperatorDetector(t *testing.T) { 27 | sentence := "2 + 3" 28 | i := interpreter{} 29 | i.of(sentence) 30 | if i.contains("+") != true { 31 | t.Error("dovrebbe conoscere l'operatore +") 32 | } 33 | } 34 | 35 | func TestShouldNotContainUnknownOperator(t *testing.T) { 36 | sentence := "2 + 3" 37 | i := interpreter{} 38 | i.of(sentence) 39 | if i.contains("unknown") != false { 40 | t.Error("it should not known unknown operator") 41 | } 42 | } 43 | 44 | func TestSplitSentencesInSplice(t *testing.T) { 45 | sentence := "2 + 3" 46 | i := interpreter{} 47 | i.of(sentence) 48 | expected := []string{"2", "+", "3"} 49 | for ind, _ := range expected { 50 | tok := i.tokens() 51 | if expected[ind] != tok[ind] { 52 | t.Error("invalid tokens") 53 | } 54 | } 55 | } 56 | 57 | func TestCountNumberOfOperators(t *testing.T) { 58 | sentence := "2 + 3" 59 | i := interpreter{} 60 | i.of(sentence) 61 | expected := []string{"2", "+", "3"} 62 | for ind, _ := range expected { 63 | tok := i.tokens() 64 | if expected[ind] != tok[ind] { 65 | t.Error("non ci siamo") 66 | } 67 | } 68 | } 69 | 70 | func TestSumOperator(t *testing.T) { 71 | sentence := "5 + 3" 72 | i := interpreter{} 73 | i.of(sentence) 74 | if i.exec() != 8 { 75 | t.Error([]string{ 76 | "La somma di 5 con 3", 77 | "non dovrebbe essere", 78 | strconv.Itoa(i.exec()), 79 | }) 80 | } 81 | } 82 | 83 | func TestMulOperator(t *testing.T) { 84 | sentence := "5 * 3" 85 | i := interpreter{} 86 | i.of(sentence) 87 | if i.exec() != 15 { 88 | t.Error([]string{ 89 | "Multiplication between 5 and 3", 90 | "shouldnt be", 91 | strconv.Itoa(i.exec()), 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /behavioral/mediator/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Mediator 2 | 3 | ## Elements 4 | 5 | - Mediator - interface to mediate between colleagues 6 | - Concrete Mediator - transfer messages between colleague 7 | - Colleague Class - communicate to the mediator to communicate with other object 8 | 9 | ## Description 10 | 11 | It defined an object that encapsulate the manner of how a set of objects 12 | interact each others. This patterns also solve the problem of coupling 13 | delegating to the mediator the responsibility to manipulate objects. 14 | 15 | ## Implementation 16 | 17 | In the following implementation we consider the mediator as a sort of teacher. 18 | The teacher will teaches to students and all students will learn. 19 | 20 | ```go 21 | type Student interface { 22 | Class() string 23 | Learn(message string) 24 | Learned() string 25 | } 26 | 27 | type Mediator interface { 28 | TeachesTo(c *Student) 29 | Spread(messqger string) 30 | } 31 | ``` 32 | 33 | A class mate implements the interface Student. It learn something and tell what 34 | he or she have learned. In this mediator implementation students dont speak 35 | each others. 36 | 37 | ```go 38 | type ClassMate struct { 39 | name string 40 | lastMessage string 41 | forum string 42 | } 43 | 44 | func (a *ClassMate) Class() string { 45 | return a.forum 46 | } 47 | 48 | func (a *ClassMate) Learn(message string) { 49 | a.lastMessage = message 50 | } 51 | 52 | func (a *ClassMate) Learned() string { 53 | return a.lastMessage 54 | } 55 | ``` 56 | 57 | Finally the teacher. Teach spread knowledge and decide the list of students to 58 | teach to. 59 | 60 | ```go 61 | type Teacher struct { 62 | learners []Student 63 | } 64 | 65 | func (m *Teacher) TeachesTo(c Student) { 66 | m.learners = append(m.learners, c) 67 | } 68 | 69 | func (m *Teacher) Spread(message string) { 70 | for _, a := range m.learners { 71 | a.Learn(message) 72 | } 73 | } 74 | ``` 75 | 76 | All works inside the main function. When the teacher say something, a student 77 | learn. 78 | 79 | ```go 80 | func NewClassMate(name string) Student { 81 | return &ClassMate{name, "meeting 1", ""} 82 | } 83 | 84 | func main() { 85 | 86 | teacher := Teacher{} 87 | 88 | student := NewClassMate("Mario") 89 | 90 | teacher.TeachesTo(student) 91 | teacher.TeachesTo(NewClassMate("Demo")) 92 | teacher.TeachesTo(NewClassMate("Mattia")) 93 | teacher.TeachesTo(NewClassMate("Simone")) 94 | 95 | fmt.Println(len(teacher.attendees)) 96 | fmt.Println(student.Class()) 97 | 98 | eteacher.Spread("Message sent to everyone") 99 | 100 | fmt.Println(student.Learned()) // Message sent to everyone 101 | 102 | } 103 | ``` 104 | 105 | Last but not least a test to prove that student really learned lesson! 106 | 107 | 108 | ```go 109 | func TestStudentLearn(t *testing.T) { 110 | teacher := Teacher{} 111 | student := NewClassMate("Mario") 112 | teacher.TeachesTo(student) 113 | teacher.Spread("Message sent to everyone") 114 | if student.Learned() != "Message sent to everyone" { 115 | t.Error("Student should learn") 116 | } 117 | } 118 | ``` 119 | -------------------------------------------------------------------------------- /behavioral/mediator/classmate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type ClassMate struct { 4 | name string 5 | lastMessage string 6 | forum string 7 | } 8 | 9 | func (a *ClassMate) Class() string { 10 | return a.forum 11 | } 12 | 13 | func (a *ClassMate) Learn(message string) { 14 | a.lastMessage = message 15 | } 16 | 17 | func (a *ClassMate) Learned() string { 18 | return a.lastMessage 19 | } 20 | -------------------------------------------------------------------------------- /behavioral/mediator/interfaces.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Student interface { 4 | Class() string 5 | Learn(message string) 6 | Learned() string 7 | } 8 | 9 | type Mediator interface { 10 | TeachesTo(c *Student) 11 | Spread(messqger string) 12 | } 13 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func NewClassMate(name string) Student { 6 | return &ClassMate{name, "meeting 1", ""} 7 | } 8 | 9 | func main() { 10 | 11 | teacher := Teacher{} 12 | 13 | student := NewClassMate("Mario") 14 | 15 | teacher.TeachesTo(student) 16 | teacher.TeachesTo(NewClassMate("Demo")) 17 | teacher.TeachesTo(NewClassMate("Mattia")) 18 | teacher.TeachesTo(NewClassMate("Simone")) 19 | 20 | fmt.Println(len(teacher.attendees)) 21 | fmt.Println(student.Class()) 22 | 23 | teacher.Spread("Message sent to everyone") 24 | 25 | fmt.Println(student.Learned()) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /behavioral/mediator/mediator_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStudentLearn(t *testing.T) { 8 | teacher := Teacher{} 9 | student := NewClassMate("Mario") 10 | teacher.TeachesTo(student) 11 | teacher.Spread("Message sent to everyone") 12 | if student.Learned() != "Message sent to everyone" { 13 | t.Error("Student should learn") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /behavioral/mediator/teacher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Teacher struct { 4 | attendees []Student 5 | } 6 | 7 | func (m *Teacher) TeachesTo(c Student) { 8 | m.attendees = append(m.attendees, c) 9 | } 10 | 11 | func (m *Teacher) Spread(message string) { 12 | for _, a := range m.attendees { 13 | a.Learn(message) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /behavioral/memento/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Memento 2 | 3 | ## Elements 4 | 5 | - Memento - store originator object 6 | - Originator - build/create mementos 7 | - Caretaker - keep the memento 8 | 9 | ## Description 10 | 11 | Every time we create new memento object, its state is taken from the originator 12 | element of the design pattern. 13 | 14 | ```go 15 | func TestNewMementoTakeOriginatorState(t *testing.T) { 16 | firstState := originator{} 17 | firstState.state = State{Description: "Idle"} 18 | mem := firstState.NewMemento() 19 | if mem.state.Description != "Idle" { 20 | t.Error("Expected state was not found") 21 | } 22 | } 23 | 24 | func (o *originator) NewMemento() memento { 25 | return memento{state: o.state} 26 | } 27 | ``` 28 | In the following test state is changed to "working" to ensure that new state 29 | will be written. 30 | 31 | ```go 32 | func TestStoresIdleState(t *testing.T) { 33 | originator := originator{state: State{"Idle"}} 34 | idleMemento := originator.NewMemento() 35 | originator.state.Description = "Working" 36 | originator.ExtractAndStoreState(idleMemento) 37 | if originator.state.Description != "Idle" { 38 | t.Error("Unexpected state found") 39 | } 40 | } 41 | 42 | func (o *originator) ExtractAndStoreState(m memento) { 43 | o.state = m.state 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /behavioral/memento/memento.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type State struct { 8 | Description string 9 | } 10 | 11 | type memento struct { 12 | state State 13 | } 14 | 15 | type originator struct { 16 | state State 17 | } 18 | 19 | func (o *originator) NewMemento() memento { 20 | return memento{state: o.state} 21 | } 22 | 23 | func (o *originator) ExtractAndStoreState(m memento) { 24 | o.state = m.state 25 | } 26 | 27 | type careTaker struct { 28 | mementoList []memento 29 | } 30 | 31 | func (c *careTaker) Add(m memento) { 32 | c.mementoList = append(c.mementoList, m) 33 | } 34 | 35 | func (c *careTaker) Memento(i int) (memento, error) { 36 | if i < 0 { 37 | return memento{}, fmt.Errorf("Index not found") 38 | } 39 | return c.mementoList[i], nil 40 | } 41 | -------------------------------------------------------------------------------- /behavioral/memento/memento_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNewMementoTakeOriginatorState(t *testing.T) { 8 | firstState := originator{} 9 | firstState.state = State{Description: "Idle"} 10 | mem := firstState.NewMemento() 11 | if mem.state.Description != "Idle" { 12 | t.Error("Expected state was not found") 13 | } 14 | } 15 | 16 | func TestCareTakerKeepAlMementoInsideAList(t *testing.T) { 17 | firstState := originator{} 18 | firstState.state = State{Description: "Idle"} 19 | mem := firstState.NewMemento() 20 | careTaker := careTaker{} 21 | lenBeforeAddedMemento := len(careTaker.mementoList) 22 | careTaker.Add(mem) 23 | if len(careTaker.mementoList) != lenBeforeAddedMemento+1 { 24 | t.Errorf("No new elements were added on the list") 25 | } 26 | } 27 | 28 | func TestCareTaker(t *testing.T) { 29 | originator := originator{} 30 | careTaker := careTaker{} 31 | originator.state = State{"foo"} 32 | careTaker.Add(originator.NewMemento()) 33 | mem, err := careTaker.Memento(0) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | if mem.state.Description != "foo" { 38 | t.Error("Unexpected state") 39 | } 40 | } 41 | 42 | func TestCareTakerDetectIfNonExistentIndexIsRequested(t *testing.T) { 43 | originator := originator{} 44 | careTaker := careTaker{} 45 | originator.state = State{"foo"} 46 | careTaker.Add(originator.NewMemento()) 47 | _, err := careTaker.Memento(0) 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | _, err = careTaker.Memento(-1) 52 | if err == nil { 53 | t.Fatal("An error is expected") 54 | } 55 | } 56 | 57 | func TestStoresIdleState(t *testing.T) { 58 | originator := originator{state: State{"Idle"}} 59 | idleMemento := originator.NewMemento() 60 | originator.state.Description = "Working" 61 | originator.ExtractAndStoreState(idleMemento) 62 | if originator.state.Description != "Idle" { 63 | t.Error("Unexpected state found") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /behavioral/observer/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Observer 2 | 3 | ## Elements 4 | 5 | - Observable/Subject - interface that define attach/detach operations 6 | - ConcreteObservable - maintain the state of an object and notifies attached observer 7 | - Observer - interface that defines how to notify 8 | - ConcreteObserver - the object that receive notifications 9 | 10 | ## Description 11 | 12 | This pattern uncouple an event from its possible handlers. It is useful to 13 | trigger many actions on same events. And it is useful whenever the number of 14 | action of this event grows. 15 | 16 | Key elements of this patterns are the publisher and the subscriber. The 17 | publisher will emit an event. All subscriber that handle that event are called 18 | when the particular event is triggered. 19 | 20 | ## Implementation 21 | 22 | First of all in this pattern we needs a publisher and a subscriber. The 23 | publisher will contain the list of subscribers/observers. The subsciber can be 24 | notified (it expose a method `Subscriber.Notify(string)`. 25 | 26 | ```go 27 | type Subscriber interface { 28 | Notify(string) 29 | } 30 | 31 | type Publisher struct { 32 | ObserverList []Subscriber 33 | } 34 | ``` 35 | 36 | As we do not care about the observer behavior now we will take care about some 37 | functionalities: 38 | 39 | - AddSubscriber 40 | - RemoveObserver 41 | - NotifyObservers 42 | 43 | ```go 44 | func (p *Publisher) AddSubscriber(o Subscriber) { 45 | p.ObserverList = append(p.ObserverList, o) 46 | } 47 | 48 | func (p *Publisher) RemoveObserver(o Subscriber) { 49 | var indexToRemove int 50 | for i, observer := range p.ObserverList { 51 | if observer == o { 52 | indexToRemove = i 53 | break 54 | } 55 | } 56 | p.ObserverList = append( 57 | p.ObserverList[:indexToRemove], 58 | p.ObserverList[indexToRemove+1:]..., 59 | ) 60 | } 61 | 62 | func (p *Publisher) NotifyObservers(message string) { 63 | for _, observer := range p.ObserverList { 64 | observer.Notify(message) 65 | } 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /behavioral/observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "testing" 5 | 6 | type Subscriber interface { 7 | Notify(string) 8 | } 9 | 10 | type Publisher struct { 11 | ObserverList []Subscriber 12 | } 13 | 14 | func (p *Publisher) AddSubscriber(o Subscriber) { 15 | p.ObserverList = append(p.ObserverList, o) 16 | } 17 | 18 | func (p *Publisher) RemoveObserver(o Subscriber) { 19 | var indexToRemove int 20 | for i, observer := range p.ObserverList { 21 | if observer == o { 22 | indexToRemove = i 23 | break 24 | } 25 | } 26 | p.ObserverList = append( 27 | p.ObserverList[:indexToRemove], 28 | p.ObserverList[indexToRemove+1:]..., 29 | ) 30 | } 31 | 32 | func (p *Publisher) NotifyObservers(message string) { 33 | for _, observer := range p.ObserverList { 34 | observer.Notify(message) 35 | } 36 | } 37 | 38 | type TestObserver struct { 39 | ID int 40 | Message string 41 | } 42 | 43 | func (p *TestObserver) Notify(m string) { 44 | fmt.Printf("Obderver %d: message '%s' received \n", p.ID, m) 45 | p.Message = m 46 | } 47 | 48 | func TestSubject(t *testing.T) { 49 | 50 | testObserver1 := &TestObserver{1, ""} 51 | testObserver2 := &TestObserver{2, ""} 52 | testObserver3 := &TestObserver{3, ""} 53 | publisher := Publisher{} 54 | 55 | t.Run("AddSubscriber", func(t *testing.T) { 56 | publisher.AddSubscriber(testObserver1) 57 | publisher.AddSubscriber(testObserver2) 58 | publisher.AddSubscriber(testObserver3) 59 | 60 | if len(publisher.ObserverList) != 3 { 61 | t.Fail() 62 | } 63 | }) 64 | 65 | t.Run("RemoveObserver", func(t *testing.T) { 66 | publisher.RemoveObserver(testObserver2) 67 | 68 | if len(publisher.ObserverList) != 2 { 69 | t.Fail() 70 | } 71 | }) 72 | 73 | t.Run("Notify", func(t *testing.T) { 74 | for _, observer := range publisher.ObserverList { 75 | printObserver, _ := observer.(*TestObserver) 76 | message := "hello" 77 | publisher.NotifyObservers(message) 78 | 79 | if printObserver.Message == "" { 80 | t.Error() 81 | } 82 | 83 | if printObserver.Message != message { 84 | t.Error() 85 | } 86 | } 87 | }) 88 | 89 | } 90 | -------------------------------------------------------------------------------- /behavioral/state/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » State 2 | 3 | ## Elements 4 | 5 | - Step: an interface that defines each step; 6 | - Contest: the one that know current step; 7 | - Client: the part that iterate steps; 8 | 9 | ## Description 10 | 11 | State is used to encapsulate different states behaviors of the same object. 12 | This pattern is really close to final state machine concepts but it is not its 13 | implementation. State can be seen as a strategy pattern when strategy change on 14 | object properties changes. 15 | 16 | ## Implementation 17 | 18 | In the following example I'll show you state pattern at work to implement a 19 | guess number game. 20 | 21 | Main important thing here is the following interface that represents a step of 22 | guess number game. In terms of state pattern represent a state. A state must 23 | execute the game according to game context. We will see that a step may define 24 | Context's next step and decide if the state machine is finished or not. In this 25 | implementation a step returns a boolean value. 26 | 27 | ```go 28 | type GameStep interface { 29 | Exec(k *Game) bool 30 | Name() string 31 | } 32 | ``` 33 | 34 | In main function we have a loop that will ends only when Exec function will 35 | returns false. 36 | 37 | ```go 38 | for t.CurrentStep.Exec(&t) { 39 | t.prntState() 40 | } 41 | ``` 42 | 43 | Before main function we have to analyze another struct: Game struct. This 44 | struct have the responsibility to store current step and other variable 45 | inherent of the context. In this implementation we have, for example an exit 46 | variable (and a step that decide to exit previous loop of context's exit value 47 | is true). 48 | 49 | ```go 50 | type Game struct { 51 | CurrentStep GameStep 52 | Number int 53 | Exit bool 54 | } 55 | 56 | func (a *Game) prntState() { 57 | fmt.Println(">>", a.CurrentStep.Name()) 58 | if a.CurrentStep.Name() == "end" { 59 | fmt.Println("") 60 | } 61 | } 62 | ``` 63 | 64 | Let's see first step. As we are going to implement a guess number game, we have 65 | to implement the Ask state. When this state is executed, it will ask end user 66 | to guess the random number. Here we have to pay attention onto Exec method. 67 | Here we read from standard input the value inserted by end user. 68 | 69 | In this implementation current step says to the Game's context struct if must 70 | exit or not. Is a non necessary step but it is important to see how it is easy 71 | to add another step the queue of the state. 72 | 73 | ```go 74 | type Ask struct{} 75 | 76 | func (s *Ask) Exec(k *AContext) bool { 77 | var n int 78 | fmt.Print(">> ") 79 | fmt.Fscanf(os.Stdin, "%v", &n) 80 | if n == k.Number { 81 | k.Exit = true 82 | } else { 83 | if n > k.Number { 84 | fmt.Println(">> you number is greater") 85 | } else { 86 | fmt.Println(">> you number is lower") 87 | } 88 | } 89 | k.CurrentState = &CheckState{} 90 | return true 91 | } 92 | 93 | func (s *Ask) Name() string { 94 | return "guess the number ... " 95 | } 96 | 97 | ``` 98 | 99 | In the main function we have to generate a random number. 100 | 101 | ```go 102 | rand.Seed(time.Now().UnixNano()) 103 | number := rand.Intn(10) 104 | ``` 105 | 106 | Then, the game context is generated. 107 | 108 | ```go 109 | t := Game{ 110 | CurrentStep: &Ask{}, 111 | Number: number, 112 | } 113 | ``` 114 | 115 | And the game can start. 116 | 117 | ```go 118 | t.prntState() 119 | for t.CurrentStep.Exec(&t) { 120 | t.prntState() 121 | } 122 | ``` 123 | 124 | Here the full main function when random number is generated, game created and 125 | game loop started. 126 | 127 | ```go 128 | func main() { 129 | rand.Seed(time.Now().UnixNano()) 130 | number := rand.Intn(10) 131 | 132 | t := Game{ 133 | CurrentStep: &Ask{}, 134 | Number: number, 135 | } 136 | 137 | t.prntState() 138 | for t.CurrentStep.Exec(&t) { 139 | t.prntState() 140 | } 141 | } 142 | ``` 143 | -------------------------------------------------------------------------------- /behavioral/state/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | import "math/rand" 5 | 6 | func main() { 7 | rand.Seed(time.Now().UnixNano()) 8 | number := rand.Intn(10) 9 | 10 | t := AContext{ 11 | CurrentState: &Ask{}, 12 | Number: number, 13 | } 14 | 15 | t.prntState() 16 | 17 | for t.CurrentState.Exec(&t) { 18 | t.prntState() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /behavioral/state/state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "os" 5 | 6 | type TrafficLightState interface { 7 | Exec(k *AContext) bool 8 | Name() string 9 | } 10 | 11 | type FinishState struct{} 12 | 13 | func (s *FinishState) Exec(k *AContext) bool { 14 | fmt.Println("FINISHED !!!") 15 | return false 16 | } 17 | func (s *FinishState) Name() string { 18 | return "finish" 19 | } 20 | 21 | type EndState struct{} 22 | 23 | func (s *EndState) Exec(k *AContext) bool { 24 | if k.Exit == true { 25 | k.CurrentState = &FinishState{} 26 | return true 27 | } 28 | 29 | k.CurrentState = &Ask{} 30 | return true 31 | } 32 | 33 | func (s *EndState) Name() string { 34 | return "end" 35 | } 36 | 37 | type AContext struct { 38 | CurrentState TrafficLightState 39 | Number int 40 | Exit bool 41 | } 42 | 43 | func (a *AContext) prntState() { 44 | fmt.Println(">>", a.CurrentState.Name()) 45 | if a.CurrentState.Name() == "end" { 46 | fmt.Println("") 47 | } 48 | } 49 | 50 | type Ask struct{} 51 | 52 | func (s *Ask) Exec(k *AContext) bool { 53 | var n int 54 | fmt.Print(">> ") 55 | fmt.Fscanf(os.Stdin, "%v", &n) 56 | if n == k.Number { 57 | k.Exit = true 58 | } else { 59 | if n > k.Number { 60 | fmt.Println(">> you number is greater") 61 | } else { 62 | fmt.Println(">> you number is lower") 63 | } 64 | } 65 | k.CurrentState = &EndState{} 66 | return true 67 | } 68 | 69 | func (s *Ask) Name() string { 70 | return "guess the number ... " 71 | } 72 | -------------------------------------------------------------------------------- /behavioral/state/state_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | import "math/rand" 5 | import "time" 6 | 7 | func TestStart(t *testing.T) { 8 | rand.Seed(time.Now().UnixNano()) 9 | number := rand.Intn(10) 10 | 11 | c := AContext{ 12 | CurrentState: &FinishState{}, 13 | Number: number, 14 | } 15 | 16 | c.prntState() 17 | 18 | if c.CurrentState.Name() != "finish" { 19 | t.Error("At FinishState game should be ended") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /behavioral/strategy/README.md: -------------------------------------------------------------------------------- 1 | # Behavioral » Strategy 2 | 3 | ## Elements 4 | 5 | - Strategy: the interface that define the behavior; 6 | - Concrete Strategy: each different implementation that respects the strategy; 7 | 8 | ## Description 9 | 10 | The strategy pattern uses different algorithms to achieve some specific 11 | functionality. These algorithms are hidden behind an interface and, of course, 12 | they must be interchangeable. All algorithms achieve same functionality in a 13 | different way. 14 | 15 | For example, `io.Writer` interface defines a strategy ti write, and the 16 | functionality is always the same. 17 | 18 | ## Implementation 19 | 20 | Main feature of this pattern is that we can have different algorithms that uses 21 | same interface. In this example we'll print some content to a different output 22 | using different strategies: 23 | 24 | - FileStrategy 25 | - ConsoleStrategy 26 | 27 | The strategy will be selected using a console argument. 28 | 29 | All strategies must implement same interface: 30 | 31 | ```go 32 | type PrintStrategy interface { 33 | Print() error 34 | } 35 | ``` 36 | 37 | As said few words ago, we have two strategies. One to print contents in console 38 | and another for files. 39 | 40 | ```go 41 | type ConsoleStrategy struct{} 42 | 43 | type FileStrategy struct { 44 | DestinationFilePath string 45 | } 46 | ``` 47 | 48 | Each strategy must implement same `Print()` method. 49 | 50 | ```go 51 | func (c *ConsoleStrategy) Print() error { 52 | fmt.Println("ConsoleStrategy") 53 | lister, _ := template.New("foo").Parse(tplTemplate()) 54 | lister.Execute(os.Stdout, tplParams()) 55 | return nil 56 | } 57 | 58 | func (c *FileStrategy) Print() error { 59 | fmt.Println("FileStrategy") 60 | var t bytes.Buffer 61 | foo, _ := template.New("bar").Parse(tplTemplate()) 62 | foo.Execute(&t, tplParams()) 63 | 64 | f, err := os.Create(c.DestinationFilePath) 65 | if err != nil { 66 | panic(err) 67 | } 68 | defer f.Close() 69 | f.Write(t.Bytes()) 70 | return nil 71 | } 72 | ``` 73 | 74 | Code of `tplParams()` and `tplTemplate()` is omitted here. It provide a simple 75 | template and parameters to build it. 76 | 77 | Main program will get from console the flag "strategy". If omitted, 78 | ConsoleStrategy will be selected and the template rendered will be visible in 79 | console. Otherwise, if strategy flag is sent with: 80 | 81 | > $ go run strategy.go --strategy=file 82 | 83 | Same content is saved into a file called "bigciao". 84 | 85 | ```go 86 | func main() { 87 | strategy := flag.String("strategy", "console", "selected strategy") 88 | flag.Parse() 89 | 90 | var printStrategy PrintStrategy 91 | 92 | switch *strategy { 93 | case "console": 94 | printStrategy = &ConsoleStrategy{} 95 | case "file": 96 | printStrategy = &FileStrategy{"bigciao"} 97 | default: 98 | printStrategy = &ConsoleStrategy{} 99 | } 100 | 101 | printStrategy.Print() 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /behavioral/strategy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | ) 6 | 7 | func main() { 8 | strategy := flag.String("strategy", "console", "selected strategy") 9 | flag.Parse() 10 | 11 | var printStrategy PrintStrategy 12 | 13 | switch *strategy { 14 | case "console": 15 | printStrategy = &ConsoleStrategy{} 16 | case "file": 17 | printStrategy = &FileStrategy{"bigciao"} 18 | default: 19 | printStrategy = &ConsoleStrategy{} 20 | } 21 | 22 | printStrategy.Print() 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "text/template" 8 | ) 9 | 10 | type PrintStrategy interface { 11 | Print() error 12 | } 13 | 14 | type ConsoleStrategy struct{} 15 | 16 | type FileStrategy struct { 17 | DestinationFilePath string 18 | } 19 | 20 | func (c *ConsoleStrategy) BuildOutput() string { 21 | return "ConsoleStrategy" 22 | } 23 | 24 | func (c *ConsoleStrategy) Print() error { 25 | fmt.Println(c.BuildOutput()) 26 | lister, _ := template.New("foo").Parse(tplTemplate()) 27 | lister.Execute(os.Stdout, tplParams()) 28 | return nil 29 | } 30 | 31 | func (c *FileStrategy) Print() error { 32 | fmt.Println("FileStrategy") 33 | var t bytes.Buffer 34 | foo, _ := template.New("bar").Parse(tplTemplate()) 35 | foo.Execute(&t, tplParams()) 36 | 37 | f, err := os.Create(c.DestinationFilePath) 38 | if err != nil { 39 | panic(err) 40 | } 41 | defer f.Close() 42 | f.Write(t.Bytes()) 43 | return nil 44 | } 45 | 46 | func tplParams() map[string]interface{} { 47 | items := []int{1, 1, 2, 3, 5, 8} 48 | return map[string]interface{}{ 49 | "items": items, 50 | "last": len(items) - 1, 51 | } 52 | } 53 | 54 | func tplTemplate() string { 55 | return "" + 56 | "{{range $i, $el := .items}}" + 57 | "{{$el}}" + 58 | "{{if eq $i $.last}}.{{else}}, {{end}}" + 59 | "{{end}}" 60 | } 61 | -------------------------------------------------------------------------------- /behavioral/strategy/strategy_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFoo(t *testing.T) { 8 | cs := ConsoleStrategy{} 9 | output := cs.BuildOutput() 10 | 11 | if output != "ConsoleStrategy" { 12 | t.Error("wrong output message") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /behavioral/template/README.md: -------------------------------------------------------------------------------- 1 | # Template 2 | 3 | ## Elements 4 | 5 | - Template: an interface that define behavior; 6 | - ConcreteTemplate: each class that implement all methods declared in template; 7 | 8 | ## Description 9 | 10 | [Strategy](../strategy) pattern consists in a different way to solve 11 | same problem with different strategies. In [Strategy](../strategy) 12 | entire algorithm is store in different classes. In Template pattern, instead, 13 | the algorithm is just one. The template defines steps but some other steps are 14 | deferred to user. 15 | 16 | ## Implementation 17 | 18 | In this case the template is a sort of flow of steps to solve a problem. While 19 | in [strategy](../strategy) pattern each [strategy](../strategy) 20 | implements entire solution, in this case the template define the steps 21 | execution but defer one or more steps to the user. In this case, the deferred 22 | step is called `templateSteps`. 23 | 24 | ```go 25 | type TheTemplate interface { 26 | first() string 27 | second() string 28 | templateSteps(MessageRetriever) string 29 | } 30 | ``` 31 | 32 | The `MessageRetriever` is an interface with `Message()` method. The template 33 | pattern here will joins strings. While first and third steps are implemented, 34 | the custom step is implemented by user. 35 | 36 | ```go 37 | type MessageRetriever interface { 38 | Message() string 39 | } 40 | ``` 41 | 42 | Now let's implement a concrete `Template`. 43 | 44 | ```go 45 | type Template struct{} 46 | ``` 47 | 48 | In this case first and second steps are defined. The `templateSteps` will join 49 | different strings receiving the third step from outside. 50 | 51 | ```go 52 | func (t *Template) first() string { 53 | return "hello" 54 | } 55 | 56 | func (t *Template) second() string { 57 | return "template" 58 | } 59 | ``` 60 | 61 | ```go 62 | func (t *Template) templateSteps(m MessageRetriever) string { 63 | return strings.Join( 64 | []string{ 65 | t.first(), 66 | m.Message(), 67 | t.second(), 68 | }, 69 | " ", 70 | ) 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /behavioral/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "strings" 4 | 5 | type MessageRetriever interface { 6 | Message() string 7 | } 8 | 9 | type TheTemplate interface { 10 | first() string 11 | second() string 12 | customStep(MessageRetriever) string 13 | } 14 | 15 | type Template struct{} 16 | 17 | func (t *Template) first() string { 18 | return "hello" 19 | } 20 | 21 | func (t *Template) second() string { 22 | return "template" 23 | } 24 | 25 | func (t *Template) customStep(m MessageRetriever) string { 26 | return strings.Join( 27 | []string{ 28 | t.first(), 29 | m.Message(), 30 | t.second(), 31 | }, 32 | " ", 33 | ) 34 | } 35 | 36 | type Anonymous struct{} 37 | 38 | func (a *Anonymous) first() string { 39 | return "hello" 40 | } 41 | 42 | func (a *Anonymous) second() string { 43 | return "template" 44 | } 45 | 46 | func (a *Anonymous) customStep(f func() string) string { 47 | return strings.Join( 48 | []string{ 49 | a.first(), 50 | f(), 51 | a.second(), 52 | }, 53 | " ", 54 | ) 55 | } 56 | 57 | type Wrapper struct { 58 | myFunc func() string 59 | } 60 | 61 | func (a *Wrapper) Message() string { 62 | if a.myFunc != nil { 63 | return a.myFunc() 64 | } 65 | 66 | return "" 67 | } 68 | 69 | func MessageRetrieverAdapter(f func() string) MessageRetriever { 70 | return &Wrapper{myFunc: f} 71 | } 72 | -------------------------------------------------------------------------------- /behavioral/template/template_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | type EmbedTemplate struct { 9 | Template 10 | } 11 | 12 | func (m *EmbedTemplate) Message() string { 13 | return "world" 14 | } 15 | 16 | func TestCustomStepIsCalled(t *testing.T) { 17 | t.Run("Using interfaces", func(t *testing.T) { 18 | s := &EmbedTemplate{} 19 | res := s.customStep(s) 20 | check(res, " world ", t) 21 | }) 22 | 23 | t.Run("Define custom step via anonymous function", func(t *testing.T) { 24 | m := new(Anonymous) 25 | res := m.customStep(func() string { 26 | return "world" 27 | }) 28 | check(res, " world ", t) 29 | }) 30 | 31 | t.Run("Using anonymous functions adapted to an interface", func(t *testing.T) { 32 | customStepStep := MessageRetrieverAdapter(func() string { 33 | return "world" 34 | }) 35 | if customStepStep == nil { 36 | t.Fatal("Can not continue with a nil custom step") 37 | } 38 | template := Template{} 39 | res := template.customStep(customStepStep) 40 | check(res, " world ", t) 41 | }) 42 | } 43 | 44 | func check(res string, expected string, t *testing.T) { 45 | if !strings.Contains(res, expected) { 46 | t.Errorf("Expected string '%s' was not found on returned string '%s'\n", expected, res) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /behavioral/visitor/README.md: -------------------------------------------------------------------------------- 1 | # Visitor 2 | 3 | ## Elements 4 | 5 | - Visitor: interface used to declare the visit operations; 6 | - ConcreteVisitor: the one that implement visitor interface; 7 | - Visitable: declares the accept operation, it enables an object to be "visited"; 8 | - ConcreteVisitable: each class that implements the Visitable interface and receive viditor; 9 | - ObjectStructure: containing all the objects that can be visited; 10 | 11 | ## Description 12 | 13 | The visitor pattern is a way to separate algorithm from an object structure. It 14 | is one way to implement the open/closed principle of SOLID. It allows to add 15 | functionalities without object modification. A visitor take the object instance 16 | as input and implements the algorithm. 17 | 18 | ## Implementation 19 | 20 | In the following implementation we will create a visitor that will visit all 21 | cards inside a project. Each card represents a Task or a Bug. Each card 22 | contains a title and some points. Here, card is an interface that for 23 | simplicity implement just a part of a complete card. 24 | 25 | Let's start from the card interface. Each card will contains, at least, 26 | GetTitle() and GetPoints() method. In a real world example each card could 27 | contains more and more specific methods. This is just a trivial example. 28 | 29 | ```go 30 | type Card interface { 31 | GetTitle() string 32 | GetPoints() int 33 | } 34 | ``` 35 | 36 | Now let's implement both task and bug object within interface Card. 37 | 38 | ```go 39 | type Task struct { 40 | Title string 41 | Time int 42 | } 43 | 44 | func (b *Task) GetTitle() string { 45 | return b.Title 46 | } 47 | 48 | func (b *Task) GetPoints() int { 49 | return b.Time 50 | } 51 | 52 | type Bug struct { 53 | Title string 54 | Time int 55 | } 56 | 57 | func (b *Bug) GetTitle() string { 58 | return b.Title 59 | } 60 | 61 | func (b *Bug) GetPoints() int { 62 | return b.Time 63 | } 64 | ``` 65 | 66 | Until now we have not yet visitor pattern elements. We just have bugs and 67 | tasks. In the visitor pattern there always be `Visitable` and `Visitor` items. 68 | Our visitor will visit a card. All visitable item must accept a visitor. 69 | 70 | ```go 71 | type Visitable interface { 72 | Accept(v Visitor) 73 | } 74 | 75 | type Visitor interface { 76 | Visit(t Card) 77 | } 78 | ``` 79 | 80 | Now we want that all cards are visitable. 81 | 82 | ```go 83 | func (b *Task) Accept(v Visitor) { 84 | v.Visit(b) 85 | } 86 | 87 | func (b *Bug) Accept(v Visitor) { 88 | v.Visit(b) 89 | } 90 | ``` 91 | 92 | And here we have the visitor: a service that sum each cards points. As we can 93 | see the logic is not in Bug object nor in Task. 94 | 95 | ```go 96 | type EstimationVisitor struct { 97 | Sum int 98 | } 99 | 100 | func (e *EstimationVisitor) Visit(t Card) { 101 | e.Sum += t.GetPoints() 102 | } 103 | ``` 104 | 105 | Finally, the main function, where it is visible the Visitor in action. 106 | 107 | ```go 108 | func main() { 109 | nextRelease := []Visitable{ 110 | &Task{"Do stuff", 1}, 111 | &Task{"Implement Foo Bar", 5}, 112 | &Bug{"Error 500 on resource /foo/bar", 3}, 113 | } 114 | 115 | storyPoint := new(EstimationVisitor) 116 | 117 | for _, i := range nextRelease { 118 | i.Accept(storyPoint) 119 | } 120 | 121 | // "Next release is calulated in 9 story points" 122 | fmt.Println( 123 | "Next release is calulated in", 124 | storyPoint.Sum, 125 | "story points", 126 | ) 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /behavioral/visitor/visitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Card interface { 6 | GetTitle() string 7 | GetPoints() int 8 | } 9 | 10 | type Visitable interface { 11 | Accept(v Visitor) 12 | } 13 | 14 | type Task struct { 15 | Title string 16 | Time int 17 | } 18 | 19 | func (b *Task) GetTitle() string { 20 | return b.Title 21 | } 22 | 23 | func (b *Task) GetPoints() int { 24 | return b.Time 25 | } 26 | 27 | func (b *Task) Accept(v Visitor) { 28 | v.Visit(b) 29 | } 30 | 31 | type Bug struct { 32 | Title string 33 | Time int 34 | } 35 | 36 | func (b *Bug) GetTitle() string { 37 | return b.Title 38 | } 39 | 40 | func (b *Bug) GetPoints() int { 41 | return b.Time 42 | } 43 | 44 | func (b *Bug) Accept(v Visitor) { 45 | v.Visit(b) 46 | } 47 | 48 | type Visitor interface { 49 | Visit(t Card) 50 | } 51 | 52 | type EstimationVisitor struct { 53 | Sum int 54 | } 55 | 56 | func (e *EstimationVisitor) Visit(t Card) { 57 | e.Sum += t.GetPoints() 58 | } 59 | 60 | func main() { 61 | nextRelease := []Visitable{ 62 | &Task{"Do stuff", 1}, 63 | &Task{"Implement Foo Bar", 5}, 64 | &Bug{"Error 500 on resource /foo/bar", 3}, 65 | } 66 | 67 | storyPoints := new(EstimationVisitor) 68 | 69 | for _, i := range nextRelease { 70 | i.Accept(storyPoints) 71 | } 72 | 73 | fmt.Println( 74 | "Next release is calulated in", 75 | storyPoints.Sum, 76 | "story points", 77 | ) 78 | } 79 | -------------------------------------------------------------------------------- /behavioral/visitor/visitor_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestVisitableBuildItemWithTasksAndBugs(t *testing.T) { 6 | c := []Visitable{ 7 | &Task{"Do stuff", 1}, 8 | &Task{"Implement Foo Bar", 5}, 9 | &Bug{"Error 500 on resource /foo/bar", 3}, 10 | } 11 | 12 | storyPoints := new(EstimationVisitor) 13 | 14 | for _, i := range c { 15 | i.Accept(storyPoints) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /concurrency/README.md: -------------------------------------------------------------------------------- 1 | # Concurrency patterns 2 | 3 | These patterns manage the timing and order execution. 4 | 5 | ## Creational 6 | 7 | * [Barrier](barrier) [:notebook:](https://en.wikipedia.org/wiki/Barrier_(computer_science)) 8 | * [Future](future) [:notebook:](https://en.wikipedia.org/wiki/Futures_and_promises) 9 | * [Pipeline](pipeline) [:notebook:](https://en.wikipedia.org/wiki/Pipeline_(software)) 10 | -------------------------------------------------------------------------------- /concurrency/barrier/README.md: -------------------------------------------------------------------------------- 1 | # Concurrency » Barrier 2 | 3 | ## Description 4 | 5 | The purpose of this pattern is to collect all the results from different 6 | functions and goroutines before pass. 7 | 8 | ## Implementation 9 | 10 | In this implementation we'll GET some urls (often used for testing purpose). 11 | According to pattern description we will output somethine just after both 12 | request have been received a response. 13 | 14 | ```go 15 | func main() { 16 | barrier( 17 | "http://httpbin.org/headers", 18 | "http://httpbin.org/user-agent", 19 | ) 20 | } 21 | ``` 22 | 23 | This implementation is very idiomatic and uses `go` keyword. This keyword will 24 | start new gorouting with function makeRequest. Also, this function contains a 25 | channel called `in`. 26 | 27 | 28 | ```go 29 | func barrier(urls ...string) { 30 | numOfRequsts := len(urls) 31 | 32 | in := make(chan Response, numOfRequsts) 33 | defer close(in) 34 | 35 | responses := make([]Response, numOfRequsts) 36 | 37 | for _, uri := range urls { 38 | go makeRequest( 39 | in, 40 | uri, 41 | ) 42 | } 43 | 44 | // … 45 | } 46 | ``` 47 | 48 | Instead, makeRequest function get the content, makes a covertion to string and 49 | return to channel everythin. 50 | 51 | ```go 52 | func makeRequest(out chan<- Response, url string) { 53 | // … 54 | 55 | resp, err := client.Get(url) 56 | byt, err := ioutil.ReadAll(resp.Body) 57 | res.Resp = string(byt) 58 | out <- res 59 | 60 | // … 61 | } 62 | ``` 63 | 64 | Here the complete solution: 65 | 66 | ```go 67 | var timeout int = 5000 68 | 69 | type Response struct { 70 | Err error 71 | Resp string 72 | } 73 | 74 | func barrier(urls ...string) { 75 | numOfRequsts := len(urls) 76 | 77 | in := make(chan Response, numOfRequsts) 78 | defer close(in) 79 | 80 | responses := make([]Response, numOfRequsts) 81 | 82 | for _, uri := range urls { 83 | go makeRequest( 84 | in, 85 | uri, 86 | ) 87 | } 88 | 89 | var hasError bool 90 | for i := 0; i < numOfRequsts; i++ { 91 | resp := <-in 92 | if resp.Err != nil { 93 | fmt.Println("ERROR: ", resp.Err) 94 | hasError = true 95 | } 96 | responses[i] = resp 97 | } 98 | 99 | if !hasError { 100 | for _, resp := range responses { 101 | fmt.Println(resp.Resp) 102 | } 103 | } 104 | } 105 | 106 | func makeRequest(out chan<- Response, url string) { 107 | res := Response{} 108 | client := http.Client{ 109 | Timeout: time.Duration(time.Duration(timeout) * 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 | 130 | func main() { 131 | barrier( 132 | "http://httpbin.org/headers", 133 | "http://httpbin.org/user-agent", 134 | ) 135 | } 136 | ``` 137 | -------------------------------------------------------------------------------- /concurrency/barrier/barrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "io/ioutil" 5 | import "net/http" 6 | import "time" 7 | 8 | var timeout int = 5000 9 | 10 | type Response struct { 11 | Err error 12 | Resp string 13 | } 14 | 15 | func barrier(urls ...string) { 16 | numOfRequsts := len(urls) 17 | 18 | in := make(chan Response, numOfRequsts) 19 | defer close(in) 20 | 21 | responses := make([]Response, numOfRequsts) 22 | 23 | for _, uri := range urls { 24 | go makeRequest( 25 | in, 26 | uri, 27 | ) 28 | } 29 | 30 | var hasError bool 31 | for i := 0; i < numOfRequsts; i++ { 32 | resp := <-in 33 | if resp.Err != nil { 34 | fmt.Println("ERROR: ", resp.Err) 35 | hasError = true 36 | } 37 | responses[i] = resp 38 | } 39 | 40 | if !hasError { 41 | for _, resp := range responses { 42 | fmt.Println(resp.Resp) 43 | } 44 | } 45 | } 46 | 47 | func makeRequest(out chan<- Response, url string) { 48 | res := Response{} 49 | client := http.Client{ 50 | Timeout: time.Duration(time.Duration(timeout) * time.Millisecond), 51 | } 52 | 53 | resp, err := client.Get(url) 54 | if err != nil { 55 | res.Err = err 56 | out <- res 57 | return 58 | } 59 | 60 | byt, err := ioutil.ReadAll(resp.Body) 61 | if err != nil { 62 | res.Err = err 63 | out <- res 64 | return 65 | } 66 | 67 | res.Resp = string(byt) 68 | out <- res 69 | } 70 | -------------------------------------------------------------------------------- /concurrency/barrier/barrier_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestDidAllUrlsDidLoaded(t *testing.T) { 6 | barrier( 7 | "http://httpbin.org/headers", 8 | "http://httpbin.org/user-agent", 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /concurrency/future/README.md: -------------------------------------------------------------------------------- 1 | # Concurrency » Future 2 | 3 | ## Description 4 | 5 | This design pattern describe an object that acts as a proxy for a result that is 6 | initially unknown, usually because the computation of its value is yet incomplete. 7 | 8 | The following is a solution with row code. First function in case of failure and 9 | second function in case of success. Here, for semplicity is just checked that the 10 | number is equal or not to 42. In a real world example there could be an http call 11 | or a query to database. Let's imagine whatever you want. 12 | 13 | ```go 14 | var wg sync.WaitGroup 15 | wg.Add(1) 16 | 17 | go func(num int) { 18 | if num != 42 { 19 | // in case of failure 20 | func() { 21 | fmt.Println("wrong answer") 22 | wg.Done() 23 | }() 24 | } else { 25 | // in case of success 26 | func() { 27 | fmt.Println("right answer") 28 | wg.Done() 29 | }() 30 | } 31 | fmt.Println(num) 32 | }(42) 33 | 34 | wg.Wait() 35 | ``` 36 | 37 | ## Implementation 38 | 39 | Now let's build a real implementation of the pattern. First of all we 40 | should define some functions for the future: 41 | 42 | - SuccessFunc 43 | - FailureFunc 44 | - ExecuteFunc 45 | 46 | ```go 47 | type SuccessFunc func(string) 48 | type FailureFunc func(error) 49 | type ExecuteFunc func(int) (string, error) 50 | ``` 51 | 52 | In this particular example, the execute function receive an integer. In this 53 | pattern, this function, should always return a string and an error. In case of 54 | success, string is passed to SuccessFunc. On the other case, error will be sent 55 | to FailureFunc. 56 | 57 | The following code represent the Subject in the pattern. In a real world 58 | example it could be better to chose a more semantic name. 59 | 60 | ```go 61 | type Subject struct { 62 | success SuccessFunc 63 | failure FailureFunc 64 | } 65 | 66 | func (s *Subject) Success(f SuccessFunc) *Subject { 67 | s.success = f 68 | return s 69 | } 70 | 71 | func (s *Subject) Failure(f FailureFunc) *Subject { 72 | s.failure = f 73 | return s 74 | } 75 | ``` 76 | 77 | The key point of the pattern is here. Like in barrier pattern is used a 78 | WaitGroup. Just for this example, a random number is generated. This should be 79 | replaced with a real domain algorithm. 80 | 81 | The fact is that `ExecuteFunc` will be executed and it will return a message or 82 | an error. 83 | 84 | ```go 85 | func (s *Subject) Execute(f ExecuteFunc) { 86 | var wg sync.WaitGroup 87 | wg.Add(1) 88 | 89 | go func(s *Subject) { 90 | r := rand.NewSource(time.Now().UnixNano()) 91 | n := rand.New(r) 92 | 93 | str, err := f(n.Intn(200)) 94 | 95 | if err != nil { 96 | s.failure(err) 97 | wg.Done() 98 | } else { 99 | s.success(str) 100 | wg.Done() 101 | } 102 | }(s) 103 | 104 | wg.Wait() 105 | } 106 | ``` 107 | 108 | And finally, the main function where our promise is a struct called Subject. 109 | This struct implement Success, Failure and Execute functions. 110 | 111 | ```go 112 | func main() { 113 | s := Subject{} 114 | 115 | s.Success(func(m string) { 116 | fmt.Println("SUCCESS: ", m) 117 | }).Failure(func(e error) { 118 | fmt.Println("FAILURE: ", e) 119 | }).Execute(func(num int) (string, error) { 120 | if num < 100 { 121 | return "", errors.New("too low number") 122 | } else { 123 | return "valid number", nil 124 | } 125 | }) 126 | 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /concurrency/future/future.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "sync" 5 | import "errors" 6 | import "math/rand" 7 | import "time" 8 | 9 | type SuccessFunc func(string) 10 | type FailureFunc func(error) 11 | type ExecuteFunc func(int) (string, error) 12 | 13 | type Subject struct { 14 | success SuccessFunc 15 | failure FailureFunc 16 | } 17 | 18 | func (s *Subject) Success(f SuccessFunc) *Subject { 19 | s.success = f 20 | return s 21 | } 22 | 23 | func (s *Subject) Failure(f FailureFunc) *Subject { 24 | s.failure = f 25 | return s 26 | } 27 | 28 | func (s *Subject) Execute(f ExecuteFunc) { 29 | var wg sync.WaitGroup 30 | wg.Add(1) 31 | 32 | go func(s *Subject) { 33 | r := rand.NewSource(time.Now().UnixNano()) 34 | n := rand.New(r) 35 | 36 | str, err := f(n.Intn(200)) 37 | 38 | if err != nil { 39 | s.failure(err) 40 | wg.Done() 41 | } else { 42 | s.success(str) 43 | wg.Done() 44 | } 45 | }(s) 46 | 47 | wg.Wait() 48 | } 49 | 50 | func main() { 51 | s := Subject{} 52 | 53 | s.Success(func(m string) { 54 | fmt.Println("SUCCESS: ", m) 55 | }).Failure(func(e error) { 56 | fmt.Println("FAILURE: ", e) 57 | }).Execute(func(num int) (string, error) { 58 | if num < 100 { 59 | return "", errors.New("too low number") 60 | } else { 61 | return "valid number", nil 62 | } 63 | }) 64 | 65 | } 66 | -------------------------------------------------------------------------------- /concurrency/pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Concurrency » Pipeline 2 | 3 | ## Elements 4 | 5 | - Visitor 6 | - ConcreteVisitor 7 | - Visitable 8 | - ConcreteVisitable 9 | - ObjectStructure 10 | 11 | ## Description 12 | 13 | A pipeline consists of a chain of elements arranged so that the output of each 14 | element is the input of the next. 15 | 16 | ## Implementation 17 | 18 | Each element of the pipeline is a function like the following function. In this 19 | piece of code is visible the isolation of current step. It is also important to 20 | get that inside goroutine the for treat all data received by channel input. 21 | When all data in input is managed the goroutine ends and finally the channel is 22 | returned. This means that each step's variable are close to current scope. 23 | 24 | ```go 25 | type x struct { } 26 | 27 | func step(in <-chan x) <-chan x { 28 | out := make(chan x, 100) 29 | go func() { 30 | for i := range in { 31 | // do something 32 | } 33 | close(out) 34 | }() 35 | return out 36 | } 37 | ``` 38 | 39 | There also be the beginning function that build the pipeline. In this case 40 | first step build a bundle of integers. Following steps sum all the integers, 41 | append the sum with a string ":foo" and finally append the string ":bar". 42 | 43 | 44 | ```go 45 | func StartPipeline(amount int) string { 46 | source := generator(amount) 47 | sum := sum(source) 48 | foo := appendFoo(sum) 49 | return <-appendBar(foo) 50 | } 51 | ``` 52 | 53 | ```go 54 | func generator(max int) <-chan int { 55 | outChInt := make(chan int, 100) 56 | go func() { 57 | for i := 1; i <= max; i++ { 58 | outChInt <- i 59 | } 60 | close(outChInt) 61 | }() 62 | return outChInt 63 | } 64 | ``` 65 | 66 | And finally main code. 67 | 68 | ```go 69 | func main() { 70 | fmt.Println("pipeline") 71 | res := StartPipeline(4) 72 | fmt.Println(res) 73 | } 74 | ``` 75 | 76 | -------------------------------------------------------------------------------- /concurrency/pipeline/pipeline.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "strings" 5 | import "strconv" 6 | 7 | func StartPipeline(amount int) string { 8 | source := generator(amount) 9 | sum := sum(source) 10 | foo := appendFoo(sum) 11 | return <-appendBar(foo) 12 | } 13 | 14 | func appendBar(in <-chan string) <-chan string { 15 | out := make(chan string, 100) 16 | go func() { 17 | bar := <-in 18 | out <- string(strings.Join([]string{bar, "bar"}, ":")) 19 | close(out) 20 | }() 21 | return out 22 | } 23 | 24 | func appendFoo(in <-chan int) <-chan string { 25 | out := make(chan string, 100) 26 | go func() { 27 | foo := <-in 28 | out <- string(strings.Join([]string{strconv.Itoa(foo), "foo"}, ":")) 29 | close(out) 30 | }() 31 | return out 32 | } 33 | 34 | func sum(in <-chan int) <-chan int { 35 | out := make(chan int, 100) 36 | go func() { 37 | var sum int 38 | for v := range in { 39 | sum += v 40 | } 41 | out <- sum 42 | close(out) 43 | }() 44 | return out 45 | } 46 | 47 | func generator(max int) <-chan int { 48 | outChInt := make(chan int, 100) 49 | go func() { 50 | for i := 1; i <= max; i++ { 51 | outChInt <- i 52 | } 53 | close(outChInt) 54 | }() 55 | return outChInt 56 | } 57 | 58 | func main() { 59 | fmt.Println("pipeline") 60 | res := StartPipeline(4) 61 | fmt.Println(res) 62 | } 63 | -------------------------------------------------------------------------------- /creational/README.md: -------------------------------------------------------------------------------- 1 | # Creational patterns 2 | 3 | Some patterns can overlap a bit. Use them with care and in the right context. 4 | Remember that these patterns's purpose is to abstract the creation of objects 5 | for complexity and maintenance purpose. 6 | 7 | ## Creational 8 | 9 | * [Abstract Factory method](abstract_factory) [:notebook:](http://en.wikipedia.org/wiki/Abstract_Factory_pattern) 10 | * [Builder](builder) [:notebook:](http://en.wikipedia.org/wiki/Builder_pattern) 11 | * [Factory method](factory) [:notebook:](http://en.wikipedia.org/wiki/Factory_pattern) 12 | * [Object Pool](pool) [:notebook:](http://en.wikipedia.org/wiki/Object_Pool_pattern) 13 | * [Prototype](prototype) [:notebook:](http://en.wikipedia.org/wiki/Prototype_pattern) 14 | * [Singleton](singleton) [:notebook:](http://en.wikipedia.org/wiki/Singleton_pattern) (is considered an anti-pattern! :no_entry:) 15 | -------------------------------------------------------------------------------- /creational/abstract_factory/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Abstract Factory 2 | 3 | ## Elements 4 | 5 | - AbstractFactory 6 | - ConcreteFactory 7 | - AbstactProduct 8 | - Product 9 | - Client 10 | 11 | ## Description 12 | 13 | The difference from factory design pattern is that with this pattern it is possible to manage complex data type. 14 | 15 | ## Implementation 16 | 17 | The better way to explain this pattern is to show its power. We need just three steps. First of all we create a factory of italian cars. Second we can ask to this factory a Ferrari. 18 | 19 | ```go 20 | func TestFoo(t *testing.T) { 21 | itaF, _ := BuildFactory(ItalianType) 22 | m, _ := itaF.Build(FerrariModel) 23 | 24 | car, ok := m.(Vehicle) 25 | if !ok { 26 | t.Fatal("Invalid model") 27 | } 28 | 29 | t.Logf("%v car has %d wheels", car.GetModelName(), car.NumOfWheels()) 30 | } 31 | ``` 32 | 33 | ## Usage 34 | 35 | BuildFactory provide a factory of italian cars. Then italian builder build a particular model of car. This model must have 4 wheels. 36 | 37 | ```go 38 | fca, _ := BuildFactory(ItalianType) 39 | m, _ := fca.Build(FerrariModel) 40 | car, err := m.(Vehicle) 41 | if model.NumOfWheels() != 4 { 42 | panic("Ferrari shoud have 4 wheels") 43 | } 44 | ``` 45 | 46 | BuildFactory provide a factory of italian cars. Then italian builder build a particular model of car. This model must have 5 wheels. 47 | 48 | ```go 49 | fca, _ := BuildFactory(ItalianType) 50 | model, _ := fca.Build(CarWithFiveWheelModel) 51 | if model.NumOfWheels() != 5 { 52 | panic("the car should have 5 wheels") 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /creational/abstract_factory/factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const ( 8 | ItalianType = 1 9 | ) 10 | 11 | const ( 12 | FerrariModel = 1 13 | CarWithFiveWheelModel = 2 14 | ) 15 | 16 | type Vehicle interface { 17 | NumOfWheels() int 18 | GetModelName() string 19 | } 20 | 21 | type VehicleFactory interface { 22 | Build(v int) (Vehicle, error) 23 | } 24 | 25 | type ItalianFactory struct{} 26 | 27 | type CarWithFiveWheelType struct{} 28 | 29 | func (f *CarWithFiveWheelType) NumOfWheels() int { 30 | return 5 31 | } 32 | 33 | func (f *CarWithFiveWheelType) GetModelName() string { 34 | return "Star" 35 | } 36 | 37 | type FerrariModelType struct { 38 | } 39 | 40 | func (f *FerrariModelType) NumOfWheels() int { 41 | return 4 42 | } 43 | 44 | func (f *FerrariModelType) GetModelName() string { 45 | return "Ferrari" 46 | } 47 | 48 | func (i *ItalianFactory) Build(v int) (Vehicle, error) { 49 | switch v { 50 | case FerrariModel: 51 | return new(FerrariModelType), nil 52 | case CarWithFiveWheelModel: 53 | return new(CarWithFiveWheelType), nil 54 | } 55 | return nil, fmt.Errorf("No Italian cars of type %d\n", v) 56 | } 57 | 58 | func BuildFactory(f int) (VehicleFactory, error) { 59 | switch f { 60 | case ItalianType: 61 | return new(ItalianFactory), nil 62 | default: 63 | return nil, fmt.Errorf("No factory with id %d\n", f) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /creational/abstract_factory/factory_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFoo(t *testing.T) { 8 | itaF, _ := BuildFactory(ItalianType) 9 | m, _ := itaF.Build(FerrariModel) 10 | 11 | car, ok := m.(Vehicle) 12 | if !ok { 13 | t.Fatal("Invalid model") 14 | } 15 | 16 | t.Logf("%v car has %d wheels", car.GetModelName(), car.NumOfWheels()) 17 | } 18 | 19 | func TestBar(t *testing.T) { 20 | itaF, _ := BuildFactory(ItalianType) 21 | m, _ := itaF.Build(CarWithFiveWheelModel) 22 | 23 | car, ok := m.(Vehicle) 24 | if !ok { 25 | t.Fatal("Invalid model") 26 | } 27 | 28 | if m.NumOfWheels() != 5 { 29 | t.Fatal("Star car should have 5 wheels") 30 | } 31 | 32 | t.Logf("%v car has %d wheels", car.GetModelName(), car.NumOfWheels()) 33 | } 34 | -------------------------------------------------------------------------------- /creational/builder/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Builder 2 | 3 | ## Elements 4 | 5 | - Builder 6 | - ConcreteBuilder 7 | - Director 8 | - Product 9 | 10 | ## Description 11 | 12 | The Builder is a pattern used to build objects. Objects in go can be created with just {}. But in Go is possible to create objects composed by other objects. This is really idiomatic in go, as it doesn't support inheritance. 13 | 14 | The target of this pattern is to build something. An in this example we will create a Product. 15 | 16 | ```go 17 | type Product struct { 18 | Wheels int 19 | Seats int 20 | } 21 | ``` 22 | 23 | This kind of product have wheels and seats. All products must be builded following a process. This process aims to complete the product. The actions to do to complete the product are `SetWheelsNumber` and `SetSeatsNumber`. After this, the process can deploy the product. 24 | 25 | ```go 26 | type VehicleBuildProcess interface { 27 | SetWheelsNumber() VehicleBuildProcess 28 | SetSeatsNumber() VehicleBuildProcess 29 | GetVehicle() Product 30 | } 31 | ``` 32 | 33 | All the process are managed by a ManufacturingDirector. It will ask the builder all the actions to do. 34 | 35 | ```go 36 | type ManufacturingDirector struct { 37 | builder VehicleBuildProcess 38 | } 39 | ``` 40 | 41 | In the following code we tell to the director "who" is the builder and he will make the product with construct method. 42 | 43 | ```go 44 | func (f *ManufacturingDirector) SetBuilder(b VehicleBuildProcess) { 45 | f.builder = b 46 | } 47 | 48 | func (f *ManufacturingDirector) Construct() { 49 | f.builder.SetSeatsNumber().SetWheelsNumber() 50 | } 51 | ``` 52 | 53 | ## A car builder 54 | 55 | The final thing to do is to create a builder. In this example we will build a car. A car with 5 seats and 4 wheels. 56 | 57 | ```go 58 | type CarBuilder struct { 59 | p Product 60 | } 61 | 62 | func (c *CarBuilder) SetWheelsNumber() VehicleBuildProcess { 63 | c.p.Seats = 5 64 | return c 65 | } 66 | 67 | func (c *CarBuilder) SetSeatsNumber() VehicleBuildProcess { 68 | c.p.Wheels = 4 69 | return c 70 | } 71 | 72 | func (c *CarBuilder) GetVehicle() Product { 73 | return c.p 74 | } 75 | 76 | func (c *CarBuilder) Build() Product { 77 | return c.p 78 | } 79 | ``` 80 | 81 | # Add new product, … 82 | 83 | After we have built a car, we could create a bicycle. Here the test: 84 | 85 | ```go 86 | bicicletta := &BiciclettaBuilder{} 87 | director.SetBuilder(bicicletta) 88 | director.Construct() 89 | biciletta := bicicletta.Build() 90 | if biciletta.Wheels != 2 { 91 | t.Errorf("Something went wrong : " + strconv.Itoa(bicicletta.Wheels) + " wheels found") 92 | } 93 | ``` 94 | 95 | And here the builder: 96 | 97 | ```go 98 | type BiciclettaBuilder struct { 99 | p Product 100 | } 101 | 102 | func (c *BiciclettaBuilder) SetWheelsNumber() VehicleBuildProcess { 103 | c.p.Seats = 1 104 | return c 105 | } 106 | 107 | func (c *BiciclettaBuilder) SetSeatsNumber() VehicleBuildProcess { 108 | c.p.Wheels = 2 109 | return c 110 | } 111 | 112 | func (c *BiciclettaBuilder) GetVehicle() Product { 113 | return c.p 114 | } 115 | 116 | func (c *BiciclettaBuilder) Build() Product { 117 | return c.p 118 | } 119 | ``` 120 | 121 | ## Usage 122 | 123 | The director must build a vehicle like a car. Once the builder is provided. The director ask the builder to build the car. Finally we'll have right model of car. 124 | 125 | ```go 126 | director := ManufacturingDirector{} 127 | 128 | carBuilder := &CarBuilder{} 129 | director.SetBuilder(carBuilder) 130 | director.Construct() 131 | car := carBuilder.Build() 132 | ``` 133 | 134 | The director must build a vehicle like a bike. Once the builder is provided. The director ask the builder to build the bike. Finally we'll have right model of bike. 135 | 136 | ```go 137 | biciclettaBuilder := &BiciclettaBuilder{} 138 | director.SetBuilder(biciclettaBuilder) 139 | director.Construct() 140 | bike := biciclettaBuilder.Build() 141 | ``` 142 | -------------------------------------------------------------------------------- /creational/builder/builder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | type Product struct { 4 | Wheels int 5 | Seats int 6 | } 7 | 8 | type VehicleBuildProcess interface { 9 | SetWheelsNumber() VehicleBuildProcess 10 | SetSeatsNumber() VehicleBuildProcess 11 | GetVehicle() Product 12 | } 13 | 14 | type ManufacturingDirector struct { 15 | builder VehicleBuildProcess 16 | } 17 | 18 | func (f *ManufacturingDirector) SetBuilder(b VehicleBuildProcess) { 19 | f.builder = b 20 | } 21 | 22 | func (f *ManufacturingDirector) Construct() { 23 | f.builder.SetSeatsNumber().SetWheelsNumber() 24 | } 25 | 26 | type CarBuilder struct { 27 | p Product 28 | } 29 | 30 | func (c *CarBuilder) SetWheelsNumber() VehicleBuildProcess { 31 | c.p.Seats = 5 32 | return c 33 | } 34 | 35 | func (c *CarBuilder) SetSeatsNumber() VehicleBuildProcess { 36 | c.p.Wheels = 4 37 | return c 38 | } 39 | 40 | func (c *CarBuilder) GetVehicle() Product { 41 | return c.p 42 | } 43 | 44 | func (c *CarBuilder) Build() Product { 45 | return c.p 46 | } 47 | 48 | type BiciclettaBuilder struct { 49 | p Product 50 | } 51 | 52 | func (c *BiciclettaBuilder) SetWheelsNumber() VehicleBuildProcess { 53 | c.p.Seats = 1 54 | return c 55 | } 56 | 57 | func (c *BiciclettaBuilder) SetSeatsNumber() VehicleBuildProcess { 58 | c.p.Wheels = 2 59 | return c 60 | } 61 | 62 | func (c *BiciclettaBuilder) GetVehicle() Product { 63 | return c.p 64 | } 65 | 66 | func (c *BiciclettaBuilder) Build() Product { 67 | return c.p 68 | } 69 | -------------------------------------------------------------------------------- /creational/builder/builder_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "testing" 4 | import "strconv" 5 | 6 | func TestBuilderPattern(t *testing.T) { 7 | director := ManufacturingDirector{} 8 | 9 | carBuilder := &CarBuilder{} 10 | director.SetBuilder(carBuilder) 11 | director.Construct() 12 | car := carBuilder.Build() 13 | if car.Wheels != 4 { 14 | t.Errorf("Something went wrong : " + strconv.Itoa(car.Wheels) + " wheels found") 15 | } 16 | 17 | biciclettaBuilder := &BiciclettaBuilder{} 18 | director.SetBuilder(biciclettaBuilder) 19 | director.Construct() 20 | bike := biciclettaBuilder.Build() 21 | if bike.Wheels != 2 { 22 | t.Errorf("Something went wrong : " + strconv.Itoa(bike.Wheels) + " wheels found") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /creational/factory/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Factory 2 | 3 | ## Description 4 | 5 | The purpose of current pattern is to provide an interface that fits the developer's needs, delegating the decision of objects creation to a factory. Only the factory knows how to create stuffs. The creation project is completely abstract here. 6 | 7 | ## Implementation 8 | 9 | In this example we will build a sort of greeting generators. Just for italian and english translations. 10 | 11 | First of all we need languages. We can define them as constants: 12 | 13 | ```go 14 | const ( 15 | Italian = 1 16 | English = 2 17 | ) 18 | ``` 19 | 20 | Then, we need a translator. A translator that build a greeting message. 21 | 22 | ```go 23 | type Translator interface { 24 | GetGreeting() string 25 | } 26 | ``` 27 | 28 | To be sure that all works fine, we create a greeting from Italian. In our test we just care about the content of the message. 29 | 30 | ```go 31 | func TestCreateGetGreetingOfWarehouse(t *testing.T) { 32 | greeting, err := GetTranslator(Italian) 33 | if err != nil { 34 | t.Fatal("A GetGreetingment method of type 'Italian' must exists") 35 | } 36 | 37 | msg := greeting.GetGreeting() 38 | if !strings.Contains(msg, "Ciao") { 39 | t.Error("The italian greeting isn't correct") 40 | } 41 | } 42 | ``` 43 | 44 | As you can see, from tests EnglishGreeting and ItalianGreeting are not present. The factory successfully abstract the message creation, delegating it to ItalianGreeting and EnglishGreeting. 45 | -------------------------------------------------------------------------------- /creational/factory/factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import "fmt" 4 | 5 | const ( 6 | Italian = 1 7 | English = 2 8 | ) 9 | 10 | type Translator interface { 11 | GetGreeting() string 12 | } 13 | 14 | func GetTranslator(m int) (Translator, error) { 15 | switch m { 16 | case Italian: 17 | return new(ItalianGreeting), nil 18 | case English: 19 | return new(EnglishGreeting), nil 20 | default: 21 | return nil, fmt.Errorf("Unknown building") 22 | } 23 | return nil, fmt.Errorf("Not implemented yet") 24 | } 25 | 26 | type ItalianGreeting struct{} 27 | 28 | func (c *ItalianGreeting) GetGreeting() string { 29 | return "Ciao" 30 | } 31 | 32 | type EnglishGreeting struct{} 33 | 34 | func (d *EnglishGreeting) GetGreeting() string { 35 | return "Hello" 36 | } 37 | -------------------------------------------------------------------------------- /creational/factory/factory_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestCreateGetGreetingOfWarehouse(t *testing.T) { 9 | greeting, err := GetTranslator(Italian) 10 | if err != nil { 11 | t.Fatal("A GetGreetingment method of type 'Italian' must exists") 12 | } 13 | 14 | msg := greeting.GetGreeting() 15 | if !strings.Contains(msg, "Ciao") { 16 | t.Error("The italian greeting isn't correct") 17 | } 18 | } 19 | 20 | func TestCreateGetGreetingOfEnglish(t *testing.T) { 21 | greeting, err := GetTranslator(English) 22 | if err != nil { 23 | t.Fatal("A GetGreetingment method of type 'English' must exists") 24 | } 25 | 26 | msg := greeting.GetGreeting() 27 | if !strings.Contains(msg, "Hello") { 28 | t.Error("The english greeting isn't correct") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /creational/pool/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Pool 2 | 3 | ## Description 4 | 5 | The object pool design pattern creates a set of objects that may be reused. When a new object is needed, it is requested from the pool. Once an object has been used and returned, existing references will become invalid. If a previously prepared object is available it is returned immediately, avoiding the instantiation cost. If no objects are present in the pool, a new item is created and returned. 6 | 7 | ## Implementation 8 | 9 | For this pattern we will use two object. The PoolObject and the Pool. The former is the object that must be managed, the latter is the object that manage the pool. That provide idle objects. That receive object no more in use. 10 | 11 | ```go 12 | type PoolObject struct { 13 | id int 14 | } 15 | 16 | type Pool struct { 17 | idle *list.List 18 | active *list.List 19 | } 20 | ``` 21 | 22 | In this implementation, the pool initialize two lists. The idle list will contains all retured object, read for reuse. The active list, contains all generate object. 23 | 24 | ```go 25 | func InitPool() Pool { 26 | pool := &Pool{ 27 | list.New(), 28 | list.New(), 29 | } 30 | return *pool 31 | } 32 | ``` 33 | 34 | In a very simple implementation, object are created each time requested from the pool. 35 | 36 | ```go 37 | func (p *Pool) Loan() PoolObject { 38 | object := PoolObject{p.NumberOfObjectsInPool() + 1} 39 | p.active.PushBack(object) 40 | return object 41 | } 42 | ``` 43 | 44 | The complete implementation of Loan method checl if there are already created object. If idle object exists, it is provided avoiding creation time. 45 | 46 | ```go 47 | func (p *Pool) Loan() PoolObject { 48 | if p.idle.Len() > 0 { 49 | for e, i := p.idle.Front(), 0; e != nil; e, i = e.Next(), i+1 { 50 | if i == 0 { 51 | object := e.Value.(PoolObject) 52 | return object 53 | } 54 | } 55 | } 56 | 57 | object := PoolObject{p.NumberOfObjectsInPool() + 1} 58 | p.active.PushBack(object) 59 | return object 60 | } 61 | ``` 62 | 63 | When an object is used, is returned to the pool. Used object are stored in idle list, ready to be returned. 64 | 65 | ```go 66 | func (p *Pool) Receive(object PoolObject) { 67 | p.idle.PushBack(object) 68 | for e, i := p.active.Front(), 0; e != nil; e, i = e.Next(), i+1 { 69 | if object == e.Value.(PoolObject) { 70 | p.active.Remove(e) 71 | return 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | ## Usage 78 | 79 | First, the pool provide two object. Second, the pool receive one used object. Third, another loan is requested but instead of provide another instance of new object, thirdObject contains firstObject. No time is spent to build another object. 80 | 81 | ```go 82 | pool := InitPool() 83 | firstObject := pool.Loan() 84 | secondObject := pool.Loan() 85 | 86 | pool.Receive(firstObject) 87 | thirdObject := pool.Loan() 88 | 89 | if firstObject.id != thirdObject.id { 90 | panic("thir object must contain firs one") 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /creational/pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "container/list" 5 | "errors" 6 | "log" 7 | "strconv" 8 | ) 9 | 10 | type PoolObject struct { 11 | id int 12 | } 13 | 14 | type Pool struct { 15 | idle *list.List 16 | active *list.List 17 | } 18 | 19 | func InitPool() Pool { 20 | pool := &Pool{ 21 | list.New(), 22 | list.New(), 23 | } 24 | return *pool 25 | } 26 | 27 | func (p *Pool) Borrow() (*PoolObject, error) { 28 | if p.idle.Len() > 0 { 29 | for e, i := p.idle.Front(), 0; e != nil; e, i = e.Next(), i+1 { 30 | if i == 0 { 31 | object := e.Value.(PoolObject) 32 | return &object, nil 33 | } 34 | } 35 | } 36 | 37 | log.Println(strconv.Itoa(p.active.Len())) 38 | if p.NumberOfObjectsInPool() >= 3 { 39 | return nil, errors.New("...") 40 | } 41 | 42 | object := PoolObject{p.NumberOfObjectsInPool() + 1} 43 | p.active.PushBack(object) 44 | return &object, nil 45 | } 46 | 47 | func (p *Pool) Receive(object PoolObject) { 48 | p.idle.PushBack(object) 49 | for e := p.active.Front(); e != nil; e = e.Next() { 50 | p.active.Remove(e) 51 | return 52 | } 53 | } 54 | 55 | func (p *Pool) NumberOfObjectsInPool() int { 56 | return p.active.Len() + p.idle.Len() 57 | } 58 | 59 | func (p *Pool) NumberOfActiveObjects() int { 60 | return p.active.Len() 61 | } 62 | 63 | func (p *Pool) NumberOfIdleObjects() int { 64 | return p.idle.Len() 65 | } 66 | -------------------------------------------------------------------------------- /creational/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestWhenObjectIsNeededItIsRequestedFromThePool(t *testing.T) { 10 | pool := InitPool() 11 | if pool.NumberOfActiveObjects() != 0 { 12 | t.Fatal("Actually there should be zero objects in pool") 13 | } 14 | if pool.NumberOfIdleObjects() != 0 { 15 | t.Fatal("Actually there should be zero idle objects in pool") 16 | } 17 | if pool.NumberOfObjectsInPool() != 0 { 18 | t.Fatal("There shouldnt be more than zero object in pool") 19 | } 20 | 21 | objectOne, _ := pool.Borrow() 22 | if pool.NumberOfActiveObjects() != 1 { 23 | t.Fatal("Something went wrong") 24 | } 25 | if pool.NumberOfIdleObjects() != 0 { 26 | t.Fatal("Actually there should be zero idle objects in pool") 27 | } 28 | 29 | createdId := objectOne.id 30 | if createdId != 1 { 31 | t.Fatal("object id should be 1") 32 | } 33 | 34 | pool.Receive(*objectOne) 35 | if pool.NumberOfActiveObjects() != 0 { 36 | t.Fatal("There should not be active object") 37 | } 38 | if pool.NumberOfIdleObjects() != 1 { 39 | t.Fatal("Actually there should be zero idle objects in pool") 40 | } 41 | 42 | secondCall, _ := pool.Borrow() 43 | if secondCall.id != createdId { 44 | t.Fatal(strings.Join([]string{ 45 | "Pool should return same object: we now have ", 46 | strconv.Itoa(secondCall.id), 47 | " instead of ", 48 | strconv.Itoa(createdId), 49 | }, "")) 50 | } 51 | if pool.NumberOfIdleObjects() != 1 { 52 | t.Fatal("Actually there should be zero idle objects in pool") 53 | } 54 | } 55 | 56 | func TestPool(t *testing.T) { 57 | pool := InitPool() 58 | 59 | _, _ = pool.Borrow() 60 | foo, _ := pool.Borrow() 61 | _, _ = pool.Borrow() 62 | 63 | if pool.NumberOfActiveObjects() != 3 { 64 | t.Fatal("Actually there should be zero idle objects in pool") 65 | } 66 | 67 | pool.Receive(*foo) 68 | if pool.NumberOfActiveObjects() != 2 { 69 | t.Fatal("Actually there should be zero idle objects in pool") 70 | } 71 | } 72 | 73 | func TestPoolLimitNumberOfAvailableObject(t *testing.T) { 74 | pool := InitPool() 75 | _, _ = pool.Borrow() 76 | _, _ = pool.Borrow() 77 | _, err := pool.Borrow() 78 | if err != nil { 79 | t.Fatal("There should be one more available object in pool") 80 | } 81 | 82 | _, err = pool.Borrow() 83 | if err == nil { 84 | t.Fatal("Current pool should manage only four object") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /creational/prototype/README.md: -------------------------------------------------------------------------------- 1 | # Creational » Prototype 2 | 3 | ## Elements 4 | 5 | - Client 6 | - Prototype 7 | - ConcretePrototype 8 | 9 | ## Description 10 | 11 | With this pattern, is used an already created instance of some type to clone it and complete it with the particular needs of each context. Objects to clone are created at compilation time and can be cloned as many times it is needed at runtime. 12 | 13 | ```go 14 | firstInstance := GetInstance() 15 | 16 | secondInstance := GetInstance() 17 | if firstInstance != secondInstance { 18 | t.Error("expected same instance") 19 | } 20 | ``` 21 | 22 | Any following calls, update first intance. 23 | 24 | ```go 25 | thirdInstance := GetInstance() 26 | if thirdInstance.NumberOfCalls() != 3 { 27 | t.Error("expected three calls") 28 | } 29 | ``` 30 | 31 | ## Few words … 32 | 33 | Is not good pattern if used to bring the state of the applicatoin and can cnange during its lifecycle. Making something global to avoid passing it around is a code smell. But use it to read configuration is good. Used to load a resource just first time is requested and to provide that resource everywere is a good way to use this pattern. 34 | 35 | ## Usage 36 | 37 | Two kind of t-shirt are needed. Same model, with different SKU. Instead of recreeate same white shirt from scratch, base model is cloned. 38 | 39 | ```go 40 | shirtCache := GetShirtsCloner() 41 | firstItem, err := shirtCache.GetClone(White) 42 | firstItem.SKU = "abc" 43 | 44 | secondItem, err := shirtCache.GetClone(White) 45 | secondItem.SKU = "xxx" 46 | ``` 47 | -------------------------------------------------------------------------------- /creational/prototype/prototype.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import "fmt" 4 | 5 | type ShirtCloner interface { 6 | GetClone(s int) (ItemInfoGetter, error) 7 | } 8 | 9 | const ( 10 | White = 1 11 | Black = 2 12 | Blue = 3 13 | ) 14 | 15 | func GetShirtsCloner() ShirtCloner { 16 | return new(ShirtsCache) 17 | } 18 | 19 | type ShirtsCache struct{} 20 | 21 | func (s *ShirtsCache) GetClone(m int) (ItemInfoGetter, error) { 22 | switch m { 23 | case White: 24 | newItem := *whitePrototype 25 | return &newItem, nil 26 | } 27 | return nil, fmt.Errorf("Not implemented yet") 28 | } 29 | 30 | type ItemInfoGetter interface { 31 | GetInfo() string 32 | } 33 | 34 | type ShirtColor byte 35 | 36 | type Shirt struct { 37 | Price float32 38 | SKU string 39 | Color ShirtColor 40 | } 41 | 42 | func (s *Shirt) GetInfo() string { 43 | return "" 44 | } 45 | 46 | var whitePrototype *Shirt = &Shirt{ 47 | Price: 15.00, 48 | SKU: "empty", 49 | Color: White, 50 | } 51 | 52 | func (i *Shirt) GetPrice() float32 { 53 | return i.Price 54 | } 55 | -------------------------------------------------------------------------------- /creational/prototype/prototype_test.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import "testing" 4 | 5 | func TestClone(t *testing.T) { 6 | shirtCache := GetShirtsCloner() 7 | if shirtCache == nil { 8 | t.Fatal("Received cache was nil") 9 | } 10 | 11 | firstItem, err := shirtCache.GetClone(White) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | if firstItem == whitePrototype { 17 | t.Fatal("firstitem cannot be equal to the white prototype") 18 | } 19 | 20 | secondItem, err := shirtCache.GetClone(White) 21 | 22 | firstShirt, ok := firstItem.(*Shirt) 23 | if !ok { 24 | t.Fatal("Type assertion for shirt1 couldnt be done successfully") 25 | } 26 | 27 | firstShirt.SKU = "abbcde" 28 | secondShirt, ok := secondItem.(*Shirt) 29 | 30 | if firstShirt.SKU == secondShirt.SKU { 31 | t.Fatal("SKU's must be different") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sensorario/go-design-patterns 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /structural/README.md: -------------------------------------------------------------------------------- 1 | # Structural Patterns 2 | 3 | Structural patterns help us to shape application with common used structures and 4 | relationships. 5 | 6 | ## Structural 7 | 8 | * [Adapter](adapter) [:notebook:](https://en.wikipedia.org/wiki/Adapter_pattern) 9 | * [Binary Tree compositions](binary-tree-compositions) [:notebook:](https://en.wikipedia.org/wiki/Binary_tree) 10 | * [Bridge](bridge) [:notebook:](https://en.wikipedia.org/wiki/Bridge_pattern) 11 | * [Composite](composite) [:notebook:](http://en.wikipedia.org/wiki/Composite_pattern) 12 | * [Decorator](decorator) [:notebook:](https://en.wikipedia.org/wiki/Decorator_pattern) 13 | * [Flyweight](flyweight) [:notebook:](https://en.wikipedia.org/wiki/Flyweight_pattern) 14 | * [Proxy](proxy) [:notebook:](https://en.wikipedia.org/wiki/Proxy_pattern) 15 | -------------------------------------------------------------------------------- /structural/adapter/README.md: -------------------------------------------------------------------------------- 1 | # Adapter 2 | 3 | ## Elements 4 | 5 | - Adaptee 6 | - Target 7 | - Client 8 | - Adapter 9 | 10 | ## Description 11 | 12 | Also known as Wrapper, allows the interface of an existing class to be used as another interface. 13 | It is often used to make existing classes work with others without modifying their source code. 14 | -------------------------------------------------------------------------------- /structural/adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type LegacyPrinter interface { 6 | Print(s string) string 7 | } 8 | 9 | type MyLegacyPrinter struct{} 10 | 11 | type ModernPrinter interface { 12 | PrintStored() string 13 | } 14 | 15 | type PrinterAdapter struct { 16 | OldPrinter LegacyPrinter 17 | Msg string 18 | } 19 | 20 | func (p *PrinterAdapter) PrintStored() (newMsg string) { 21 | if p.OldPrinter != nil { 22 | newMsg = fmt.Sprintf("Adapter: %s", p.Msg) 23 | newMsg = p.OldPrinter.Print(newMsg) 24 | } else { 25 | newMsg = p.Msg 26 | } 27 | return 28 | } 29 | 30 | func (l *MyLegacyPrinter) Print(s string) (newMsg string) { 31 | newMsg = fmt.Sprintf("Legacy Printer: %s\n", s) 32 | println(newMsg) 33 | return 34 | } 35 | 36 | func main() { 37 | fmt.Println("vim-go") 38 | } 39 | -------------------------------------------------------------------------------- /structural/adapter/adapter_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestAdater(t *testing.T) { 6 | msg := "Hello world!!!" 7 | adapter := PrinterAdapter{ 8 | OldPrinter: &MyLegacyPrinter{}, 9 | Msg: msg, 10 | } 11 | returnedMsg := adapter.PrintStored() 12 | if returnedMsg != "Legacy Printer: Adapter: Hello world!!!\n" { 13 | t.Errorf("Message didnt match: %s\n", returnedMsg) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /structural/binary-tree-compositions/README.md: -------------------------------------------------------------------------------- 1 | # Binary Tree 2 | 3 | In computer science, a binary tree is a tree data structure in which each node has at most two children, which are referred to as the left child and the right child. 4 | -------------------------------------------------------------------------------- /structural/binary-tree-compositions/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Tree struct { 8 | LeafValue int 9 | Right *Tree 10 | Left *Tree 11 | } 12 | 13 | func main() { 14 | root := Tree{ 15 | LeafValue: 0, 16 | Right: &Tree{ 17 | LeafValue: 5, 18 | Right: &Tree{6, nil, nil}, 19 | Left: nil, 20 | }, 21 | Left: &Tree{4, nil, nil}, 22 | } 23 | 24 | fmt.Println(root.Right.Right.LeafValue) 25 | } -------------------------------------------------------------------------------- /structural/bridge/README.md: -------------------------------------------------------------------------------- 1 | # Bridge 2 | 3 | ## Elements 4 | 5 | - Abstraction 6 | - AbstractionImplementation 7 | - Implementor 8 | - ConcreteImplementor 9 | 10 | ## Description 11 | 12 | This design pattern is used to "decouple an abstraction from its implementation so that the two can vary independently". The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes. 13 | 14 | -------------------------------------------------------------------------------- /structural/bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | type PrinterImp1 struct{} 10 | 11 | func (p *PrinterImp1) PrintMessage(msg string) error { 12 | fmt.Println(msg) 13 | return nil 14 | } 15 | 16 | // --- 17 | 18 | type PrinterImp2 struct { 19 | Writer io.Writer 20 | } 21 | 22 | func (d *PrinterImp2) PrintMessage(msg string) error { 23 | if d.Writer == nil { 24 | return errors.New("You need to pass an io.Writer to PrinterImp2") 25 | } 26 | fmt.Fprintf(d.Writer, "%s", msg) 27 | return nil 28 | } 29 | 30 | // --- 31 | 32 | type PrinterAPI interface { 33 | PrintMessage(string) error 34 | } 35 | 36 | type NormalPrinter struct { 37 | Msg string 38 | Printer PrinterAPI 39 | } 40 | 41 | func (c *NormalPrinter) Print() error { 42 | c.Printer.PrintMessage(c.Msg) 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /structural/bridge/bridge_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestPrintAPI1(t *testing.T) { 10 | api1 := PrinterImp1{} 11 | err := api1.PrintMessage("Hello") 12 | if err != nil { 13 | t.Errorf("Error trying to use the API1 implementation: Message: %s\n", err.Error()) 14 | } 15 | } 16 | 17 | type TestWriter struct { 18 | Msg string 19 | } 20 | 21 | func (t *TestWriter) Write(p []byte) (n int, err error) { 22 | n = len(p) 23 | if n > 0 { 24 | t.Msg = string(p) 25 | return n, nil 26 | } 27 | err = errors.New("Content received on Writer was empty") 28 | return 29 | } 30 | 31 | func TestPrintAPI2(t *testing.T) { 32 | api2 := PrinterImp2{} 33 | err := api2.PrintMessage("Hello") 34 | if err != nil { 35 | expectedErrorMessage := "You need to pass an io.Writer to PrinterImp2" 36 | if !strings.Contains(err.Error(), expectedErrorMessage) { 37 | t.Errorf("Error message was not correct.\n") 38 | } 39 | } 40 | testWriter := TestWriter{} 41 | api2 = PrinterImp2{ 42 | Writer: &testWriter, 43 | } 44 | 45 | expectedMessage := "Hello" 46 | err = api2.PrintMessage(expectedMessage) 47 | if err != nil { 48 | t.Errorf("Error trying to use API2 implementation: %s\n", err.Error()) 49 | } 50 | 51 | if testWriter.Msg != expectedMessage { 52 | t.Fatalf("API2 did not write correctly on the io.Writer. \n Actual: %s\nExpected: %s\n", testWriter.Msg, expectedMessage) 53 | } 54 | } 55 | 56 | func TestNormalPrinter_Print(t *testing.T) { 57 | expectedMessage := "Hello io.Writer" 58 | normal := NormalPrinter{ 59 | Msg: expectedMessage, 60 | Printer: &PrinterImp1{}, 61 | } 62 | err := normal.Print() 63 | if err != nil { 64 | t.Errorf(err.Error()) 65 | } 66 | 67 | testWriter := TestWriter{} 68 | normal = NormalPrinter{ 69 | Msg: expectedMessage, 70 | Printer: &PrinterImp2{ 71 | Writer: &testWriter, 72 | }, 73 | } 74 | 75 | err = normal.Print() 76 | if err != nil { 77 | t.Error(err.Error()) 78 | } 79 | 80 | if testWriter.Msg != expectedMessage { 81 | t.Errorf("The expected message on the io.Writer doesn't match actual.\n Actual: %s\n Expected: %s\n", testWriter.Msg, expectedMessage) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /structural/composite/README.md: -------------------------------------------------------------------------------- 1 | # Composite 2 | 3 | ## Elements 4 | 5 | - Component 6 | - Leaf 7 | - Composite 8 | - Client 9 | 10 | ## Description 11 | 12 | In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that is treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly. 13 | -------------------------------------------------------------------------------- /structural/composite/composite.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Person struct{ name string } 9 | 10 | type Swimmer struct{} 11 | 12 | func (s *Swimmer) Swim(name string) { 13 | fmt.Println(strings.Join([]string{ 14 | name, 15 | " is swimming", 16 | }, "")) 17 | } 18 | 19 | type IronMan struct { 20 | person Person 21 | swimmer Swimmer 22 | } 23 | 24 | func (i *IronMan) Swim() { 25 | i.swimmer.Swim(i.person.name) 26 | } 27 | 28 | func main() { 29 | ironMan := IronMan{ 30 | person: Person{"Mariottide"}, 31 | swimmer: Swimmer{}, 32 | } 33 | 34 | ironMan.Swim() 35 | } 36 | -------------------------------------------------------------------------------- /structural/decorator/README.md: -------------------------------------------------------------------------------- 1 | # Decorator 2 | 3 | ## Elements 4 | 5 | - Component 6 | - Decorator 7 | - Concrete Component 8 | - Concrete Decorator 9 | 10 | ## Description 11 | 12 | The decorator pattern provide a lot of benefits when working with legacy code. In this example legacy code is represented by a legacy recipe. A decorator add functionality and in this example decorators are new ingredients. 13 | 14 | ```go 15 | type LegacyRecipe struct { 16 | } 17 | ``` 18 | Starting from legacy code, we must have a default behavior. For example LegacyRecipe could have a method called `Decorate` that provide the recipe. 19 | 20 | ```go 21 | func (lr *LegacyRecipe) Decorate() (string, error) { 22 | return "Original behavior: ", nil 23 | } 24 | ``` 25 | 26 | All object needed to implement the decorator pattern should implement same interface. Because of we must improve the functionality of `Decorate` method we'll create an interface like this. 27 | 28 | ```go 29 | type Decorable interface { 30 | Decorate() (string, error) 31 | } 32 | ``` 33 | 34 | And because of we are treating with a legacy recipe, all new ingredient must implement a `Decorable` interface. 35 | 36 | ```go 37 | type NewIngredient struct { 38 | recipe Decorable 39 | } 40 | 41 | func (ni *NewIngredient) Decorate() (string, error) { 42 | if ni.recipe == nil { 43 | return "", errors.New("decorable recipe is needed") 44 | } 45 | s, err := ni.recipe.Decorate() 46 | if err != nil { 47 | return "", err 48 | } 49 | return fmt.Sprintf("%s %s,", s, "with decoration"), nil 50 | } 51 | ``` 52 | As you can see we can use this decorator with: 53 | 54 | ```go 55 | dec := &NewIngredient{&LegacyRecipe{}} 56 | dev.Decorate() // Original behavior: with decoration 57 | ``` 58 | -------------------------------------------------------------------------------- /structural/decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type LegacyRecipe struct { 9 | } 10 | 11 | type Decorable interface { 12 | Decorate() (string, error) 13 | } 14 | 15 | type NewIngredient struct { 16 | recipe Decorable 17 | } 18 | 19 | func (lr *LegacyRecipe) Decorate() (string, error) { 20 | return "Legacy recipe with the following ingredients:", nil 21 | } 22 | 23 | func (ni *NewIngredient) Decorate() (string, error) { 24 | if ni.recipe == nil { 25 | return "", errors.New("decorable recipe is needed") 26 | } 27 | s, err := ni.recipe.Decorate() 28 | if err != nil { 29 | return "", err 30 | } 31 | return fmt.Sprintf("%s %s,", s, "new ingredient"), nil 32 | } 33 | -------------------------------------------------------------------------------- /structural/decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestNewIngredientCannotBeInstantiateWithotDecorableObject(t *testing.T) { 10 | ni := &NewIngredient{} 11 | niResult, err := ni.Decorate() 12 | if err == nil { 13 | t.Errorf( 14 | "Decorator cant decorate "+ 15 | "without legacy object, "+ 16 | "not a string with '%s'", 17 | niResult, 18 | ) 19 | } 20 | } 21 | 22 | func TestNewIngredientMustReturnDefaultText(t *testing.T) { 23 | ni := &NewIngredient{&LegacyRecipe{}} 24 | niResult, _ := ni.Decorate() 25 | if !strings.Contains(niResult, "new ingredient") { 26 | t.Errorf( 27 | "Legacy object must contains 'new ingredient', not '%s'", 28 | niResult, 29 | ) 30 | } 31 | 32 | fmt.Println(niResult) 33 | } 34 | -------------------------------------------------------------------------------- /structural/flyweight/README.md: -------------------------------------------------------------------------------- 1 | # Flyweight 2 | 3 | ## Elements 4 | 5 | - Flyweight 6 | - ConcreteFlyweight 7 | - FlyweightFactoy 8 | - Client 9 | 10 | ## Dscription 11 | 12 | This pattern it's very commonly used in computer graphics and the video game 13 | industry. It allow sharing the state of a heavy object between many instances of 14 | some type. 15 | 16 | This example uses a creational pattern to create objects instance. 17 | 18 | ```go 19 | func TestFactoryCreatesObjects(t *testing.T) { 20 | f := NewObjectFactory() 21 | firstObject := f.GetObject(TYPE_ONE) 22 | if firstObject == nil { 23 | t.Error("The pointer to the TYPE_ONE was nil") 24 | } 25 | } 26 | 27 | ``` 28 | 29 | According to the flyweight pattern, each object requested is returned. Well, 30 | what is really returned is not an object but a pointer to that object. 31 | 32 | ```go 33 | func TestFactoryCreatesTwoObjects(t *testing.T) { 34 | f := NewObjectFactory() 35 | _ = f.GetObject(TYPE_ONE) 36 | secondObject := f.GetObject(TYPE_ONE) 37 | if secondObject == nil { 38 | t.Error("The pointer to the TYPE_ONE was nil") 39 | } 40 | } 41 | 42 | ``` 43 | 44 | Each pointer created is a different pointer. 45 | 46 | ```go 47 | func TestFactoryCreatesJustObjectOfTypes(t *testing.T) { 48 | f := NewObjectFactory() 49 | firstObject := f.GetObject(TYPE_ONE) 50 | secondObject := f.GetObject(TYPE_ONE) 51 | if firstObject != secondObject { 52 | t.Error("TYPE_ONE pointers weren't the same") 53 | } 54 | } 55 | 56 | ``` 57 | 58 | Even if object of TYPE_ONE is requested more times, the number of created 59 | objects is equals to the number of type requested. 60 | 61 | ```go 62 | func TestNumberOfObjectsIsAlwaysNumberOfTypeOfObjectCreated(t *testing.T) { 63 | f := NewObjectFactory() 64 | _ = f.GetObject(TYPE_ONE) 65 | _ = f.GetObject(TYPE_ONE) 66 | if f.GetNumberOfObjects() != 1 { 67 | t.Errorf( 68 | "The number of objects created was not 1: %d\n", 69 | f.GetNumberOfObjects(), 70 | ) 71 | } 72 | } 73 | 74 | ``` 75 | 76 | Finally, and for completeness, if two objects are requested a huge amount of 77 | time, the number of object created is still two. 78 | 79 | ```go 80 | func TestHighVolume(t *testing.T) { 81 | f := NewObjectFactory() 82 | objects := make([]*Object, 500000*2) 83 | for i := 0; i < 500000; i++ { 84 | objects[i] = f.GetObject(TYPE_ONE) 85 | } 86 | for i := 500000; i < 2*500000; i++ { 87 | objects[i] = f.GetObject(TYPE_TWO) 88 | } 89 | if f.GetNumberOfObjects() != 2 { 90 | t.Errorf( 91 | "The number of objects created was not 2: %d\n", 92 | f.GetNumberOfObjects(), 93 | ) 94 | } 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /structural/flyweight/flyweight.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | const ( 4 | TYPE_ONE = iota 5 | TYPE_TWO 6 | ) 7 | 8 | type Object struct { 9 | ID uint64 10 | Name int 11 | } 12 | 13 | type objectFactory struct { 14 | createdObjects map[int]*Object 15 | } 16 | 17 | func (f *objectFactory) GetObject(objectID int) *Object { 18 | if f.createdObjects[objectID] != nil { 19 | return f.createdObjects[objectID] 20 | } 21 | object := getObjectFactory(objectID) 22 | f.createdObjects[objectID] = &object 23 | return f.createdObjects[objectID] 24 | } 25 | 26 | func (f *objectFactory) GetNumberOfObjects() int { 27 | return len(f.createdObjects) 28 | } 29 | 30 | func getObjectFactory(object int) Object { 31 | switch object { 32 | case TYPE_TWO: 33 | return Object{ 34 | ID: 2, 35 | Name: TYPE_TWO, 36 | } 37 | default: 38 | return Object{ 39 | ID: 1, 40 | Name: TYPE_ONE, 41 | } 42 | } 43 | } 44 | 45 | func NewObjectFactory() objectFactory { 46 | return objectFactory{ 47 | createdObjects: make(map[int]*Object), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /structural/flyweight/flyweight_test.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFactoryCreatesObjects(t *testing.T) { 8 | f := NewObjectFactory() 9 | firstObject := f.GetObject(TYPE_ONE) 10 | if firstObject == nil { 11 | t.Error("The pointer to the TYPE_ONE was nil") 12 | } 13 | } 14 | 15 | func TestFactoryCreatesTwoObjects(t *testing.T) { 16 | f := NewObjectFactory() 17 | _ = f.GetObject(TYPE_ONE) 18 | secondObject := f.GetObject(TYPE_ONE) 19 | if secondObject == nil { 20 | t.Error("The pointer to the TYPE_ONE was nil") 21 | } 22 | } 23 | 24 | func TestFactoryCreatesJustObjectOfTypes(t *testing.T) { 25 | f := NewObjectFactory() 26 | firstObject := f.GetObject(TYPE_ONE) 27 | secondObject := f.GetObject(TYPE_ONE) 28 | if firstObject != secondObject { 29 | t.Error("TYPE_ONE pointers weren't the same") 30 | } 31 | } 32 | 33 | func TestNumberOfObjectsIsAlwaysNumberOfTypeOfObjectCreated(t *testing.T) { 34 | f := NewObjectFactory() 35 | _ = f.GetObject(TYPE_ONE) 36 | _ = f.GetObject(TYPE_ONE) 37 | if f.GetNumberOfObjects() != 1 { 38 | t.Errorf( 39 | "The number of objects created was not 1: %d\n", 40 | f.GetNumberOfObjects(), 41 | ) 42 | } 43 | } 44 | 45 | func TestHighVolume(t *testing.T) { 46 | f := NewObjectFactory() 47 | objects := make([]*Object, 500000*2) 48 | for i := 0; i < 500000; i++ { 49 | objects[i] = f.GetObject(TYPE_ONE) 50 | } 51 | for i := 500000; i < 2*500000; i++ { 52 | objects[i] = f.GetObject(TYPE_TWO) 53 | } 54 | if f.GetNumberOfObjects() != 2 { 55 | t.Errorf( 56 | "The number of objects created was not 2: %d\n", 57 | f.GetNumberOfObjects(), 58 | ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /structural/proxy/README.md: -------------------------------------------------------------------------------- 1 | # Proxy 2 | 3 | ## Elements 4 | 5 | - Proxy 6 | - RealSubject 7 | - Subject 8 | 9 | ## Description 10 | 11 | The design pattern usually wraps an object to hide some of its characteristics. The objectives of this patterns are ti hide an object behind the proxy so the features can be hidden, restricted and so on. Again, it provide a new abstraction layer that is easy to work with, and can be changed easily. 12 | 13 | In this example we have a database of users. 14 | 15 | ```go 16 | type User struct { 17 | ID int32 18 | } 19 | 20 | type UserList []User 21 | 22 | type UserFinder interface { 23 | FindUser(id int32) (User, error) 24 | } 25 | ``` 26 | 27 | We dont use a slice of users because declaring a sequence of structs in this way it possibile to implement the interface UserFinder. A slice cant implement an interface. 28 | 29 | And here our proxy. A proxy here contains the database and the cache as UserList. The capacity limit the size of cache. All access to the data will be asked to this proxy. The proxy will save in cache new records. When a record will be asked first will be checked the presence in cache, then in database. 30 | 31 | ```go 32 | type UserListProxy struct { 33 | SomeDatabase UserList 34 | StackCache UserList 35 | StackCapacity int 36 | } 37 | ``` 38 | 39 | As we said before, we used UserList as sequence of User to implement UserFinder interface. This because our database and our stack are both sequence of user. 40 | 41 | ```go 42 | func (u *UserListProxy) FindUser(ID int32) (User, error) { 43 | user, err := u.StackCache.FindUser(ID) 44 | if err == nil { 45 | fmt.Println("Returning user from cache") 46 | u.DidLastSearchUsedCache = true 47 | return user, nil 48 | } 49 | 50 | user, err = u.SomeDatabase.FindUser(ID) 51 | if err == nil { 52 | fmt.Println("Returning user from database") 53 | u.addUserToStack(user) 54 | u.DidLastSearchUsedCache = false 55 | return user, nil 56 | } 57 | 58 | return User{}, fmt.Errorf("User not found") 59 | } 60 | 61 | func (u *UserList) FindUser(id int32) (User, error) { 62 | for i := 0; i < len(*u); i++ { 63 | if (*u)[i].ID == id { 64 | return (*u)[i], nil 65 | } 66 | } 67 | return User{}, fmt.Errorf("User %s could not be found\n", id) 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /structural/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type User struct { 8 | ID int32 9 | } 10 | 11 | type UserFinder interface { 12 | FindUser(id int32) (User, error) 13 | } 14 | 15 | type UserList []User 16 | 17 | type UserListProxy struct { 18 | SomeDatabase UserList 19 | StackCache UserList 20 | StackCapacity int 21 | DidLastSearchUsedCache bool 22 | } 23 | 24 | func (u *UserListProxy) FindUser(ID int32) (User, error) { 25 | user, err := u.StackCache.FindUser(ID) 26 | if err == nil { 27 | fmt.Println("Returning user from cache") 28 | u.DidLastSearchUsedCache = true 29 | return user, nil 30 | } 31 | 32 | user, err = u.SomeDatabase.FindUser(ID) 33 | if err == nil { 34 | fmt.Println("Returning user from database") 35 | u.addUserToStack(user) 36 | u.DidLastSearchUsedCache = false 37 | return user, nil 38 | } 39 | 40 | return User{}, fmt.Errorf("User not found") 41 | } 42 | 43 | func (u *UserListProxy) addUserToStack(user User) { 44 | if len(u.StackCache) >= u.StackCapacity { 45 | u.StackCache = append(u.StackCache[1:], user) 46 | } else { 47 | u.StackCache.addUser(user) 48 | } 49 | } 50 | 51 | func (u *UserList) addUser(newUser User) { 52 | *u = append(*u, newUser) 53 | } 54 | 55 | func (u *UserList) FindUser(id int32) (User, error) { 56 | for i := 0; i < len(*u); i++ { 57 | if (*u)[i].ID == id { 58 | return (*u)[i], nil 59 | } 60 | } 61 | return User{}, fmt.Errorf("User %d could not be found\n", id) 62 | } 63 | -------------------------------------------------------------------------------- /structural/proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func TestUserListProxy(t *testing.T) { 9 | someDatabase := UserList{} 10 | rand.Seed(2342342) 11 | for i := 0; i < 1000000; i++ { 12 | n := rand.Int31() 13 | someDatabase = append(someDatabase, User{ID: n}) 14 | } 15 | 16 | proxy := UserListProxy{ 17 | SomeDatabase: someDatabase, 18 | StackCapacity: 2, 19 | StackCache: UserList{}, 20 | } 21 | 22 | knownIDs := [3]int32{ 23 | someDatabase[3].ID, 24 | someDatabase[4].ID, 25 | someDatabase[5].ID, 26 | } 27 | 28 | t.Run("FindUser = Empty cache", func(t *testing.T) { 29 | user, err := proxy.FindUser(knownIDs[0]) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | if user.ID != knownIDs[0] { 34 | t.Error("Returned user name doesnt match with expected") 35 | } 36 | if len(proxy.StackCache) != 1 { 37 | t.Error("After one successful search in an empty cache, the size of it must be one") 38 | } 39 | if proxy.DidLastSearchUsedCache { 40 | t.Error("No user can be returned frmo an emtpy cache") 41 | } 42 | }) 43 | 44 | t.Run("FindUser = One user ask for same user", func(t *testing.T) { 45 | _, err := proxy.FindUser(knownIDs[0]) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | if !proxy.DidLastSearchUsedCache { 50 | t.Error("No user can be returned frmo an emtpy cache") 51 | } 52 | }) 53 | } 54 | --------------------------------------------------------------------------------