├── .gitignore ├── .golangci.yaml ├── .travis.yml ├── LICENSE ├── README.md ├── _config.yml ├── appveyor.yml ├── behavioral ├── chain_of_responsibility.go ├── chain_of_responsibility_test.go ├── command.go ├── command_test.go ├── doc.go ├── interpreter.go ├── interpreter_test.go ├── iterator.go ├── iterator_test.go ├── mediator.go ├── mediator_test.go ├── memento.go ├── memento_test.go ├── observer.go ├── observer_test.go ├── state.go ├── state_test.go ├── strategy.go ├── strategy_test.go ├── template_method.go ├── template_method_test.go ├── visitor.go └── visitor_test.go ├── creational ├── abstract_factory.go ├── abstract_factory_test.go ├── builder.go ├── builder_test.go ├── doc.go ├── factory_method.go ├── factory_method_test.go ├── object_pool.go ├── object_pool_test.go ├── prototype.go ├── prototype_test.go ├── singleton.go └── singleton_test.go ├── go.mod ├── go.sum ├── jigsaw.png ├── patterns.go ├── structural ├── adapter.go ├── adapter_test.go ├── bridge.go ├── bridge_test.go ├── composite.go ├── composite_test.go ├── decorator.go ├── decorator_test.go ├── doc.go ├── facade.go ├── facade_test.go ├── flyweight.go ├── flyweight_test.go ├── proxy.go └── proxy_test.go └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # Gogland IDE 17 | .idea 18 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | issues-exit-code: #Default 3 | tests: true #Default 4 | 5 | linters: 6 | enable: 7 | - errcheck 8 | - gofmt 9 | - goimports 10 | - gosec 11 | - govet 12 | - ineffassign 13 | - misspell 14 | - nakedret 15 | - staticcheck 16 | - unused 17 | - whitespace 18 | 19 | linter-settings: 20 | gofmt: 21 | simplify: true 22 | 23 | issues: 24 | exclude-rules: 25 | - path: _test\.go 26 | linters: 27 | - errcheck -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | os: 4 | - linux 5 | 6 | go: 7 | - "1.20.x" 8 | 9 | install: 10 | - echo $PATH 11 | - echo $GOPATH 12 | - go version 13 | - go env 14 | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.52.2 15 | - golangci-lint --version 16 | 17 | script: 18 | - ./test.sh 19 | - golangci-lint run ./... 20 | 21 | after_success: 22 | - bash <(curl -s https://codecov.io/bash) 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ben Wells 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns for Go 2 | 3 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/bvwells/go-patterns?tab=overview) 4 | ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/bvwells/go-patterns) 5 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bvwells/go-patterns) 6 | [![Build Status](https://app.travis-ci.com/bvwells/go-patterns.svg?branch=master)](https://app.travis-ci.com/bvwells/go-patterns) 7 | [![Build status](https://ci.appveyor.com/api/projects/status/ea2u4hpy555b6ady?svg=true)](https://ci.appveyor.com/project/bvwells/go-patterns) 8 | [![codecov](https://codecov.io/gh/bvwells/go-patterns/branch/master/graph/badge.svg)](https://codecov.io/gh/bvwells/go-patterns) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/bvwells/go-patterns)](https://goreportcard.com/report/github.com/bvwells/go-patterns) 10 | 11 | Design patterns for the Go programming language. 12 | 13 | ![Gopher jigsaw](jigsaw.png) 14 | 15 | ``` go 16 | import "github.com/bvwells/go-patterns" 17 | ``` 18 | 19 | To install the packages on your system, 20 | 21 | ``` 22 | $ go get -u github.com/bvwells/go-patterns/... 23 | ``` 24 | 25 | Documentation and examples are available at https://pkg.go.dev/github.com/bvwells/go-patterns?tab=overview 26 | 27 | * [Design Patterns](#design-patterns) 28 | * [Creational](#creational) 29 | * [Structural](#structural) 30 | * [Behavioral](#behavioral) 31 | * [Go Versions Supported](#go-versions-supported) 32 | 33 | ## Design Patterns 34 | 35 | Pattern | Package | Description 36 | -----------|-------------------------------------------|------------ 37 | Creational | [`creational`][creational-ref] | Creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or in added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation. 38 | Structural | [`structural`][structural-ref] | Structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities. 39 | Behavioral | [`behavioral`][behavioral-ref] | Behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication. 40 | 41 | ## Creational [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/bvwells/go-patterns/creational?tab=overview) 42 | 43 | Name | Description 44 | -----------|------------------------------------------- 45 | [`Abstract Factory`](./creational/abstract_factory.go) | Provide an interface for creating families of related or dependent objects without specifying their concrete classes. 46 | [`Builder`](./creational/builder.go) | Separate the construction of a complex object from its representation, allowing the same construction process to create various representations. 47 | [`Factory Method`](./creational/factory_method.go) | Define an interface for creating a single object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 48 | [`Object Pool`](./creational/object_pool.go) | Avoid expensive acquisition and release of resources by recycling objects that are no longer in use. Can be considered a generalisation of connection pool and thread pool patterns. 49 | [`Prototype`](./creational/prototype.go) | Specify the kinds of objects to create using a prototypical instance, and create new objects from the 'skeleton' of an existing object, thus boosting performance and keeping memory footprints to a minimum. 50 | [`Singleton`](./creational/singleton.go) | Ensure a class has only one instance, and provide a global point of access to it. 51 | 52 | ## Structural [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/bvwells/go-patterns/structural?tab=overview) 53 | 54 | Name | Description 55 | -----------|------------------------------------------- 56 | [`Adapter`](./structural/adapter.go) | Convert the interface of a class into another interface clients expect. An adapter lets classes work together that could not otherwise because of incompatible interfaces. The enterprise integration pattern equivalent is the translator. 57 | [`Bridge`](./structural/bridge.go) | Decouple an abstraction from its implementation allowing the two to vary independently. 58 | [`Composite`](./structural/composite.go) | Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 59 | [`Decorator`](./structural/decorator.go) | Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality. 60 | [`Facade`](./structural/facade.go) | Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. 61 | [`Flyweight`](./structural/flyweight.go) | Use sharing to support large numbers of similar objects efficiently. 62 | [`Proxy`](./structural/proxy.go) | Provide a surrogate or placeholder for another object to control access to it. 63 | 64 | ## Behavioral [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/bvwells/go-patterns/behavioral?tab=overview) 65 | 66 | Name | Description 67 | -----------|------------------------------------------- 68 | [`Chain of Responsibility`](./behavioral/chain_of_responsibility.go) | Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. 69 | [`Command`](./behavioral/command.go) | Encapsulate a request as an object, thereby allowing for the parameterization of clients with different requests, and the queuing or logging of requests. It also allows for the support of undoable operations. 70 | [`Interpreter`](./behavioral/interpreter.go) | Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. 71 | [`Iterator`](./behavioral/iterator.go) | Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 72 | [`Mediator`](./behavioral/mediator.go) | Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to vary independently. 73 | [`Memento`](./behavioral/memento.go) | Without violating encapsulation, capture and externalize an object's internal state allowing the object to be restored to this state later. 74 | [`Observer`](./behavioral/observer.go) | Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically. 75 | [`State`](./behavioral/state.go) | Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. 76 | [`Strategy`](./behavioral/strategy.go) | Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 77 | [`Template Method`](./behavioral/template_method.go) | Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. 78 | [`Visitor`](./behavioral/visitor.go) | Represent an operation to be performed on the elements of an object structure. Visitor lets a new operation be defined without changing the classes of the elements on which it operates. 79 | 80 | ## Go Versions Supported 81 | 82 | The most recent major version of Go is supported. You can see which versions are 83 | currently supported by looking at the lines following `go:` in 84 | [`.travis.yml`](.travis.yml). 85 | 86 | [creational-ref]: https://pkg.go.dev/github.com/bvwells/go-patterns/creational?tab=overview 87 | [structural-ref]: https://pkg.go.dev/github.com/bvwells/go-patterns/structural?tab=overview 88 | [behavioral-ref]: https://pkg.go.dev/github.com/bvwells/go-patterns/behavioral?tab=overview 89 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | build: off 4 | 5 | clone_folder: c:\github.com\bvwells\go-patterns 6 | 7 | stack: go 1.20.3 8 | 9 | install: 10 | - echo %PATH% 11 | - echo %GOPATH% 12 | - go version 13 | - go env 14 | 15 | test_script: 16 | - go test -race -v ./... 17 | -------------------------------------------------------------------------------- /behavioral/chain_of_responsibility.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // Handler defined a handler for handling a given handleID. 8 | type Handler interface { 9 | Handle(handleID int) string 10 | } 11 | 12 | type handler struct { 13 | name string 14 | next Handler 15 | handleID int 16 | } 17 | 18 | // NewHandler returns a new Handler. 19 | func NewHandler(name string, next Handler, handleID int) Handler { 20 | return &handler{name, next, handleID} 21 | } 22 | 23 | // Handle handles a given handleID. 24 | func (h *handler) Handle(handleID int) string { 25 | if h.handleID == handleID { 26 | return h.name + " handled " + strconv.Itoa(handleID) 27 | } 28 | return h.next.Handle(handleID) 29 | } 30 | -------------------------------------------------------------------------------- /behavioral/chain_of_responsibility_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewHandler_ReturnsNonNil(t *testing.T) { 10 | t.Parallel() 11 | h := NewHandler("Barry", nil, 1) 12 | assert.NotNil(t, h) 13 | } 14 | 15 | func TestNewHandler_SetsName(t *testing.T) { 16 | t.Parallel() 17 | name := "Barry" 18 | h := NewHandler(name, nil, 1) 19 | assert.NotNil(t, h.(*handler).name, name) 20 | } 21 | 22 | func TestNewHandler_SetsNext(t *testing.T) { 23 | t.Parallel() 24 | next := handler{} 25 | h := NewHandler("Barry", &next, 1) 26 | assert.NotNil(t, h.(*handler).next, next) 27 | } 28 | 29 | func TestNewHandler_SetsRequestID(t *testing.T) { 30 | t.Parallel() 31 | handleID := 1234 32 | h := NewHandler("Barry", nil, handleID) 33 | assert.NotNil(t, h.(*handler).handleID, handleID) 34 | } 35 | 36 | func TestHandle_FirstHandlerHandleRequest(t *testing.T) { 37 | t.Parallel() 38 | barry := NewHandler("Barry", nil, 1) 39 | paul := NewHandler("Paul", barry, 2) 40 | expected := "Paul handled 2" 41 | actual := paul.Handle(2) 42 | assert.Equal(t, expected, actual) 43 | } 44 | 45 | func TestHandle_SecondHanlderHandlesRequest(t *testing.T) { 46 | t.Parallel() 47 | barry := NewHandler("Barry", nil, 1) 48 | paul := NewHandler("Paul", barry, 2) 49 | expected := "Barry handled 1" 50 | actual := paul.Handle(1) 51 | assert.Equal(t, expected, actual) 52 | } 53 | -------------------------------------------------------------------------------- /behavioral/command.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import "fmt" 4 | 5 | // Command is a command which when executed on a person will call a given method. 6 | type Command struct { 7 | person *Person 8 | method func() 9 | } 10 | 11 | // NewCommand creates a new Command. 12 | func NewCommand(person *Person, method func()) Command { 13 | return Command{person, method} 14 | } 15 | 16 | // Execute executes the command. 17 | func (c *Command) Execute() { 18 | c.method() 19 | } 20 | 21 | // Person with a given name and command to execute. 22 | type Person struct { 23 | name string 24 | cmd Command 25 | } 26 | 27 | // NewPerson creates a new Person. 28 | func NewPerson(name string, cmd Command) Person { 29 | return Person{name, cmd} 30 | } 31 | 32 | // Talk talks and the executes the follow on command. 33 | func (p *Person) Talk() { 34 | fmt.Fprintf(outputWriter, "%s is talking.\n", p.name) 35 | p.cmd.Execute() 36 | } 37 | 38 | // PassOn passes on by executing the follow on command. 39 | func (p *Person) PassOn() { 40 | fmt.Fprintf(outputWriter, "%s is passing on.\n", p.name) 41 | p.cmd.Execute() 42 | } 43 | 44 | // Gossip gossips and then executes follow on command. 45 | func (p *Person) Gossip() { 46 | fmt.Fprintf(outputWriter, "%s is gossiping.\n", p.name) 47 | p.cmd.Execute() 48 | } 49 | 50 | // Listen listens without executing follow on command. 51 | func (p *Person) Listen() { 52 | fmt.Fprintf(outputWriter, "%s is listening.\n", p.name) 53 | } 54 | -------------------------------------------------------------------------------- /behavioral/command_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestTalk(t *testing.T) { 11 | bufferOutputWriter := outputWriter 12 | outputWriter = new(bytes.Buffer) 13 | defer func() { outputWriter = bufferOutputWriter }() 14 | 15 | // Fred will "Execute" Barney which will result in a call to PassOn() 16 | // Barney will "Execute" Betty which will result in a call to Gossip() 17 | // Betty will "Execute" Wilma which will result in a call to Listen() 18 | wilma := NewPerson("Wilma", NewCommand(nil, nil)) 19 | betty := NewPerson("Betty", NewCommand(&wilma, wilma.Listen)) 20 | barney := NewPerson("Barney", NewCommand(&betty, betty.Gossip)) 21 | fred := NewPerson("Fred", NewCommand(&barney, barney.PassOn)) 22 | fred.Talk() 23 | 24 | assert.Equal(t, "Fred is talking.\n"+ 25 | "Barney is passing on.\n"+ 26 | "Betty is gossiping.\n"+ 27 | "Wilma is listening.\n", outputWriter.(*bytes.Buffer).String()) 28 | } 29 | -------------------------------------------------------------------------------- /behavioral/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package behavioral describes behavioral design patterns. 3 | The design patterns described are all related to communication between objects. 4 | */ 5 | package behavioral 6 | -------------------------------------------------------------------------------- /behavioral/interpreter.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Expression represents an expression to evaluate. 8 | type Expression interface { 9 | Interpret(variables map[string]Expression) int 10 | } 11 | 12 | // Integer represents an integer number. 13 | type Integer struct { 14 | integer int 15 | } 16 | 17 | // Interpret returns the integer representation of the number. 18 | func (n *Integer) Interpret(variables map[string]Expression) int { 19 | return n.integer 20 | } 21 | 22 | // Plus represents the addition operation. 23 | type Plus struct { 24 | leftOperand Expression 25 | rightOperand Expression 26 | } 27 | 28 | // Interpret interprets by adding the left and right variables. 29 | func (p *Plus) Interpret(variables map[string]Expression) int { 30 | return p.leftOperand.Interpret(variables) + p.rightOperand.Interpret(variables) 31 | } 32 | 33 | // Minus represents the subtraction operation. 34 | type Minus struct { 35 | leftOperand Expression 36 | rightOperand Expression 37 | } 38 | 39 | // Interpret interprets by subtracting the right from left variables. 40 | func (m *Minus) Interpret(variables map[string]Expression) int { 41 | return m.leftOperand.Interpret(variables) - m.rightOperand.Interpret(variables) 42 | } 43 | 44 | // Variable represents a variable. 45 | type Variable struct { 46 | name string 47 | } 48 | 49 | // Interpret looks up the variable value and returns it, if not found returns zero. 50 | func (v *Variable) Interpret(variables map[string]Expression) int { 51 | value, found := variables[v.name] 52 | if !found { 53 | return 0 54 | } 55 | return value.Interpret(variables) 56 | } 57 | 58 | // Evaluator evaluates the expression. 59 | type Evaluator struct { 60 | syntaxTree Expression 61 | } 62 | 63 | // NewEvaluator creates a new Evaluator. 64 | func NewEvaluator(expression string) *Evaluator { 65 | expressionStack := new(Stack) 66 | for _, token := range strings.Split(expression, " ") { 67 | switch token { 68 | case "+": 69 | right := expressionStack.Pop().(Expression) 70 | left := expressionStack.Pop().(Expression) 71 | subExpression := &Plus{left, right} 72 | expressionStack.Push(subExpression) 73 | case "-": 74 | right := expressionStack.Pop().(Expression) 75 | left := expressionStack.Pop().(Expression) 76 | subExpression := &Minus{left, right} 77 | expressionStack.Push(subExpression) 78 | default: 79 | expressionStack.Push(&Variable{token}) 80 | } 81 | } 82 | syntaxTree := expressionStack.Pop().(Expression) 83 | return &Evaluator{syntaxTree} 84 | } 85 | 86 | // Interpret interprets the expression syntax tree. 87 | func (e *Evaluator) Interpret(context map[string]Expression) int { 88 | return e.syntaxTree.Interpret(context) 89 | } 90 | 91 | // Node represents a node in the stack. 92 | type Node struct { 93 | value interface{} 94 | next *Node 95 | } 96 | 97 | // Stack represents a stack with push and pop operations. 98 | type Stack struct { 99 | top *Node 100 | size int 101 | } 102 | 103 | // Push pushes a new value into the stack. 104 | func (s *Stack) Push(value interface{}) { 105 | s.top = &Node{value, s.top} 106 | s.size++ 107 | } 108 | 109 | // Pop pops a value out the stack. 110 | func (s *Stack) Pop() interface{} { 111 | if s.size == 0 { 112 | return nil 113 | } 114 | value := s.top.value 115 | s.top = s.top.next 116 | s.size-- 117 | return value 118 | } 119 | -------------------------------------------------------------------------------- /behavioral/interpreter_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestInterpreter(t *testing.T) { 10 | t.Parallel() 11 | expression := "w x z - +" 12 | sentence := NewEvaluator(expression) 13 | variables := make(map[string]Expression) 14 | variables["w"] = &Integer{5} 15 | variables["x"] = &Integer{10} 16 | variables["z"] = &Integer{42} 17 | result := sentence.Interpret(variables) 18 | assert.Equal(t, -27, result) 19 | } 20 | -------------------------------------------------------------------------------- /behavioral/iterator.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | // Iterator is an interface for an iterator. 4 | type Iterator interface { 5 | 6 | // Index returns the index of the current iterator. 7 | Index() int 8 | 9 | // Value returns the current value of the iterator. 10 | Value() interface{} 11 | 12 | // HasNext returns whether another next element exists. 13 | HasNext() bool 14 | 15 | // Next increments the iterator to point to the next element. 16 | Next() 17 | } 18 | 19 | // ArrayIterator is an iterator which iterates over an array. 20 | type ArrayIterator struct { 21 | array []interface{} 22 | index int 23 | } 24 | 25 | // Index returns the index of the current iterator. 26 | func (i *ArrayIterator) Index() int { 27 | return i.index 28 | } 29 | 30 | // Value returns the current value of the iterator. 31 | func (i *ArrayIterator) Value() interface{} { 32 | return i.array[i.index] 33 | } 34 | 35 | // HasNext returns whether another next element exists. 36 | func (i *ArrayIterator) HasNext() bool { 37 | return i.index+1 != len(i.array) 38 | } 39 | 40 | // Next increments the iterator to point to the next element. 41 | func (i *ArrayIterator) Next() { 42 | if i.HasNext() { 43 | i.index++ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /behavioral/iterator_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIterator(t *testing.T) { 8 | t.Parallel() 9 | 10 | array := []interface{}{10.0, 20.0, 30.0, 40.0, 50.0} 11 | 12 | iterator := ArrayIterator{array, 0} 13 | 14 | for it := &iterator; iterator.HasNext(); iterator.Next() { 15 | index, value := it.Index(), it.Value().(float64) 16 | if value != array[index] { 17 | t.Errorf("Expected array value to equal %v, but received %v", array[index], value) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /behavioral/mediator.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import "fmt" 4 | 5 | // WildStallion describes an interface for a Wild Stallion band member. 6 | type WildStallion interface { 7 | SetMediator(mediator Mediator) 8 | } 9 | 10 | // Bill describes Bill S. Preston, Esquire. 11 | type Bill struct { 12 | mediator Mediator 13 | } 14 | 15 | // SetMediator sets the mediator. 16 | func (b *Bill) SetMediator(mediator Mediator) { 17 | b.mediator = mediator 18 | } 19 | 20 | // Respond responds. 21 | func (b *Bill) Respond() { 22 | fmt.Fprintf(outputWriter, "Bill: What?\n") 23 | b.mediator.Communicate("Bill") 24 | } 25 | 26 | // Ted describes Ted "Theodore" Logan. 27 | type Ted struct { 28 | mediator Mediator 29 | } 30 | 31 | // SetMediator sets the mediator. 32 | func (t *Ted) SetMediator(mediator Mediator) { 33 | t.mediator = mediator 34 | } 35 | 36 | // Talk talks through mediator. 37 | func (t *Ted) Talk() { 38 | fmt.Fprintf(outputWriter, "Ted: Bill?\n") 39 | t.mediator.Communicate("Ted") 40 | } 41 | 42 | // Respond responds. 43 | func (t *Ted) Respond() { 44 | fmt.Fprintf(outputWriter, "Ted: Strange things are afoot at the Circle K.\n") 45 | } 46 | 47 | // Mediator describes the interface for communicating between Wild Stallion band members. 48 | type Mediator interface { 49 | Communicate(who string) 50 | } 51 | 52 | // ConcreateMediator describes a mediator between Bill and Ted. 53 | type ConcreateMediator struct { 54 | Bill 55 | Ted 56 | } 57 | 58 | // NewMediator creates a new ConcreateMediator. 59 | func NewMediator() *ConcreateMediator { 60 | mediator := &ConcreateMediator{} 61 | mediator.Bill.SetMediator(mediator) 62 | mediator.Ted.SetMediator(mediator) 63 | return mediator 64 | } 65 | 66 | // Communicate communicates between Bill and Ted. 67 | func (m *ConcreateMediator) Communicate(who string) { 68 | if who == "Ted" { 69 | m.Bill.Respond() 70 | } else if who == "Bill" { 71 | m.Ted.Respond() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /behavioral/mediator_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMediator(t *testing.T) { 11 | bufferOutputWriter := outputWriter 12 | outputWriter = new(bytes.Buffer) 13 | defer func() { outputWriter = bufferOutputWriter }() 14 | 15 | mediator := NewMediator() 16 | mediator.Ted.Talk() 17 | 18 | assert.Equal(t, "Ted: Bill?\n"+ 19 | "Bill: What?\n"+ 20 | "Ted: Strange things are afoot at the Circle K.\n", outputWriter.(*bytes.Buffer).String()) 21 | } 22 | -------------------------------------------------------------------------------- /behavioral/memento.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | // Memento stores the state of the Number. 4 | type Memento struct { 5 | state int 6 | } 7 | 8 | // NewMemento creates a new memento. 9 | func NewMemento(value int) *Memento { 10 | return &Memento{value} 11 | } 12 | 13 | // Number represents an integer which can be operated on. 14 | type Number struct { 15 | value int 16 | } 17 | 18 | // NewNumber creates a new Number. 19 | func NewNumber(value int) *Number { 20 | return &Number{value} 21 | } 22 | 23 | // Dubble doubles the value of the number. 24 | func (n *Number) Dubble() { 25 | n.value = 2 * n.value 26 | } 27 | 28 | // Half halves the value of the number. 29 | func (n *Number) Half() { 30 | n.value /= 2 31 | } 32 | 33 | // Value returns the value of the number. 34 | func (n *Number) Value() int { 35 | return n.value 36 | } 37 | 38 | // CreateMemento creates a Memento with the current state of the number. 39 | func (n *Number) CreateMemento() *Memento { 40 | return NewMemento(n.value) 41 | } 42 | 43 | // ReinstateMemento reinstates the value of the Number to the value of the memento. 44 | func (n *Number) ReinstateMemento(memento *Memento) { 45 | n.value = memento.state 46 | } 47 | -------------------------------------------------------------------------------- /behavioral/memento_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMemento(t *testing.T) { 10 | t.Parallel() 11 | 12 | n := NewNumber(10) 13 | n.Dubble() 14 | assert.Equal(t, 20, n.Value()) 15 | 16 | memento := n.CreateMemento() 17 | 18 | n.Half() 19 | assert.Equal(t, 10, n.Value()) 20 | 21 | n.ReinstateMemento(memento) 22 | assert.Equal(t, 20, n.Value()) 23 | } 24 | -------------------------------------------------------------------------------- /behavioral/observer.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | var outputWriter io.Writer = os.Stdout // modified during testing 10 | 11 | // Event describes an event to observe and notify on. 12 | type Event struct { 13 | id string 14 | } 15 | 16 | // EventObserver describes an interface for observing events. 17 | type EventObserver interface { 18 | OnNotify(event Event) 19 | } 20 | 21 | // observer is an implementation of the EventObserver interface. 22 | type observer struct { 23 | name string 24 | } 25 | 26 | // NewEventObserver returns a new instance of an EventObserver. 27 | func NewEventObserver(name string) EventObserver { 28 | return &observer{name} 29 | } 30 | 31 | // OnNotify logs the event being notified on. 32 | func (o *observer) OnNotify(event Event) { 33 | fmt.Fprintf(outputWriter, "observer '%s' received event '%s'\n", o.name, event.id) 34 | } 35 | 36 | // EventNotifier describes an interface for registering and de-registering observers to 37 | // be notified when an event occurs. 38 | type EventNotifier interface { 39 | Register(obs EventObserver) 40 | Deregister(obs EventObserver) 41 | Notify(event Event) 42 | } 43 | 44 | // eventNotifer is an implementation of the EventNotifier interface. 45 | type eventNotifer struct { 46 | observers []EventObserver 47 | } 48 | 49 | // NewEventNotifier returns a new instance of an EventNotifier. 50 | func NewEventNotifier() EventNotifier { 51 | return &eventNotifer{} 52 | } 53 | 54 | // Register registers a new observer for notifying on. 55 | func (e *eventNotifer) Register(obs EventObserver) { 56 | e.observers = append(e.observers, obs) 57 | } 58 | 59 | // Deregister de-registers an observer for notifying on. 60 | func (e *eventNotifer) Deregister(obs EventObserver) { 61 | for i := 0; i < len(e.observers); i++ { 62 | if obs == e.observers[i] { 63 | e.observers = append(e.observers[:i], e.observers[i+1:]...) 64 | } 65 | } 66 | } 67 | 68 | // Notify notifies all observers on an event. 69 | func (e *eventNotifer) Notify(event Event) { 70 | for i := 0; i < len(e.observers); i++ { 71 | e.observers[i].OnNotify(event) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /behavioral/observer_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewEventObserver_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | observer := NewEventObserver("peeping tom") 13 | assert.NotNil(t, observer) 14 | } 15 | 16 | func TestOnNotify_LogsMessage(t *testing.T) { 17 | bufferOutputWriter := outputWriter 18 | outputWriter = new(bytes.Buffer) 19 | defer func() { outputWriter = bufferOutputWriter }() 20 | 21 | event := Event{"something happened"} 22 | observer := NewEventObserver("peeping tom") 23 | observer.OnNotify(event) 24 | 25 | assert.Equal(t, "observer 'peeping tom' received event 'something happened'\n", outputWriter.(*bytes.Buffer).String()) 26 | } 27 | 28 | func TestNewEventNotifer_ReturnsNonNil(t *testing.T) { 29 | t.Parallel() 30 | notifier := NewEventNotifier() 31 | assert.NotNil(t, notifier) 32 | } 33 | 34 | func TestRegister_RegistersObservers(t *testing.T) { 35 | t.Parallel() 36 | notifier := NewEventNotifier() 37 | observers := []EventObserver{ 38 | NewEventObserver("tom"), 39 | NewEventObserver("dick"), 40 | NewEventObserver("harry"), 41 | } 42 | 43 | for i := 0; i < len(observers); i++ { 44 | notifier.Register(observers[i]) 45 | } 46 | 47 | assert.Len(t, notifier.(*eventNotifer).observers, len(observers)) 48 | for i := 0; i < len(observers); i++ { 49 | assert.Equal(t, observers[i], notifier.(*eventNotifer).observers[i]) 50 | } 51 | } 52 | 53 | func TestDeregister_DeregistersObservers(t *testing.T) { 54 | t.Parallel() 55 | notifier := NewEventNotifier() 56 | observers := []EventObserver{ 57 | NewEventObserver("tom"), 58 | NewEventObserver("dick"), 59 | NewEventObserver("harry"), 60 | } 61 | 62 | for i := 0; i < len(observers); i++ { 63 | notifier.Register(observers[i]) 64 | } 65 | 66 | notifier.Deregister(observers[0]) 67 | notifier.Deregister(observers[2]) 68 | 69 | assert.Len(t, notifier.(*eventNotifer).observers, 1) 70 | assert.Equal(t, observers[1], notifier.(*eventNotifer).observers[0]) 71 | } 72 | 73 | func TestNotify_NotifiesObservers(t *testing.T) { 74 | bufferOutputWriter := outputWriter 75 | outputWriter = new(bytes.Buffer) 76 | defer func() { outputWriter = bufferOutputWriter }() 77 | 78 | notifier := NewEventNotifier() 79 | observers := []EventObserver{ 80 | NewEventObserver("tom"), 81 | NewEventObserver("dick"), 82 | NewEventObserver("harry"), 83 | } 84 | 85 | for i := 0; i < len(observers); i++ { 86 | notifier.Register(observers[i]) 87 | } 88 | 89 | notifier.Notify(Event{"birthday!"}) 90 | 91 | assert.Equal(t, "observer 'tom' received event 'birthday!'\n"+ 92 | "observer 'dick' received event 'birthday!'\n"+ 93 | "observer 'harry' received event 'birthday!'\n", outputWriter.(*bytes.Buffer).String()) 94 | } 95 | -------------------------------------------------------------------------------- /behavioral/state.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Machine defines a machine which can be swwitched on and off. 8 | type Machine struct { 9 | current State 10 | } 11 | 12 | // NewMachine creates a new machine. 13 | func NewMachine() *Machine { 14 | fmt.Fprintf(outputWriter, "Machine is ready.\n") 15 | return &Machine{NewOFF()} 16 | } 17 | 18 | // setCurrent sets the current state of the machine. 19 | func (m *Machine) setCurrent(s State) { 20 | m.current = s 21 | } 22 | 23 | // On pushes the on button. 24 | func (m *Machine) On() { 25 | m.current.On(m) 26 | } 27 | 28 | // Off pushes the off button. 29 | func (m *Machine) Off() { 30 | m.current.Off(m) 31 | } 32 | 33 | // State describes the internal state of the machine. 34 | type State interface { 35 | On(m *Machine) 36 | Off(m *Machine) 37 | } 38 | 39 | // ON describes the on button state. 40 | type ON struct { 41 | } 42 | 43 | // NewON creates a new ON state. 44 | func NewON() State { 45 | return &ON{} 46 | } 47 | 48 | // On does nothing. 49 | func (o *ON) On(m *Machine) { 50 | fmt.Fprintf(outputWriter, " already ON\n") 51 | } 52 | 53 | // Off switches the state from on to off. 54 | func (o *ON) Off(m *Machine) { 55 | fmt.Fprintf(outputWriter, " going from ON to OFF\n") 56 | m.setCurrent(NewOFF()) 57 | } 58 | 59 | // OFF describes the off button state. 60 | type OFF struct { 61 | } 62 | 63 | // NewOFF creates a new OFF state. 64 | func NewOFF() State { 65 | return &OFF{} 66 | } 67 | 68 | // On switches the state from off to on. 69 | func (o *OFF) On(m *Machine) { 70 | fmt.Fprintf(outputWriter, " going from OFF to ON\n") 71 | m.setCurrent(NewON()) 72 | } 73 | 74 | // Off does nothing. 75 | func (o *OFF) Off(m *Machine) { 76 | fmt.Fprintf(outputWriter, " already OFF\n") 77 | } 78 | -------------------------------------------------------------------------------- /behavioral/state_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestState(t *testing.T) { 11 | bufferOutputWriter := outputWriter 12 | outputWriter = new(bytes.Buffer) 13 | defer func() { outputWriter = bufferOutputWriter }() 14 | 15 | machine := NewMachine() 16 | machine.Off() 17 | machine.On() 18 | machine.On() 19 | machine.Off() 20 | 21 | assert.Equal(t, "Machine is ready.\n"+ 22 | " already OFF\n"+ 23 | " going from OFF to ON\n"+ 24 | " already ON\n"+ 25 | " going from ON to OFF\n", outputWriter.(*bytes.Buffer).String()) 26 | } 27 | -------------------------------------------------------------------------------- /behavioral/strategy.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import "fmt" 4 | 5 | // Strategy defines the interface for the strategy to execute. 6 | type Strategy interface { 7 | Execute() 8 | } 9 | 10 | // strategyA defines an implementation of a Strategy to execute. 11 | type strategyA struct { 12 | } 13 | 14 | // NewStrategyA creates a new instance of strategy A. 15 | func NewStrategyA() Strategy { 16 | return &strategyA{} 17 | } 18 | 19 | // Execute executes strategy A. 20 | func (s *strategyA) Execute() { 21 | fmt.Fprintf(outputWriter, "executing strategy A\n") 22 | } 23 | 24 | // strategyB defines an implementation of a Strategy to execute. 25 | type strategyB struct { 26 | } 27 | 28 | // NewStrategyB creates a new instance of strategy B. 29 | func NewStrategyB() Strategy { 30 | return &strategyB{} 31 | } 32 | 33 | // Execute executes strategy B. 34 | func (s *strategyB) Execute() { 35 | fmt.Fprintf(outputWriter, "executing strategy B\n") 36 | } 37 | 38 | // Context defines a context for executing a strategy. 39 | type Context struct { 40 | strategy Strategy 41 | } 42 | 43 | // NewContext creates a new instance of a context. 44 | func NewContext() *Context { 45 | return &Context{} 46 | } 47 | 48 | // SetStrategy sets the strategy to execute for this context. 49 | func (c *Context) SetStrategy(strategy Strategy) { 50 | c.strategy = strategy 51 | } 52 | 53 | // Execute executes the strategy. 54 | func (c *Context) Execute() { 55 | c.strategy.Execute() 56 | } 57 | -------------------------------------------------------------------------------- /behavioral/strategy_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewStrategyA_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | strategy := NewStrategyA() 13 | assert.NotNil(t, strategy) 14 | } 15 | 16 | func TestStrategyAExecute_ExecutesStrategyA(t *testing.T) { 17 | bufferOutputWriter := outputWriter 18 | outputWriter = new(bytes.Buffer) 19 | defer func() { outputWriter = bufferOutputWriter }() 20 | 21 | strategy := NewStrategyA() 22 | strategy.Execute() 23 | 24 | assert.Equal(t, "executing strategy A\n", outputWriter.(*bytes.Buffer).String()) 25 | } 26 | 27 | func TestNewStrategyB_ReturnsNonNil(t *testing.T) { 28 | t.Parallel() 29 | strategy := NewStrategyB() 30 | assert.NotNil(t, strategy) 31 | } 32 | 33 | func TestStrategyBExecute_ExecutesStrategyB(t *testing.T) { 34 | bufferOutputWriter := outputWriter 35 | outputWriter = new(bytes.Buffer) 36 | defer func() { outputWriter = bufferOutputWriter }() 37 | 38 | strategy := NewStrategyB() 39 | strategy.Execute() 40 | 41 | assert.Equal(t, "executing strategy B\n", outputWriter.(*bytes.Buffer).String()) 42 | } 43 | 44 | func TestNewContext_ReturnsNonNil(t *testing.T) { 45 | t.Parallel() 46 | context := NewContext() 47 | assert.NotNil(t, context) 48 | } 49 | 50 | func TestSetStrategy_SetsStrategy(t *testing.T) { 51 | t.Parallel() 52 | strategy := NewStrategyB() 53 | context := NewContext() 54 | context.SetStrategy(strategy) 55 | assert.Equal(t, strategy, context.strategy) 56 | } 57 | 58 | func TestContextExecute_ExecutesSetStrategy(t *testing.T) { 59 | bufferOutputWriter := outputWriter 60 | outputWriter = new(bytes.Buffer) 61 | defer func() { outputWriter = bufferOutputWriter }() 62 | 63 | strategy := NewStrategyB() 64 | context := NewContext() 65 | context.SetStrategy(strategy) 66 | context.Execute() 67 | assert.Equal(t, "executing strategy B\n", outputWriter.(*bytes.Buffer).String()) 68 | } 69 | -------------------------------------------------------------------------------- /behavioral/template_method.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // WorkerInterface defines an interface for a worker. 8 | type WorkerInterface interface { 9 | GetUp() 10 | EatBreakfast() 11 | GoToWork() 12 | Work() 13 | ReturnHome() 14 | Relax() 15 | Sleep() 16 | } 17 | 18 | // Worker defines the worker. 19 | type Worker struct { 20 | WorkerInterface 21 | } 22 | 23 | // NewWorker returns a new Worker. 24 | func NewWorker(w WorkerInterface) *Worker { 25 | return &Worker{w} 26 | } 27 | 28 | // DailyRoutine is the template method for printing the workers daily routine. 29 | func (w *Worker) DailyRoutine() { 30 | w.GetUp() 31 | w.EatBreakfast() 32 | w.GoToWork() 33 | w.Work() 34 | w.ReturnHome() 35 | w.Relax() 36 | w.Sleep() 37 | } 38 | 39 | // PostMan is a worker. 40 | type PostMan struct { 41 | } 42 | 43 | // GetUp prints what the postman does to get up. 44 | func (w *PostMan) GetUp() { 45 | fmt.Fprintf(outputWriter, "Getting up\n") 46 | } 47 | 48 | // EatBreakfast prints what the postman does to eat breakfast. 49 | func (w *PostMan) EatBreakfast() { 50 | fmt.Fprintf(outputWriter, "Eating pop tarts\n") 51 | } 52 | 53 | // GoToWork prints what the postman does to get to work. 54 | func (w *PostMan) GoToWork() { 55 | fmt.Fprintf(outputWriter, "Cycle to work\n") 56 | } 57 | 58 | // Work prints what the postman does to work. 59 | func (w *PostMan) Work() { 60 | fmt.Fprintf(outputWriter, "Post letters\n") 61 | } 62 | 63 | // ReturnHome prints what the postman does to get home. 64 | func (w *PostMan) ReturnHome() { 65 | fmt.Fprintf(outputWriter, "Cycle home\n") 66 | } 67 | 68 | // Relax prints what the postman does to relax. 69 | func (w *PostMan) Relax() { 70 | fmt.Fprintf(outputWriter, "Collect stamps\n") 71 | } 72 | 73 | // Sleep prints what the postman does to sleep. 74 | func (w *PostMan) Sleep() { 75 | fmt.Fprintf(outputWriter, "Zzzzzzz\n") 76 | } 77 | -------------------------------------------------------------------------------- /behavioral/template_method_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestTemplateMethod(t *testing.T) { 11 | bufferOutputWriter := outputWriter 12 | outputWriter = new(bytes.Buffer) 13 | defer func() { outputWriter = bufferOutputWriter }() 14 | 15 | worker := NewWorker(&PostMan{}) 16 | worker.DailyRoutine() 17 | 18 | assert.Equal(t, "Getting up\n"+ 19 | "Eating pop tarts\n"+ 20 | "Cycle to work\n"+ 21 | "Post letters\n"+ 22 | "Cycle home\n"+ 23 | "Collect stamps\n"+ 24 | "Zzzzzzz\n", outputWriter.(*bytes.Buffer).String()) 25 | } 26 | -------------------------------------------------------------------------------- /behavioral/visitor.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import "fmt" 4 | 5 | // Element defines an interface for accepting visitors. 6 | type Element interface { 7 | Accept(v Visitor) 8 | } 9 | 10 | // This defines a struct which is an element. 11 | type This struct { 12 | } 13 | 14 | // This returns 'This' as a string. 15 | func (t *This) This() string { 16 | return "This" 17 | } 18 | 19 | // Accept accepts a visitor. 20 | func (t *This) Accept(v Visitor) { 21 | v.VisitThis(t) 22 | } 23 | 24 | // That defines a struct which is an element. 25 | type That struct { 26 | } 27 | 28 | // That returns 'That' as a string. 29 | func (t *That) That() string { 30 | return "That" 31 | } 32 | 33 | // Accept accepts a visitor. 34 | func (t *That) Accept(v Visitor) { 35 | v.VisitThat(t) 36 | } 37 | 38 | // TheOther defines a struct which is an element. 39 | type TheOther struct { 40 | } 41 | 42 | // TheOther returns 'TheOther' as a string. 43 | func (t *TheOther) TheOther() string { 44 | return "TheOther" 45 | } 46 | 47 | // Accept accepts a visitor. 48 | func (t *TheOther) Accept(v Visitor) { 49 | v.VisitTheOther(t) 50 | } 51 | 52 | // Visitor defines an interface for visiting this, that and the other. 53 | type Visitor interface { 54 | VisitThis(e *This) 55 | VisitThat(e *That) 56 | VisitTheOther(e *TheOther) 57 | } 58 | 59 | // UpVisitor defines an up visitor. 60 | type UpVisitor struct { 61 | } 62 | 63 | // VisitThis visits this. 64 | func (v *UpVisitor) VisitThis(e *This) { 65 | fmt.Printf("do Up on %v\n", e.This()) 66 | } 67 | 68 | // VisitThat visits that. 69 | func (v *UpVisitor) VisitThat(e *That) { 70 | fmt.Printf("do Up on %v\n", e.That()) 71 | } 72 | 73 | // VisitTheOther visits the other. 74 | func (v *UpVisitor) VisitTheOther(e *TheOther) { 75 | fmt.Printf("do Up on %v\n", e.TheOther()) 76 | } 77 | 78 | // DownVisitor defines a down visitor. 79 | type DownVisitor struct { 80 | } 81 | 82 | // VisitThis visits this. 83 | func (v *DownVisitor) VisitThis(e *This) { 84 | fmt.Printf("do Down on %v\n", e.This()) 85 | } 86 | 87 | // VisitThat visits that. 88 | func (v *DownVisitor) VisitThat(e *That) { 89 | fmt.Printf("do Down on %v\n", e.That()) 90 | } 91 | 92 | // VisitTheOther visits the other. 93 | func (v *DownVisitor) VisitTheOther(e *TheOther) { 94 | fmt.Printf("do Down on %v\n", e.TheOther()) 95 | } 96 | -------------------------------------------------------------------------------- /behavioral/visitor_test.go: -------------------------------------------------------------------------------- 1 | package behavioral 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestVisitor(t *testing.T) { 8 | t.Parallel() 9 | 10 | list := []Element{&This{}, &That{}, &TheOther{}} 11 | 12 | up := &UpVisitor{} 13 | down := &DownVisitor{} 14 | 15 | for i := 0; i < len(list); i++ { 16 | list[i].Accept(up) 17 | } 18 | for i := 0; i < len(list); i++ { 19 | list[i].Accept(down) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /creational/abstract_factory.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Shape is an interface for interacting with a shape. 8 | type Shape interface { 9 | Draw() 10 | } 11 | 12 | type circle struct { 13 | } 14 | 15 | func (c *circle) Draw() { 16 | fmt.Fprintf(outputWriter, "I am a circle.") 17 | } 18 | 19 | type square struct { 20 | } 21 | 22 | func (s *square) Draw() { 23 | fmt.Fprintf(outputWriter, "I am a square.") 24 | } 25 | 26 | type ellipse struct { 27 | } 28 | 29 | func (e *ellipse) Draw() { 30 | fmt.Fprintf(outputWriter, "I am an ellipse.") 31 | } 32 | 33 | type rectangle struct { 34 | } 35 | 36 | func (r *rectangle) Draw() { 37 | fmt.Fprintf(outputWriter, "I am a rectangle.") 38 | } 39 | 40 | // ShapeFactory is an interface for a factory which can be used 41 | // to create curved and straight shapes. 42 | type ShapeFactory interface { 43 | CreateCurvedShape() Shape 44 | CreateStraightShape() Shape 45 | } 46 | 47 | type simpleShapeFactory struct { 48 | } 49 | 50 | // NewSimpleShapeFactory creates a new simpleShapeFactory. 51 | func NewSimpleShapeFactory() ShapeFactory { 52 | return &simpleShapeFactory{} 53 | } 54 | 55 | func (s *simpleShapeFactory) CreateCurvedShape() Shape { 56 | return &circle{} 57 | } 58 | 59 | func (s *simpleShapeFactory) CreateStraightShape() Shape { 60 | return &square{} 61 | } 62 | 63 | type robustShapeFactory struct { 64 | } 65 | 66 | // NewRobustShapeFactory creates a new robustShapeFactory. 67 | func NewRobustShapeFactory() ShapeFactory { 68 | return &robustShapeFactory{} 69 | } 70 | 71 | func (s *robustShapeFactory) CreateCurvedShape() Shape { 72 | return &ellipse{} 73 | } 74 | 75 | func (s *robustShapeFactory) CreateStraightShape() Shape { 76 | return &rectangle{} 77 | } 78 | -------------------------------------------------------------------------------- /creational/abstract_factory_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewSimpleShapeFactory_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | factory := NewSimpleShapeFactory() 13 | assert.NotNil(t, factory) 14 | } 15 | 16 | func TestNewRobustShapeFactory_ReturnsNonNil(t *testing.T) { 17 | t.Parallel() 18 | factory := NewRobustShapeFactory() 19 | assert.NotNil(t, factory) 20 | } 21 | 22 | func TestCreateCurvedShape_WhenFactoryIsSimple_ReturnsCircle(t *testing.T) { 23 | t.Parallel() 24 | factory := NewSimpleShapeFactory() 25 | shape := factory.CreateCurvedShape() 26 | _, ok := shape.(*circle) 27 | assert.True(t, ok) 28 | } 29 | 30 | func TestDraw_WhenFactoryIsSimpleAndCreateCurvedShapeIsCalled_PrintsCircleDrawMessage(t *testing.T) { 31 | bufferOutputWriter := outputWriter 32 | outputWriter = new(bytes.Buffer) 33 | defer func() { outputWriter = bufferOutputWriter }() 34 | 35 | factory := NewSimpleShapeFactory() 36 | shape := factory.CreateCurvedShape() 37 | shape.Draw() 38 | assert.Equal(t, "I am a circle.", outputWriter.(*bytes.Buffer).String()) 39 | } 40 | 41 | func TestCreateStraightShape_WhenFactoryIsSimple_ReturnsSquare(t *testing.T) { 42 | t.Parallel() 43 | factory := NewSimpleShapeFactory() 44 | shape := factory.CreateStraightShape() 45 | _, ok := shape.(*square) 46 | assert.True(t, ok) 47 | } 48 | 49 | func TestDraw_WhenFactoryIsSimpleAndCreateStraightShapeIsCalled_PrintsSquareDrawMessage(t *testing.T) { 50 | bufferOutputWriter := outputWriter 51 | outputWriter = new(bytes.Buffer) 52 | defer func() { outputWriter = bufferOutputWriter }() 53 | 54 | factory := NewSimpleShapeFactory() 55 | shape := factory.CreateStraightShape() 56 | shape.Draw() 57 | assert.Equal(t, "I am a square.", outputWriter.(*bytes.Buffer).String()) 58 | } 59 | 60 | func TestCreateCurvedShape_WhenFactoryIsRobust_ReturnsEllipse(t *testing.T) { 61 | t.Parallel() 62 | factory := NewRobustShapeFactory() 63 | shape := factory.CreateCurvedShape() 64 | _, ok := shape.(*ellipse) 65 | assert.True(t, ok) 66 | } 67 | 68 | func TestDraw_WhenFactoryIsRobustAndCreateCurvedShapeIsCalled_PrintsEllipseDrawMessage(t *testing.T) { 69 | bufferOutputWriter := outputWriter 70 | outputWriter = new(bytes.Buffer) 71 | defer func() { outputWriter = bufferOutputWriter }() 72 | 73 | factory := NewRobustShapeFactory() 74 | shape := factory.CreateCurvedShape() 75 | shape.Draw() 76 | assert.Equal(t, "I am an ellipse.", outputWriter.(*bytes.Buffer).String()) 77 | } 78 | 79 | func TestCreateStraightShape_WhenFactoryIsRobust_ReturnsRectangle(t *testing.T) { 80 | t.Parallel() 81 | factory := NewRobustShapeFactory() 82 | shape := factory.CreateStraightShape() 83 | _, ok := shape.(*rectangle) 84 | assert.True(t, ok) 85 | } 86 | 87 | func TestDraw_WhenFactoryIsRobustAndCreateStraightShapeIsCalled_PrintsRectangleDrawMessage(t *testing.T) { 88 | bufferOutputWriter := outputWriter 89 | outputWriter = new(bytes.Buffer) 90 | defer func() { outputWriter = bufferOutputWriter }() 91 | 92 | factory := NewRobustShapeFactory() 93 | shape := factory.CreateStraightShape() 94 | shape.Draw() 95 | assert.Equal(t, "I am a rectangle.", outputWriter.(*bytes.Buffer).String()) 96 | } 97 | -------------------------------------------------------------------------------- /creational/builder.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | /* 4 | Example of builder pattern: 5 | 6 | builder := NewConcreteBuilder() 7 | director := NewDirector(builder) 8 | director.Construct() 9 | product := builder.GetResult() 10 | */ 11 | 12 | // Director is the object which orchestrates the building of a product. 13 | type Director struct { 14 | builder Builder 15 | } 16 | 17 | // NewDirector creates a new Director with a specified Builder. 18 | func NewDirector(builder Builder) Director { 19 | return Director{builder} 20 | } 21 | 22 | // Construct builds the product from a series of steps. 23 | func (d *Director) Construct() { 24 | d.builder.Build() 25 | } 26 | 27 | // Builder is an interface for building. 28 | type Builder interface { 29 | Build() 30 | } 31 | 32 | // ConcreteBuilder is a builder for building a Product 33 | type ConcreteBuilder struct { 34 | built bool 35 | } 36 | 37 | // NewConcreteBuilder returns a new Builder. 38 | func NewConcreteBuilder() ConcreteBuilder { 39 | return ConcreteBuilder{false} 40 | } 41 | 42 | // Build builds the product. 43 | func (b *ConcreteBuilder) Build() { 44 | b.built = true 45 | } 46 | 47 | // GetResult returns the Product which has been build during the Build step. 48 | func (b *ConcreteBuilder) GetResult() Product { 49 | return Product{b.built} 50 | } 51 | 52 | // Product describes the product to be built. 53 | type Product struct { 54 | Built bool 55 | } 56 | -------------------------------------------------------------------------------- /creational/builder_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewConcreteBuilder_ReturnsNonNil(t *testing.T) { 10 | t.Parallel() 11 | builder := NewConcreteBuilder() 12 | assert.NotNil(t, builder) 13 | } 14 | 15 | func TestNewConcreteBuilder_SetsBuildToFalse(t *testing.T) { 16 | t.Parallel() 17 | builder := NewConcreteBuilder() 18 | assert.False(t, builder.built) 19 | } 20 | 21 | func TestBuild_SetsBuildToTrue(t *testing.T) { 22 | t.Parallel() 23 | builder := NewConcreteBuilder() 24 | builder.Build() 25 | assert.True(t, builder.built) 26 | } 27 | 28 | func TestGetResult_WhenBuildNotCalled_ReturnsUnBuiltProduct(t *testing.T) { 29 | t.Parallel() 30 | builder := NewConcreteBuilder() 31 | product := builder.GetResult() 32 | assert.False(t, product.Built) 33 | } 34 | 35 | func TestGetResult_WhenBuildCalled_ReturnsBuiltProduct(t *testing.T) { 36 | t.Parallel() 37 | builder := NewConcreteBuilder() 38 | builder.Build() 39 | product := builder.GetResult() 40 | assert.True(t, product.Built) 41 | } 42 | 43 | func TestNewDirector_ReturnsNonNil(t *testing.T) { 44 | t.Parallel() 45 | director := NewDirector(nil) 46 | assert.NotNil(t, director) 47 | } 48 | 49 | func TestNewDirector_SetsBuilder(t *testing.T) { 50 | t.Parallel() 51 | builder := NewConcreteBuilder() 52 | director := NewDirector(&builder) 53 | assert.Equal(t, &builder, director.builder) 54 | } 55 | 56 | func TestConstruct_SetsBuiltOnBuilder(t *testing.T) { 57 | t.Parallel() 58 | builder := NewConcreteBuilder() 59 | director := NewDirector(&builder) 60 | director.Construct() 61 | assert.True(t, builder.built) 62 | } 63 | 64 | func TestConstruct_BuildsProduct(t *testing.T) { 65 | builder := NewConcreteBuilder() 66 | director := NewDirector(&builder) 67 | director.Construct() 68 | product := builder.GetResult() 69 | assert.True(t, product.Built) 70 | } 71 | -------------------------------------------------------------------------------- /creational/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package creational describes creational design patterns. 3 | 4 | The design patterns described are all related to instantiation. 5 | */ 6 | package creational 7 | -------------------------------------------------------------------------------- /creational/factory_method.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | var outputWriter io.Writer = os.Stdout // modified during testing 10 | 11 | // StoogeType is used as a enum for stooge types. 12 | type StoogeType int 13 | 14 | // Names of stooges as enums. 15 | const ( 16 | Larry StoogeType = iota 17 | Moe 18 | Curly 19 | ) 20 | 21 | // Stooge provides an interface for interacting with stooges. 22 | type Stooge interface { 23 | SlapStick() 24 | } 25 | 26 | type larry struct { 27 | } 28 | 29 | func (s *larry) SlapStick() { 30 | fmt.Fprint(outputWriter, "Larry: Poke eyes\n") 31 | } 32 | 33 | type moe struct { 34 | } 35 | 36 | func (s *moe) SlapStick() { 37 | fmt.Fprint(outputWriter, "Moe: Slap head\n") 38 | } 39 | 40 | type curly struct { 41 | } 42 | 43 | func (s *curly) SlapStick() { 44 | fmt.Fprint(outputWriter, "Curly: Suffer abuse\n") 45 | } 46 | 47 | // NewStooge creates new stooges given the stooge type. 48 | // Nil is returned if the stooge type is not recognised. 49 | func NewStooge(stooge StoogeType) Stooge { 50 | switch stooge { 51 | case Larry: 52 | return &larry{} 53 | case Moe: 54 | return &moe{} 55 | case Curly: 56 | return &curly{} 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /creational/factory_method_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewStooge_WhenStoogeTypeIsLarry_ReturnsLarry(t *testing.T) { 11 | t.Parallel() 12 | stooge := NewStooge(Larry) 13 | assert.NotNil(t, stooge) 14 | _, ok := stooge.(*larry) 15 | assert.True(t, ok) 16 | } 17 | 18 | func TestNewStooge_WhenStoogeTypeIsMoe_ReturnsMoe(t *testing.T) { 19 | t.Parallel() 20 | stooge := NewStooge(Moe) 21 | assert.NotNil(t, stooge) 22 | _, ok := stooge.(*moe) 23 | assert.True(t, ok) 24 | } 25 | 26 | func TestNewStooge_WhenStoogeTypeIsCurly_ReturnsCurly(t *testing.T) { 27 | t.Parallel() 28 | stooge := NewStooge(Curly) 29 | assert.NotNil(t, stooge) 30 | _, ok := stooge.(*curly) 31 | assert.True(t, ok) 32 | } 33 | 34 | func TestNewStooge_WhenStoogeTypeIsNotRecognised_ReturnsNil(t *testing.T) { 35 | t.Parallel() 36 | stooge := NewStooge(10) 37 | assert.Nil(t, stooge) 38 | } 39 | 40 | func TestSlapStick_WhenNewStoogeIsLarry_PrintsLarryMessage(t *testing.T) { 41 | bufferOutputWriter := outputWriter 42 | outputWriter = new(bytes.Buffer) 43 | defer func() { outputWriter = bufferOutputWriter }() 44 | 45 | stooge := NewStooge(Larry) 46 | stooge.SlapStick() 47 | assert.Equal(t, "Larry: Poke eyes\n", outputWriter.(*bytes.Buffer).String()) 48 | } 49 | 50 | func TestSlapStick_WhenNewStoogeIsMoe_PrintsMoeMessage(t *testing.T) { 51 | bufferOutputWriter := outputWriter 52 | outputWriter = new(bytes.Buffer) 53 | defer func() { outputWriter = bufferOutputWriter }() 54 | 55 | stooge := NewStooge(Moe) 56 | stooge.SlapStick() 57 | assert.Equal(t, "Moe: Slap head\n", outputWriter.(*bytes.Buffer).String()) 58 | } 59 | 60 | func TestSlapStick_WhenNewStoogeIsCurly_PrintsCurlyMessage(t *testing.T) { 61 | bufferOutputWriter := outputWriter 62 | outputWriter = new(bytes.Buffer) 63 | defer func() { outputWriter = bufferOutputWriter }() 64 | 65 | stooge := NewStooge(Curly) 66 | stooge.SlapStick() 67 | assert.Equal(t, "Curly: Suffer abuse\n", outputWriter.(*bytes.Buffer).String()) 68 | } 69 | -------------------------------------------------------------------------------- /creational/object_pool.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Pool represents the pool of objects to use. 8 | type Pool struct { 9 | sync.Mutex 10 | inuse []interface{} 11 | available []interface{} 12 | new func() interface{} 13 | } 14 | 15 | // NewPool creates a new pool. 16 | func NewPool(new func() interface{}) *Pool { 17 | return &Pool{new: new} 18 | } 19 | 20 | // Acquire acquires a new PoolObject to use from the pool. 21 | // Here acquire creates a new instance of a PoolObject if none available. 22 | func (p *Pool) Acquire() interface{} { 23 | p.Lock() 24 | var object interface{} 25 | if len(p.available) != 0 { 26 | object = p.available[0] 27 | p.available = append(p.available[:0], p.available[1:]...) 28 | p.inuse = append(p.inuse, object) 29 | } else { 30 | object = p.new() 31 | p.inuse = append(p.inuse, object) 32 | } 33 | p.Unlock() 34 | return object 35 | } 36 | 37 | // Release releases a PoolObject back to the Pool. 38 | func (p *Pool) Release(object interface{}) { 39 | p.Lock() 40 | p.available = append(p.available, object) 41 | for i, v := range p.inuse { 42 | if v == object { 43 | p.inuse = append(p.inuse[:i], p.inuse[i+1:]...) 44 | break 45 | } 46 | } 47 | p.Unlock() 48 | } 49 | -------------------------------------------------------------------------------- /creational/object_pool_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewPool_ReturnsNonNil(t *testing.T) { 10 | t.Parallel() 11 | pool := NewPool(nil) 12 | assert.NotNil(t, pool) 13 | } 14 | 15 | func TestNewPool_SetsNewFunc(t *testing.T) { 16 | t.Parallel() 17 | expectedNew := func() interface{} { 18 | return float64(10.0) 19 | } 20 | pool := NewPool(expectedNew) 21 | assert.NotNil(t, pool.new) 22 | } 23 | 24 | func TestAcquire_AddsInstanceToInUse(t *testing.T) { 25 | t.Parallel() 26 | expectedNew := func() interface{} { 27 | return float64(10.0) 28 | } 29 | pool := NewPool(expectedNew) 30 | pool.Acquire() 31 | assert.Len(t, pool.inuse, 1) 32 | } 33 | 34 | func TestAcquire_DoesNotAddsInstanceToAvailable(t *testing.T) { 35 | t.Parallel() 36 | expectedNew := func() interface{} { 37 | return float64(10.0) 38 | } 39 | pool := NewPool(expectedNew) 40 | pool.Acquire() 41 | assert.Len(t, pool.available, 0) 42 | } 43 | 44 | func TestAcquire_UsesAvailableInstanceIfAvailable(t *testing.T) { 45 | t.Parallel() 46 | expectedNew := func() interface{} { 47 | return 10.0 48 | } 49 | pool := NewPool(expectedNew) 50 | value := pool.Acquire() 51 | pool.Release(value) 52 | pool.Acquire() 53 | assert.Len(t, pool.available, 0) 54 | assert.Len(t, pool.inuse, 1) 55 | } 56 | 57 | func TestRelease_AddsInstanceToAvailable(t *testing.T) { 58 | t.Parallel() 59 | expectedNew := func() interface{} { 60 | return float64(10.0) 61 | } 62 | pool := NewPool(expectedNew) 63 | value := pool.Acquire() 64 | pool.Release(value) 65 | assert.Len(t, pool.available, 1) 66 | assert.Equal(t, value, pool.available[0]) 67 | } 68 | 69 | func TestRelease_RemovesInstanceFromInUse(t *testing.T) { 70 | t.Parallel() 71 | expectedNew := func() interface{} { 72 | return 10.0 73 | } 74 | pool := NewPool(expectedNew) 75 | value := pool.Acquire() 76 | pool.Release(value) 77 | assert.Len(t, pool.inuse, 0) 78 | } 79 | -------------------------------------------------------------------------------- /creational/prototype.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | // Prototype interface 4 | type Prototype interface { 5 | Name() string 6 | Clone() Prototype 7 | } 8 | 9 | // concretePrototype is the first concrete instance of a prototype. 10 | type concretePrototype struct { 11 | name string 12 | } 13 | 14 | // Name returns the name of the concreatePrototype. 15 | func (p *concretePrototype) Name() string { 16 | return p.name 17 | } 18 | 19 | // Clone creates a cloned new instance of a concretePrototype1. 20 | func (p *concretePrototype) Clone() Prototype { 21 | return &concretePrototype{p.name} 22 | } 23 | -------------------------------------------------------------------------------- /creational/prototype_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestName_ReturnsName(t *testing.T) { 10 | t.Parallel() 11 | const expectedName = "prototype instance" 12 | 13 | proto := concretePrototype{expectedName} 14 | actualName := proto.Name() 15 | 16 | assert.Equal(t, expectedName, actualName) 17 | } 18 | 19 | func TestClone_ReturnsNonNil(t *testing.T) { 20 | t.Parallel() 21 | const name = "prototype instance" 22 | 23 | proto := concretePrototype{name} 24 | newProto := proto.Clone() 25 | 26 | assert.NotNil(t, newProto) 27 | } 28 | 29 | func TestClone_ReturnsDifferentInstance(t *testing.T) { 30 | t.Parallel() 31 | name := "prototype instance" 32 | 33 | proto := concretePrototype{name} 34 | newProto := proto.Clone() 35 | 36 | assert.NotEqual(t, proto, newProto) 37 | } 38 | 39 | func TestName_WhenPrototypeIsCloned_ReturnsName(t *testing.T) { 40 | t.Parallel() 41 | const expectedName = "prototype instance" 42 | 43 | proto := concretePrototype{expectedName} 44 | newProto := proto.Clone() 45 | actualName := newProto.Name() 46 | 47 | assert.Equal(t, expectedName, actualName) 48 | } 49 | -------------------------------------------------------------------------------- /creational/singleton.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Singleton describes a struct of which only a single 8 | // instance can exist. 9 | type Singleton struct { 10 | } 11 | 12 | var instance *Singleton 13 | var once sync.Once 14 | 15 | // GetInstance ensures that only a single instance of the 16 | // struct singleton gets created and provides a global point 17 | // to access to it. 18 | func GetInstance() *Singleton { 19 | once.Do(func() { 20 | instance = &Singleton{} 21 | }) 22 | return instance 23 | } 24 | -------------------------------------------------------------------------------- /creational/singleton_test.go: -------------------------------------------------------------------------------- 1 | package creational 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestGetInstance_ReturnsSingleton(t *testing.T) { 10 | t.Parallel() 11 | singleton := GetInstance() 12 | assert.NotNil(t, singleton) 13 | } 14 | 15 | func TestGetInstance_MultipleCallsToGetInstance_ReturnsSingleton(t *testing.T) { 16 | t.Parallel() 17 | singleton := GetInstance() 18 | newSingleton := GetInstance() 19 | 20 | assert.Equal(t, singleton, newSingleton) 21 | } 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bvwells/go-patterns 2 | 3 | go 1.20 4 | 5 | require github.com/stretchr/testify v1.8.2 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 11 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 12 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /jigsaw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bvwells/go-patterns/e844ef49e91c0e60a0fd21c77d39ee19e0e6c598/jigsaw.png -------------------------------------------------------------------------------- /patterns.go: -------------------------------------------------------------------------------- 1 | // Package patterns is the root of the packages used to access go-patterns 2 | // examples. See https://godoc.org/github.com/bvwells/go-patterns for a full list 3 | // of sub-packages. 4 | package patterns 5 | -------------------------------------------------------------------------------- /structural/adapter.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Target is the target interface to adapt to. 8 | type Target interface { 9 | Execute() 10 | } 11 | 12 | // Adaptee defines the old interface which needs to be adapted. 13 | type Adaptee struct { 14 | } 15 | 16 | // SpecificExecute defines the old interface for executing which 17 | // needs to be adapted to the new interface defined by Target. 18 | func (a *Adaptee) SpecificExecute() { 19 | fmt.Fprint(outputWriter, "Executing SpecificExecute.") 20 | } 21 | 22 | // Adapter is the adaptor to the new interface Target. 23 | type Adapter struct { 24 | *Adaptee 25 | } 26 | 27 | // Execute adapts from the new interface Execute to the old SpecificExecute. 28 | func (a *Adapter) Execute() { 29 | a.SpecificExecute() 30 | } 31 | -------------------------------------------------------------------------------- /structural/adapter_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExecute_ExecutesSpecificExecute(t *testing.T) { 11 | bufferOutputWriter := outputWriter 12 | outputWriter = new(bytes.Buffer) 13 | defer func() { outputWriter = bufferOutputWriter }() 14 | 15 | adapter := Adapter{} 16 | adapter.Execute() 17 | 18 | assert.Equal(t, "Executing SpecificExecute.", outputWriter.(*bytes.Buffer).String()) 19 | } 20 | -------------------------------------------------------------------------------- /structural/bridge.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import "fmt" 4 | 5 | // TimeImp is the interface for different implementations of telling the time. 6 | type TimeImp interface { 7 | Tell() 8 | } 9 | 10 | // BasicTimeImp defines a TimeImp which tells the time in 24 hour format. 11 | type BasicTimeImp struct { 12 | hour int 13 | minute int 14 | } 15 | 16 | // NewBasicTimeImp creates a new TimeImp. 17 | func NewBasicTimeImp(hour, minute int) TimeImp { 18 | return &BasicTimeImp{hour, minute} 19 | } 20 | 21 | // Tell tells the time in 24 hour format. 22 | func (t *BasicTimeImp) Tell() { 23 | fmt.Fprintf(outputWriter, "The time is %2.2d:%2.2d\n", t.hour, t.minute) 24 | } 25 | 26 | // CivilianTimeImp defines a TimeImp which tells the time in 12 hour format with meridem. 27 | type CivilianTimeImp struct { 28 | BasicTimeImp 29 | meridiem string 30 | } 31 | 32 | // NewCivilianTimeImp creates a new TimeImp. 33 | func NewCivilianTimeImp(hour, minute int, pm bool) TimeImp { 34 | meridiem := "AM" 35 | if pm { 36 | meridiem = "PM" 37 | } 38 | time := &CivilianTimeImp{meridiem: meridiem} 39 | time.hour = hour 40 | time.minute = minute 41 | return time 42 | } 43 | 44 | // Tell tells the time in 12 hour format. 45 | func (t *CivilianTimeImp) Tell() { 46 | fmt.Fprintf(outputWriter, "The time is %2.2d:%2.2d %s\n", t.hour, t.minute, t.meridiem) 47 | } 48 | 49 | // ZuluTimeImp defines a TimeImp which tells the time in Zulu zone format. 50 | type ZuluTimeImp struct { 51 | BasicTimeImp 52 | zone string 53 | } 54 | 55 | // NewZuluTimeImp creates a new TimeImp. 56 | func NewZuluTimeImp(hour, minute, zoneID int) TimeImp { 57 | zone := "" 58 | if zoneID == 5 { 59 | zone = "Eastern Standard Time" 60 | } else if zoneID == 6 { 61 | zone = "Central Standard Time" 62 | } 63 | time := &ZuluTimeImp{zone: zone} 64 | time.hour = hour 65 | time.minute = minute 66 | return time 67 | } 68 | 69 | // Tell tells the time in 24 hour format in Zulu time zone. 70 | func (t *ZuluTimeImp) Tell() { 71 | fmt.Fprintf(outputWriter, "The time is %2.2d:%2.2d %s\n", t.hour, t.minute, t.zone) 72 | } 73 | 74 | // Time is the base struct for Time containing the implementation. 75 | type Time struct { 76 | imp TimeImp 77 | } 78 | 79 | // NewTime creates a new time which uses a basic time for its implementation. 80 | func NewTime(hour, minute int) *Time { 81 | return &Time{imp: NewBasicTimeImp(hour, minute)} 82 | } 83 | 84 | // Tell tells the time with the given implementation. 85 | func (t *Time) Tell() { 86 | t.imp.Tell() 87 | } 88 | 89 | // CivilianTime is a time with a civilian time implementation. 90 | type CivilianTime struct { 91 | Time 92 | } 93 | 94 | // NewCivilianTime creates a new civilian time. 95 | func NewCivilianTime(hour, minute int, pm bool) *Time { 96 | time := &CivilianTime{} 97 | time.imp = NewCivilianTimeImp(hour, minute, pm) 98 | return &time.Time 99 | } 100 | 101 | // ZuluTime is a time with a Zulu time implementation. 102 | type ZuluTime struct { 103 | Time 104 | } 105 | 106 | // NewZuluTime creates a new Zulu time. 107 | func NewZuluTime(hour, minute, zoneID int) *Time { 108 | time := &ZuluTime{} 109 | time.imp = NewZuluTimeImp(hour, minute, zoneID) 110 | return &time.Time 111 | } 112 | -------------------------------------------------------------------------------- /structural/bridge_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewBasicTimeImp_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | impl := NewBasicTimeImp(1, 2) 13 | assert.NotNil(t, impl) 14 | } 15 | 16 | func TestBasicTimeImplTell_TellsTime(t *testing.T) { 17 | bufferOutputWriter := outputWriter 18 | outputWriter = new(bytes.Buffer) 19 | defer func() { outputWriter = bufferOutputWriter }() 20 | 21 | impl := NewBasicTimeImp(1, 2) 22 | impl.Tell() 23 | assert.Equal(t, "The time is 01:02\n", outputWriter.(*bytes.Buffer).String()) 24 | } 25 | 26 | func TestNewCivilianTimeImp_ReturnsNonNil(t *testing.T) { 27 | t.Parallel() 28 | impl := NewCivilianTimeImp(1, 2, false) 29 | assert.NotNil(t, impl) 30 | } 31 | 32 | func TestCivilianTimeImpTell_TellsAMTime(t *testing.T) { 33 | bufferOutputWriter := outputWriter 34 | outputWriter = new(bytes.Buffer) 35 | defer func() { outputWriter = bufferOutputWriter }() 36 | 37 | impl := NewCivilianTimeImp(1, 2, false) 38 | impl.Tell() 39 | assert.Equal(t, "The time is 01:02 AM\n", outputWriter.(*bytes.Buffer).String()) 40 | } 41 | 42 | func TestCivilianTimeImpTell_TellsPMTime(t *testing.T) { 43 | bufferOutputWriter := outputWriter 44 | outputWriter = new(bytes.Buffer) 45 | defer func() { outputWriter = bufferOutputWriter }() 46 | 47 | impl := NewCivilianTimeImp(1, 2, true) 48 | impl.Tell() 49 | assert.Equal(t, "The time is 01:02 PM\n", outputWriter.(*bytes.Buffer).String()) 50 | } 51 | 52 | func TestNewZuluTimeImp_ReturnsNonNil(t *testing.T) { 53 | t.Parallel() 54 | impl := NewZuluTimeImp(1, 2, 5) 55 | assert.NotNil(t, impl) 56 | } 57 | 58 | func TestZuluTimeImpTell_TellsEasternStandardTime(t *testing.T) { 59 | bufferOutputWriter := outputWriter 60 | outputWriter = new(bytes.Buffer) 61 | defer func() { outputWriter = bufferOutputWriter }() 62 | 63 | impl := NewZuluTimeImp(1, 2, 5) 64 | impl.Tell() 65 | assert.Equal(t, "The time is 01:02 Eastern Standard Time\n", outputWriter.(*bytes.Buffer).String()) 66 | } 67 | 68 | func TestZuluTimeImpTell_TellsCentralStandardTime(t *testing.T) { 69 | bufferOutputWriter := outputWriter 70 | outputWriter = new(bytes.Buffer) 71 | defer func() { outputWriter = bufferOutputWriter }() 72 | 73 | impl := NewZuluTimeImp(1, 2, 6) 74 | impl.Tell() 75 | assert.Equal(t, "The time is 01:02 Central Standard Time\n", outputWriter.(*bytes.Buffer).String()) 76 | } 77 | 78 | func TestZuluTimeImpTell_TellsTime(t *testing.T) { 79 | bufferOutputWriter := outputWriter 80 | outputWriter = new(bytes.Buffer) 81 | defer func() { outputWriter = bufferOutputWriter }() 82 | 83 | impl := NewZuluTimeImp(1, 2, 7) 84 | impl.Tell() 85 | assert.Equal(t, "The time is 01:02 \n", outputWriter.(*bytes.Buffer).String()) 86 | } 87 | 88 | func TestTell_TellsTime(t *testing.T) { 89 | t.Parallel() 90 | times := make([]*Time, 3) 91 | times[0] = NewTime(14, 30) 92 | times[1] = NewCivilianTime(2, 30, true) 93 | times[2] = NewZuluTime(14, 30, 6) 94 | 95 | for i := 0; i < len(times); i++ { 96 | times[i].Tell() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /structural/composite.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import "fmt" 4 | 5 | // Component describes the behavior that needs to be exercised uniformly 6 | // across all primitive and composite objects. 7 | type Component interface { 8 | Traverse() 9 | } 10 | 11 | // Leaf describes a primitive leaf object in the hierarchy. 12 | type Leaf struct { 13 | value int 14 | } 15 | 16 | // NewLeaf creates a new leaf. 17 | func NewLeaf(value int) *Leaf { 18 | return &Leaf{value} 19 | } 20 | 21 | // Traverse prints the value of the leaf. 22 | func (l *Leaf) Traverse() { 23 | fmt.Printf("%v ", l.value) 24 | } 25 | 26 | // Composite describes a composite of components. 27 | type Composite struct { 28 | children []Component 29 | } 30 | 31 | // NewComposite creates a new composite. 32 | func NewComposite() *Composite { 33 | return &Composite{make([]Component, 0)} 34 | } 35 | 36 | // Add adds a new component to the composite. 37 | func (c *Composite) Add(component Component) { 38 | c.children = append(c.children, component) 39 | } 40 | 41 | // Traverse traverses the composites children. 42 | func (c *Composite) Traverse() { 43 | for i := 0; i < len(c.children); i++ { 44 | c.children[i].Traverse() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /structural/composite_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewLeaf_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | leaf := NewLeaf(10) 13 | assert.NotNil(t, leaf) 14 | } 15 | 16 | func TestNewLeaf_SetsValue(t *testing.T) { 17 | t.Parallel() 18 | value := 42 19 | leaf := NewLeaf(value) 20 | assert.Equal(t, value, leaf.value) 21 | } 22 | 23 | func TestNewComposite_ReturnsNonNil(t *testing.T) { 24 | t.Parallel() 25 | composite := NewComposite() 26 | assert.NotNil(t, composite) 27 | } 28 | 29 | func TestAdd_AddsNewContainer(t *testing.T) { 30 | t.Parallel() 31 | composite := NewComposite() 32 | leaf1 := NewLeaf(10) 33 | composite.Add(leaf1) 34 | leaf2 := NewLeaf(10) 35 | composite.Add(leaf2) 36 | leaf3 := NewComposite() 37 | composite.Add(leaf3) 38 | 39 | assert.Len(t, composite.children, 3) 40 | assert.Equal(t, leaf1, composite.children[0]) 41 | assert.Equal(t, leaf2, composite.children[1]) 42 | assert.Equal(t, leaf3, composite.children[2]) 43 | } 44 | 45 | func TestTraverse(t *testing.T) { 46 | t.Parallel() 47 | containers := make([]Composite, 4) 48 | for i := 0; i < 4; i++ { 49 | for j := 0; j < 3; j++ { 50 | containers[i].Add(NewLeaf(i*3 + j)) 51 | } 52 | } 53 | for i := 1; i < 4; i++ { 54 | containers[0].Add(&(containers[i])) 55 | } 56 | for i := 0; i < 4; i++ { 57 | containers[i].Traverse() 58 | fmt.Printf("\n") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /structural/decorator.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // TaskFunc represents a function to perform a task. 8 | type TaskFunc func(int) int 9 | 10 | // LogDecorate decorates a task functions execution with some logging. 11 | func LogDecorate(fn TaskFunc) TaskFunc { 12 | return func(taskID int) int { 13 | log.Printf("Starting task with ID %v.\n", taskID) 14 | 15 | result := fn(taskID) 16 | 17 | log.Printf("Task with ID %v completed with the result %v.\n", taskID, result) 18 | 19 | return result 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /structural/decorator_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | ) 7 | 8 | func TestLogDecorate(t *testing.T) { 9 | t.Parallel() 10 | 11 | decorator := LogDecorate(func(taskID int) int { 12 | log.Printf("Task with ID %v is running....", taskID) 13 | return 0 14 | }) 15 | 16 | decorator(5) 17 | } 18 | -------------------------------------------------------------------------------- /structural/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package structural describes structural design patterns. 3 | The design patterns described are all related to composition. 4 | */ 5 | package structural 6 | -------------------------------------------------------------------------------- /structural/facade.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // CarModel is the first car subsystem which describes the car model. 8 | type CarModel struct { 9 | } 10 | 11 | // NewCarModel creates a new car model. 12 | func NewCarModel() *CarModel { 13 | return &CarModel{} 14 | } 15 | 16 | // SetModel sets the car model and logs. 17 | func (c *CarModel) SetModel() { 18 | fmt.Fprintf(outputWriter, " CarModel - SetModel\n") 19 | } 20 | 21 | // CarEngine is the second car subsystem which describes the car engine. 22 | type CarEngine struct { 23 | } 24 | 25 | // NewCarEngine creates a new car engine. 26 | func NewCarEngine() *CarEngine { 27 | return &CarEngine{} 28 | } 29 | 30 | // SetEngine sets the car engine and logs. 31 | func (c *CarEngine) SetEngine() { 32 | fmt.Fprintf(outputWriter, " CarEngine - SetEngine\n") 33 | } 34 | 35 | // CarBody is the third car subsystem which describes the car body. 36 | type CarBody struct { 37 | } 38 | 39 | // NewCarBody creates a new car body. 40 | func NewCarBody() *CarBody { 41 | return &CarBody{} 42 | } 43 | 44 | // SetBody sets the car body and logs. 45 | func (c *CarBody) SetBody() { 46 | fmt.Fprintf(outputWriter, " CarBody - SetBody\n") 47 | } 48 | 49 | // CarAccessories is the fourth car subsystem which describes the car accessories. 50 | type CarAccessories struct { 51 | } 52 | 53 | // NewCarAccessories creates new car accessories. 54 | func NewCarAccessories() *CarAccessories { 55 | return &CarAccessories{} 56 | } 57 | 58 | // SetAccessories sets the car accessories and logs. 59 | func (c *CarAccessories) SetAccessories() { 60 | fmt.Fprintf(outputWriter, " CarAccessories - SetAccessories\n") 61 | } 62 | 63 | // CarFacade describes the car facade which provides a simplified interface to create a car. 64 | type CarFacade struct { 65 | accessories *CarAccessories 66 | body *CarBody 67 | engine *CarEngine 68 | model *CarModel 69 | } 70 | 71 | // NewCarFacade creates a new CarFacade. 72 | func NewCarFacade() *CarFacade { 73 | return &CarFacade{ 74 | accessories: NewCarAccessories(), 75 | body: NewCarBody(), 76 | engine: NewCarEngine(), 77 | model: NewCarModel(), 78 | } 79 | } 80 | 81 | // CreateCompleteCar creates a new complete car. 82 | func (c *CarFacade) CreateCompleteCar() { 83 | fmt.Fprintf(outputWriter, "******** Creating a Car **********\n") 84 | c.model.SetModel() 85 | c.engine.SetEngine() 86 | c.body.SetBody() 87 | c.accessories.SetAccessories() 88 | fmt.Fprintf(outputWriter, "******** Car creation is completed. **********\n") 89 | } 90 | -------------------------------------------------------------------------------- /structural/facade_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewCarModel_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | model := NewCarModel() 13 | assert.NotNil(t, model) 14 | } 15 | 16 | func TestSetModel_PrintsSetModel(t *testing.T) { 17 | bufferOutputWriter := outputWriter 18 | outputWriter = new(bytes.Buffer) 19 | defer func() { outputWriter = bufferOutputWriter }() 20 | 21 | model := NewCarModel() 22 | model.SetModel() 23 | assert.Equal(t, " CarModel - SetModel\n", outputWriter.(*bytes.Buffer).String()) 24 | } 25 | 26 | func TestNewCarEngine_ReturnsNonNil(t *testing.T) { 27 | t.Parallel() 28 | engine := NewCarEngine() 29 | assert.NotNil(t, engine) 30 | } 31 | 32 | func TestSetEngine_PrintsSetEngine(t *testing.T) { 33 | bufferOutputWriter := outputWriter 34 | outputWriter = new(bytes.Buffer) 35 | defer func() { outputWriter = bufferOutputWriter }() 36 | 37 | engine := NewCarEngine() 38 | engine.SetEngine() 39 | assert.Equal(t, " CarEngine - SetEngine\n", outputWriter.(*bytes.Buffer).String()) 40 | } 41 | 42 | func TestNewCarBody_ReturnsNonNil(t *testing.T) { 43 | t.Parallel() 44 | body := NewCarBody() 45 | assert.NotNil(t, body) 46 | } 47 | 48 | func TestSetBody_PrintsSetBody(t *testing.T) { 49 | t.Parallel() 50 | bufferOutputWriter := outputWriter 51 | outputWriter = new(bytes.Buffer) 52 | defer func() { outputWriter = bufferOutputWriter }() 53 | 54 | body := NewCarBody() 55 | body.SetBody() 56 | assert.Equal(t, " CarBody - SetBody\n", outputWriter.(*bytes.Buffer).String()) 57 | } 58 | 59 | func TestNewCarAccessories_ReturnsNonNil(t *testing.T) { 60 | t.Parallel() 61 | accessories := NewCarAccessories() 62 | assert.NotNil(t, accessories) 63 | } 64 | 65 | func TestSetAccessories_PrintsSetAccessories(t *testing.T) { 66 | bufferOutputWriter := outputWriter 67 | outputWriter = new(bytes.Buffer) 68 | defer func() { outputWriter = bufferOutputWriter }() 69 | 70 | accessories := NewCarAccessories() 71 | accessories.SetAccessories() 72 | assert.Equal(t, " CarAccessories - SetAccessories\n", outputWriter.(*bytes.Buffer).String()) 73 | } 74 | 75 | func TestNewCarFacade_ReturnsNonNil(t *testing.T) { 76 | t.Parallel() 77 | facade := NewCarFacade() 78 | assert.NotNil(t, facade) 79 | } 80 | 81 | func TestNewCarFacade_SetsAccessories(t *testing.T) { 82 | t.Parallel() 83 | facade := NewCarFacade() 84 | assert.NotNil(t, facade.accessories) 85 | } 86 | 87 | func TestNewCarFacade_SetsBody(t *testing.T) { 88 | t.Parallel() 89 | facade := NewCarFacade() 90 | assert.NotNil(t, facade.body) 91 | } 92 | 93 | func TestNewCarFacade_SetEngine(t *testing.T) { 94 | t.Parallel() 95 | facade := NewCarFacade() 96 | assert.NotNil(t, facade.engine) 97 | } 98 | 99 | func TestNewCarFacade_SetsModel(t *testing.T) { 100 | facade := NewCarFacade() 101 | t.Parallel() 102 | assert.NotNil(t, facade.model) 103 | } 104 | 105 | func TestCreateCompleteCar_PrintsCompleteCar(t *testing.T) { 106 | bufferOutputWriter := outputWriter 107 | outputWriter = new(bytes.Buffer) 108 | defer func() { outputWriter = bufferOutputWriter }() 109 | 110 | facade := NewCarFacade() 111 | facade.CreateCompleteCar() 112 | 113 | assert.Equal(t, "******** Creating a Car **********\n"+ 114 | " CarModel - SetModel\n"+ 115 | " CarEngine - SetEngine\n"+ 116 | " CarBody - SetBody\n"+ 117 | " CarAccessories - SetAccessories\n"+ 118 | "******** Car creation is completed. **********\n", outputWriter.(*bytes.Buffer).String()) 119 | } 120 | -------------------------------------------------------------------------------- /structural/flyweight.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | // Flyweight is a flyweight object which can be shared to support large 4 | // numbers of objects efficiently. 5 | type Flyweight struct { 6 | Name string 7 | } 8 | 9 | // NewFlyweight creates a new Flyweight object. 10 | func NewFlyweight(name string) *Flyweight { 11 | return &Flyweight{name} 12 | } 13 | 14 | // FlyweightFactory is a factory for creating and storing flyweights. 15 | type FlyweightFactory struct { 16 | pool map[string]*Flyweight 17 | } 18 | 19 | // NewFlyweightFactory creates a new FlyweightFactory. 20 | func NewFlyweightFactory() *FlyweightFactory { 21 | return &FlyweightFactory{pool: make(map[string]*Flyweight)} 22 | } 23 | 24 | // GetFlyweight gets or creates a flyweight depending on whether a 25 | // flyweight of the same name already exists. 26 | func (f *FlyweightFactory) GetFlyweight(name string) *Flyweight { 27 | flyweight, okay := f.pool[name] 28 | if !okay { 29 | flyweight = NewFlyweight(name) 30 | f.pool[name] = flyweight 31 | } 32 | return flyweight 33 | } 34 | -------------------------------------------------------------------------------- /structural/flyweight_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewFlyweight_ReturnsNonNil(t *testing.T) { 10 | t.Parallel() 11 | flyweight := NewFlyweight("McFly") 12 | assert.NotNil(t, flyweight) 13 | } 14 | 15 | func TestNewFlyweight_SetsName(t *testing.T) { 16 | t.Parallel() 17 | name := "McFly" 18 | flyweight := NewFlyweight(name) 19 | assert.Equal(t, name, flyweight.Name) 20 | } 21 | 22 | func TestNewFlyweightFactory_ReturnsNonNil(t *testing.T) { 23 | t.Parallel() 24 | factory := NewFlyweightFactory() 25 | assert.NotNil(t, factory) 26 | } 27 | 28 | func TestGetFlyweight_WhenFlyweightDoesNotExist_CreatesAndReturnsNewInstance(t *testing.T) { 29 | t.Parallel() 30 | factory := NewFlyweightFactory() 31 | marty := factory.GetFlyweight("Marty McFly") 32 | george := factory.GetFlyweight("George McFly") 33 | assert.Len(t, factory.pool, 2) 34 | assert.Equal(t, marty, factory.pool["Marty McFly"]) 35 | assert.Equal(t, george, factory.pool["George McFly"]) 36 | } 37 | 38 | func TestGetFlyweight_WhenFlyweightAlreadyExists_ReturnsInstance(t *testing.T) { 39 | t.Parallel() 40 | factory := NewFlyweightFactory() 41 | marty := factory.GetFlyweight("McFly") 42 | george := factory.GetFlyweight("McFly") 43 | assert.Equal(t, marty, george) 44 | assert.Len(t, factory.pool, 1) 45 | } 46 | -------------------------------------------------------------------------------- /structural/proxy.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | var outputWriter io.Writer = os.Stdout // modified during testing 10 | 11 | // ITask is an interface for performing tasks. 12 | type ITask interface { 13 | Execute(taskType string) 14 | } 15 | 16 | // Task implements the ITask interface for performing tasks. 17 | type Task struct { 18 | } 19 | 20 | // Execute implements the task. 21 | func (t *Task) Execute(taskType string) { 22 | fmt.Fprint(outputWriter, "Performing task type: "+taskType) 23 | } 24 | 25 | // ProxyTask represents a proxy task with re-routes tasks. 26 | type ProxyTask struct { 27 | task *Task 28 | } 29 | 30 | // NewProxyTask creates a new instance of a ProxyTask. 31 | func NewProxyTask() *ProxyTask { 32 | return &ProxyTask{task: &Task{}} 33 | } 34 | 35 | // Execute intercepts the Execute command and re-routes it to the Task Execute command. 36 | func (t *ProxyTask) Execute(taskType string) { 37 | if taskType == "Run" { 38 | t.task.Execute(taskType) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /structural/proxy_test.go: -------------------------------------------------------------------------------- 1 | package structural 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewProxyTask_ReturnsNonNil(t *testing.T) { 11 | t.Parallel() 12 | proxy := NewProxyTask() 13 | assert.NotNil(t, proxy) 14 | } 15 | 16 | func TestNewProxyTask_SetsTask(t *testing.T) { 17 | t.Parallel() 18 | proxy := NewProxyTask() 19 | assert.NotNil(t, proxy.task) 20 | } 21 | 22 | func TestExecute_WhenTaskTypeIsRun_ExecutesTaskExecute(t *testing.T) { 23 | bufferOutputWriter := outputWriter 24 | outputWriter = new(bytes.Buffer) 25 | defer func() { outputWriter = bufferOutputWriter }() 26 | 27 | proxy := NewProxyTask() 28 | proxy.Execute("Run") 29 | 30 | assert.Equal(t, "Performing task type: Run", outputWriter.(*bytes.Buffer).String()) 31 | } 32 | 33 | func TestExecute_WhenTaskTypeIsNotRun_DoesNotExecuteTaskExecute(t *testing.T) { 34 | bufferOutputWriter := outputWriter 35 | outputWriter = new(bytes.Buffer) 36 | defer func() { outputWriter = bufferOutputWriter }() 37 | 38 | proxy := NewProxyTask() 39 | proxy.Execute("Stop") 40 | 41 | assert.Equal(t, "", outputWriter.(*bytes.Buffer).String()) 42 | } 43 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v vendor); do 7 | go test -race -coverprofile=profile.out -covermode=atomic $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done 13 | --------------------------------------------------------------------------------