├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SUMMARY.md ├── behavioral ├── observer.md ├── observer │ └── main.go └── strategy.md ├── book.json ├── concurrency ├── bounded_parallelism.go ├── bounded_parallelism.md ├── generator.md ├── parallelism.go └── parallelism.md ├── creational ├── builder.md ├── factory.md ├── object-pool.md └── singleton.md ├── gopher.png ├── idiom └── functional-options.md ├── messaging ├── fan_in.md ├── fan_out.md └── publish_subscribe.md ├── profiling └── timing.md ├── stability └── circuit-breaker.md ├── structural ├── decorator.md ├── proxy.md └── proxy │ └── main.go └── synchronization └── semaphore.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.prof 23 | # Test binary, build with `go test -c` 24 | *.test 25 | # Binaries for programs and plugins 26 | *.exe 27 | *.dll 28 | *.dylib 29 | 30 | # JetBrains project files 31 | .idea/ 32 | 33 | # Output of the go coverage tool, specifically when used with LiteIDE 34 | *.out -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | env: 4 | global: 5 | - GH_REPO="github.com/tmrts/go-patterns" 6 | - secure: SRAVBGLCkoVpCNC5J43qC6xcQvigIYbGKgLMLiP9B4XiyKH/Q6VGjk/BVVPYuC0d072jNjgfhVdTLx/jGgy6nN+AD7i8U/FoDY6pQmy4cK1nghUUlt44mq7JTlXYHLmV3NsaxmRMV5QuO9L/9AMcCh6U0MxrgMYafSPaSdHQq8hTkFFOYU05zKKUihLF3sVfEZ0KpxhHjtKA+SqcJK2NjqaGdySaziSe6Nj1kZgF9/SJkOiw/bM7O4/uqFXqEGZo5QaOQpwaj2B0wfGqwfJtyE2wM+80Aw5Ya/yqdQWplUozHKv36/u1N45cHkeDbr+RXnBpmUfGh8YTbInWh9BjyU5MLgKeJTtUMAVvwr/soa+OsHuGmdeVM5mRdXISlFSnXCkoowJ6iQsPdqGvYROz0KqqXmkVDuUKdxPU4ShyKo/LqtRwXvxQS9etF4ais8MoNmW0zI3eKdc4b6cpCXWt5fUtK8uzSUGDHHVFGpWnk8VsF0cPfLYxd9bo87amHqYGQoPJ4ughTtOAbA6uSNlcDM9AkQ591+vHpQE15td2VXUOf7aKqqPFWy+GagsI/yPry6v3d/Mk5D4ZLUXZGOv5uvengyos0dxWg9EV1yjm/mpiCtuqAtvV9HMNxcMGGCii7dMy37WmGBj3HBqeGPYHvt8pKMo2/gkcXxadzBXvJVs= 7 | 8 | install: 9 | - npm install gitbook-cli 10 | - gitbook install 11 | 12 | script: 13 | - gitbook build . out 14 | 15 | after_success: 16 | - echo -e "Deploying updates to GitHub..." 17 | - MSG=$(git log -1 --oneline) 18 | - cd out 19 | - git config --global user.email "contact@tmrts.com" 20 | - git config --global user.name "Tamer Tas" 21 | - git init 22 | - git checkout -b gh-pages 23 | - git add -A :/ 24 | - git commit -m "Travis CI | ${MSG}" 25 | - git push "https://${GH_TOKEN}@${GH_REPO}" gh-pages -f 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Please ensure your pull request adheres to the following guidelines: 4 | 5 | - Make an individual pull request for each suggestion. 6 | - Choose the corresponding patterns section for your suggestion. 7 | - List, after your addition, should be in lexicographical order. 8 | 9 | ## Commit Messages Guidelines 10 | 11 | - The message should be in imperative form and uncapitalized. 12 | - If possible, please include an explanation in the commit message body 13 | - Use the form `/: ` (e.g. `creational/singleton: refactor singleton constructor`) 14 | 15 | ## Pattern Template 16 | 17 | Each pattern should have a single markdown file containing the important part of the implementation, the usage and the explanations for it. This is to ensure that the reader doesn't have to read bunch of boilerplate to understand what's going on and the code is as simple as possible and not simpler. 18 | 19 | Please use the following template for adding new patterns: 20 | 21 | ```markdown 22 | # 23 | 24 | 25 | ## Implementation 26 | 27 | ## Usage 28 | 29 | // Optional 30 | ## Rules of Thumb 31 | ``` 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Go Patterns 5 |
6 | build-status 7 | awesome 8 | license 9 |

10 |

11 | 12 | A curated collection of idiomatic design & application patterns for Go language. 13 | 14 | ## Creational Patterns 15 | 16 | | Pattern | Description | Status | 17 | |:-------:|:----------- |:------:| 18 | | [Abstract Factory](/creational/abstract_factory.md) | Provides an interface for creating families of releated objects | ✘ | 19 | | [Builder](/creational/builder.md) | Builds a complex object using simple objects | ✔ | 20 | | [Factory Method](/creational/factory.md) | Defers instantiation of an object to a specialized function for creating instances | ✔ | 21 | | [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | ✔ | 22 | | [Singleton](/creational/singleton.md) | Restricts instantiation of a type to one object | ✔ | 23 | 24 | ## Structural Patterns 25 | 26 | | Pattern | Description | Status | 27 | |:-------:|:----------- |:------:| 28 | | [Bridge](/structural/bridge.md) | Decouples an interface from its implementation so that the two can vary independently | ✘ | 29 | | [Composite](/structural/composite.md) | Encapsulates and provides access to a number of different objects | ✘ | 30 | | [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✔ | 31 | | [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ | 32 | | [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ | 33 | | [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✔ | 34 | 35 | ## Behavioral Patterns 36 | 37 | | Pattern | Description | Status | 38 | |:-------:|:----------- |:------:| 39 | | [Chain of Responsibility](/behavioral/chain_of_responsibility.md) | Avoids coupling a sender to receiver by giving more than object a chance to handle the request | ✘ | 40 | | [Command](/behavioral/command.md) | Bundles a command and arguments to call later | ✘ | 41 | | [Mediator](/behavioral/mediator.md) | Connects objects and acts as a proxy | ✘ | 42 | | [Memento](/behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state | ✘ | 43 | | [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ | 44 | | [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ | 45 | | [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | ✘ | 46 | | [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ | 47 | | [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | ✘ | 48 | | [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ | 49 | 50 | ## Synchronization Patterns 51 | 52 | | Pattern | Description | Status | 53 | |:-------:|:----------- |:------:| 54 | | [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | ✘ | 55 | | [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | ✘ | 56 | | [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | ✘ | 57 | | [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | ✘ | 58 | | [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | ✔ | 59 | 60 | ## Concurrency Patterns 61 | 62 | | Pattern | Description | Status | 63 | |:-------:|:----------- |:------:| 64 | | [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ | 65 | | [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ | 66 | | [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ | 67 | | [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ | 68 | | [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | ✔ | 69 | | [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | ✘ | 70 | | [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | ✔ | 71 | | [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | ✘ | 72 | 73 | ## Messaging Patterns 74 | 75 | | Pattern | Description | Status | 76 | |:-------:|:----------- |:------:| 77 | | [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | ✔ | 78 | | [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | ✔ | 79 | | [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | ✘ | 80 | | [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | ✔ | 81 | | [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | ✘ | 82 | 83 | ## Stability Patterns 84 | 85 | | Pattern | Description | Status | 86 | |:-------:|:----------- |:------:| 87 | | [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | ✘ | 88 | | [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | ✔ | 89 | | [Deadline](/stability/deadline.md) | Allows clients to stop waiting for a response once the probability of response becomes low (e.g. after waiting 10 seconds for a page refresh) | ✘ | 90 | | [Fail-Fast](/stability/fail_fast.md) | Checks the availability of required resources at the start of a request and fails if the requirements are not satisfied | ✘ | 91 | | [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | ✘ | 92 | | [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | ✘ | 93 | 94 | ## Profiling Patterns 95 | 96 | | Pattern | Description | Status | 97 | |:-------:|:----------- |:------:| 98 | | [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | ✔ | 99 | 100 | ## Idioms 101 | 102 | | Pattern | Description | Status | 103 | |:-------:|:----------- |:------:| 104 | | [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | ✔ | 105 | 106 | ## Anti-Patterns 107 | 108 | | Pattern | Description | Status | 109 | |:-------:|:----------- |:------:| 110 | | [Cascading Failures](/anti-patterns/cascading_failures.md) | A failure in a system of interconnected parts in which the failure of a part causes a domino effect | ✘ | 111 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Go Patterns](/README.md) 4 | * [Creational Patterns](/README.md#creational-patterns) 5 | * [Abstract Factory](/creational/abstract_factory.md) 6 | * [Builder](/creational/builder.md) 7 | * [Factory Method](/creational/factory.md) 8 | * [Object Pool](/creational/object-pool.md) 9 | * [Singleton](/creational/singleton.md) 10 | * [Structural Patterns](/README.md#structural-patterns) 11 | * [Bridge](/structural/bridge.md) 12 | * [Composite](/structural/composite.md) 13 | * [Decorator](/structural/decorator.md) 14 | * [Facade](/structural/facade.md) 15 | * [Flyweight](/structural/flyweight.md) 16 | * [Proxy](/structural/proxy.md) 17 | * [Behavioral Patterns](/README.md#behavioral-patterns) 18 | * [Chain of Responsibility](/behavioral/chain_of_responsibility.md) 19 | * [Command](/behavioral/command.md) 20 | * [Mediator](/behavioral/mediator.md) 21 | * [Memento](/behavioral/memento.md) 22 | * [Observer](/behavioral/observer.md) 23 | * [Registry](/behavioral/registry.md) 24 | * [State](/behavioral/state.md) 25 | * [Strategy](/behavioral/strategy.md) 26 | * [Template](/behavioral/template.md) 27 | * [Visitor](/behavioral/visitor.md) 28 | * [Synchronization Patterns](/README.md#synchronization-patterns) 29 | * [Condition Variable](/synchronization/condition_variable.md) 30 | * [Lock/Mutex](/synchronization/mutex.md) 31 | * [Monitor](/synchronization/monitor.md) 32 | * [Read-Write Lock](/synchronization/read_write_lock.md) 33 | * [Semaphore](/synchronization/semaphore.md) 34 | * [Concurrency Patterns](/README.md#concurrency-patterns) 35 | * [N-Barrier](/concurrency/barrier.md) 36 | * [Bounded Parallelism](/concurrency/bounded_parallelism.md) 37 | * [Broadcast](/concurrency/broadcast.md) 38 | * [Coroutines](/concurrency/coroutine.md) 39 | * [Generators](/concurrency/generator.md) 40 | * [Reactor](/concurrency/reactor.md) 41 | * [Parallelism](/concurrency/parallelism.md) 42 | * [Producer Consumer](/concurrency/producer_consumer.md) 43 | * [Messaging Patterns](/README.md#messaging-patterns) 44 | * [Fan-In](/messaging/fan_in.md) 45 | * [Fan-Out](/messaging/fan_out.md) 46 | * [Futures & Promises](/messaging/futures_promises.md) 47 | * [Publish/Subscribe](/messaging/publish_subscribe.md) 48 | * [Push & Pull](/messaging/push_pull.md) 49 | * [Stability Patterns](/README.md#stability-patterns) 50 | * [Bulkheads](/stability/bulkhead.md) 51 | * [Circuit-Breaker](/stability/circuit-breaker.md) 52 | * [Deadline](/stability/deadline.md) 53 | * [Fail-Fast](/stability/fail_fast.md) 54 | * [Handshaking](/stability/handshaking.md) 55 | * [Steady-State](/stability/steady_state.md) 56 | * [Profiling Patterns](/README.md#profiling-patterns) 57 | * [Timing Functions](/profiling/timing.md) 58 | * [Idioms](/README.md#idioms) 59 | * [Functional Options](/idiom/functional-options.md) 60 | * [Anti-Patterns](/README.md#anti-patterns) 61 | * [Cascading Failures](/anti-patterns/cascading_failures.md) 62 | * [Contributing](/CONTRIBUTING.md) 63 | -------------------------------------------------------------------------------- /behavioral/observer.md: -------------------------------------------------------------------------------- 1 | # Observer Pattern 2 | 3 | The [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows a type instance to "publish" events to other type instances ("observers") who wish to be updated when a particular event occurs. 4 | 5 | ## Implementation 6 | 7 | In long-running applications—such as webservers—instances can keep a collection of observers that will receive notification of triggered events. 8 | 9 | Implementations vary, but interfaces can be used to make standard observers and notifiers: 10 | 11 | ```go 12 | type ( 13 | // Event defines an indication of a point-in-time occurrence. 14 | Event struct { 15 | // Data in this case is a simple int, but the actual 16 | // implementation would depend on the application. 17 | Data int64 18 | } 19 | 20 | // Observer defines a standard interface for instances that wish to list for 21 | // the occurrence of a specific event. 22 | Observer interface { 23 | // OnNotify allows an event to be "published" to interface implementations. 24 | // In the "real world", error handling would likely be implemented. 25 | OnNotify(Event) 26 | } 27 | 28 | // Notifier is the instance being observed. Publisher is perhaps another decent 29 | // name, but naming things is hard. 30 | Notifier interface { 31 | // Register allows an instance to register itself to listen/observe 32 | // events. 33 | Register(Observer) 34 | // Deregister allows an instance to remove itself from the collection 35 | // of observers/listeners. 36 | Deregister(Observer) 37 | // Notify publishes new events to listeners. The method is not 38 | // absolutely necessary, as each implementation could define this itself 39 | // without losing functionality. 40 | Notify(Event) 41 | } 42 | ) 43 | ``` 44 | 45 | ## Usage 46 | 47 | For usage, see [observer/main.go](observer/main.go) or [view in the Playground](https://play.golang.org/p/cr8jEmDmw0). 48 | -------------------------------------------------------------------------------- /behavioral/observer/main.go: -------------------------------------------------------------------------------- 1 | // Package main serves as an example application that makes use of the observer pattern. 2 | // Playground: https://play.golang.org/p/cr8jEmDmw0 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | type ( 11 | // Event defines an indication of a point-in-time occurrence. 12 | Event struct { 13 | // Data in this case is a simple int, but the actual 14 | // implementation would depend on the application. 15 | Data int64 16 | } 17 | 18 | // Observer defines a standard interface for instances that wish to list for 19 | // the occurrence of a specific event. 20 | Observer interface { 21 | // OnNotify allows an event to be "published" to interface implementations. 22 | // In the "real world", error handling would likely be implemented. 23 | OnNotify(Event) 24 | } 25 | 26 | // Notifier is the instance being observed. Publisher is perhaps another decent 27 | // name, but naming things is hard. 28 | Notifier interface { 29 | // Register allows an instance to register itself to listen/observe 30 | // events. 31 | Register(Observer) 32 | // Deregister allows an instance to remove itself from the collection 33 | // of observers/listeners. 34 | Deregister(Observer) 35 | // Notify publishes new events to listeners. The method is not 36 | // absolutely necessary, as each implementation could define this itself 37 | // without losing functionality. 38 | Notify(Event) 39 | } 40 | ) 41 | 42 | type ( 43 | eventObserver struct{ 44 | id int 45 | } 46 | 47 | eventNotifier struct{ 48 | // Using a map with an empty struct allows us to keep the observers 49 | // unique while still keeping memory usage relatively low. 50 | observers map[Observer]struct{} 51 | } 52 | ) 53 | 54 | func (o *eventObserver) OnNotify(e Event) { 55 | fmt.Printf("*** Observer %d received: %d\n", o.id, e.Data) 56 | } 57 | 58 | func (o *eventNotifier) Register(l Observer) { 59 | o.observers[l] = struct{}{} 60 | } 61 | 62 | func (o *eventNotifier) Deregister(l Observer) { 63 | delete(o.observers, l) 64 | } 65 | 66 | func (p *eventNotifier) Notify(e Event) { 67 | for o := range p.observers { 68 | o.OnNotify(e) 69 | } 70 | } 71 | 72 | func main() { 73 | // Initialize a new Notifier. 74 | n := eventNotifier{ 75 | observers: map[Observer]struct{}{}, 76 | } 77 | 78 | // Register a couple of observers. 79 | n.Register(&eventObserver{id: 1}) 80 | n.Register(&eventObserver{id: 2}) 81 | 82 | // A simple loop publishing the current Unix timestamp to observers. 83 | stop := time.NewTimer(10 * time.Second).C 84 | tick := time.NewTicker(time.Second).C 85 | for { 86 | select { 87 | case <- stop: 88 | return 89 | case t := <-tick: 90 | n.Notify(Event{Data: t.UnixNano()}) 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /behavioral/strategy.md: -------------------------------------------------------------------------------- 1 | # Strategy Pattern 2 | Strategy behavioral design pattern enables an algorithm's behavior to be selected at runtime. 3 | 4 | It defines algorithms, encapsulates them, and uses them interchangeably. 5 | 6 | ## Implementation 7 | Implementation of an interchangeable operator object that operates on integers. 8 | 9 | ```go 10 | type Operator interface { 11 | Apply(int, int) int 12 | } 13 | 14 | type Operation struct { 15 | Operator Operator 16 | } 17 | 18 | func (o *Operation) Operate(leftValue, rightValue int) int { 19 | return o.Operator.Apply(leftValue, rightValue) 20 | } 21 | ``` 22 | 23 | ## Usage 24 | ### Addition Operator 25 | ```go 26 | type Addition struct{} 27 | 28 | func (Addition) Apply(lval, rval int) int { 29 | return lval + rval 30 | } 31 | ``` 32 | 33 | ```go 34 | add := Operation{Addition{}} 35 | add.Operate(3, 5) // 8 36 | ``` 37 | 38 | ### Multiplication Operator 39 | ```go 40 | type Multiplication struct{} 41 | 42 | func (Multiplication) Apply(lval, rval int) int { 43 | return lval * rval 44 | } 45 | ``` 46 | 47 | ```go 48 | mult := Operation{Multiplication{}} 49 | 50 | mult.Operate(3, 5) // 15 51 | ``` 52 | 53 | ## Rules of Thumb 54 | - Strategy pattern is similar to Template pattern except in its granularity. 55 | - Strategy pattern lets you change the guts of an object. Decorator pattern lets you change the skin. 56 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "-search", 4 | "-lunr", 5 | "github", 6 | "edit-link" 7 | ], 8 | "pluginsConfig": { 9 | "github": { 10 | "url": "https://github.com/tmrts/go-patterns" 11 | }, 12 | "edit-link": { 13 | "base": "https://github.com/tmrts/go-patterns/edit/master/", 14 | "label": "" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /concurrency/bounded_parallelism.go: -------------------------------------------------------------------------------- 1 | package bounded_parallelism 2 | 3 | import ( 4 | "crypto/md5" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "sort" 11 | "sync" 12 | ) 13 | 14 | // walkFiles starts a goroutine to walk the directory tree at root and send the 15 | // path of each regular file on the string channel. It sends the result of the 16 | // walk on the error channel. If done is closed, walkFiles abandons its work. 17 | func walkFiles(done <-chan struct{}, root string) (<-chan string, <-chan error) { 18 | paths := make(chan string) 19 | errc := make(chan error, 1) 20 | go func() { // HL 21 | // Close the paths channel after Walk returns. 22 | defer close(paths) // HL 23 | // No select needed for this send, since errc is buffered. 24 | errc <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // HL 25 | if err != nil { 26 | return err 27 | } 28 | if !info.Mode().IsRegular() { 29 | return nil 30 | } 31 | select { 32 | case paths <- path: // HL 33 | case <-done: // HL 34 | return errors.New("walk canceled") 35 | } 36 | return nil 37 | }) 38 | }() 39 | return paths, errc 40 | } 41 | 42 | // A result is the product of reading and summing a file using MD5. 43 | type result struct { 44 | path string 45 | sum [md5.Size]byte 46 | err error 47 | } 48 | 49 | // digester reads path names from paths and sends digests of the corresponding 50 | // files on c until either paths or done is closed. 51 | func digester(done <-chan struct{}, paths <-chan string, c chan<- result) { 52 | for path := range paths { // HLpaths 53 | data, err := ioutil.ReadFile(path) 54 | select { 55 | case c <- result{path, md5.Sum(data), err}: 56 | case <-done: 57 | return 58 | } 59 | } 60 | } 61 | 62 | // MD5All reads all the files in the file tree rooted at root and returns a map 63 | // from file path to the MD5 sum of the file's contents. If the directory walk 64 | // fails or any read operation fails, MD5All returns an error. In that case, 65 | // MD5All does not wait for inflight read operations to complete. 66 | func MD5All(root string) (map[string][md5.Size]byte, error) { 67 | // MD5All closes the done channel when it returns; it may do so before 68 | // receiving all the values from c and errc. 69 | done := make(chan struct{}) 70 | defer close(done) 71 | 72 | paths, errc := walkFiles(done, root) 73 | 74 | // Start a fixed number of goroutines to read and digest files. 75 | c := make(chan result) // HLc 76 | var wg sync.WaitGroup 77 | const numDigesters = 20 78 | wg.Add(numDigesters) 79 | for i := 0; i < numDigesters; i++ { 80 | go func() { 81 | digester(done, paths, c) // HLc 82 | wg.Done() 83 | }() 84 | } 85 | go func() { 86 | wg.Wait() 87 | close(c) // HLc 88 | }() 89 | // End of pipeline. OMIT 90 | 91 | m := make(map[string][md5.Size]byte) 92 | for r := range c { 93 | if r.err != nil { 94 | return nil, r.err 95 | } 96 | m[r.path] = r.sum 97 | } 98 | // Check whether the Walk failed. 99 | if err := <-errc; err != nil { // HLerrc 100 | return nil, err 101 | } 102 | return m, nil 103 | } 104 | 105 | func main() { 106 | // Calculate the MD5 sum of all files under the specified directory, 107 | // then print the results sorted by path name. 108 | m, err := MD5All(os.Args[1]) 109 | if err != nil { 110 | fmt.Println(err) 111 | return 112 | } 113 | var paths []string 114 | for path := range m { 115 | paths = append(paths, path) 116 | } 117 | sort.Strings(paths) 118 | for _, path := range paths { 119 | fmt.Printf("%x %s\n", m[path], path) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /concurrency/bounded_parallelism.md: -------------------------------------------------------------------------------- 1 | # Bounded Parallelism Pattern 2 | 3 | [Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](parallelism.md), but allows limits to be placed on allocation. 4 | 5 | # Implementation and Example 6 | 7 | An example showing implementation and usage can be found in [bounded_parallelism.go](bounded_parallelism.go). -------------------------------------------------------------------------------- /concurrency/generator.md: -------------------------------------------------------------------------------- 1 | # Generator Pattern 2 | 3 | [Generators](https://en.wikipedia.org/wiki/Generator_(computer_programming)) yields a sequence of values one at a time. 4 | 5 | ## Implementation 6 | 7 | ```go 8 | func Count(start int, end int) chan int { 9 | ch := make(chan int) 10 | 11 | go func(ch chan int) { 12 | for i := start; i <= end ; i++ { 13 | // Blocks on the operation 14 | ch <- i 15 | } 16 | 17 | close(ch) 18 | }(ch) 19 | 20 | return ch 21 | } 22 | ``` 23 | 24 | ## Usage 25 | 26 | ```go 27 | fmt.Println("No bottles of beer on the wall") 28 | 29 | for i := range Count(1, 99) { 30 | fmt.Println("Pass it around, put one up,", i, "bottles of beer on the wall") 31 | // Pass it around, put one up, 1 bottles of beer on the wall 32 | // Pass it around, put one up, 2 bottles of beer on the wall 33 | // ... 34 | // Pass it around, put one up, 99 bottles of beer on the wall 35 | } 36 | 37 | fmt.Println(100, "bottles of beer on the wall") 38 | ``` 39 | -------------------------------------------------------------------------------- /concurrency/parallelism.go: -------------------------------------------------------------------------------- 1 | package parallelism 2 | 3 | import ( 4 | "crypto/md5" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "sort" 11 | "sync" 12 | ) 13 | 14 | // A result is the product of reading and summing a file using MD5. 15 | type result struct { 16 | path string 17 | sum [md5.Size]byte 18 | err error 19 | } 20 | 21 | // sumFiles starts goroutines to walk the directory tree at root and digest each 22 | // regular file. These goroutines send the results of the digests on the result 23 | // channel and send the result of the walk on the error channel. If done is 24 | // closed, sumFiles abandons its work. 25 | func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) { 26 | // For each regular file, start a goroutine that sums the file and sends 27 | // the result on c. Send the result of the walk on errc. 28 | c := make(chan result) 29 | errc := make(chan error, 1) 30 | go func() { // HL 31 | var wg sync.WaitGroup 32 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 33 | if err != nil { 34 | return err 35 | } 36 | if !info.Mode().IsRegular() { 37 | return nil 38 | } 39 | wg.Add(1) 40 | go func() { // HL 41 | data, err := ioutil.ReadFile(path) 42 | select { 43 | case c <- result{path, md5.Sum(data), err}: // HL 44 | case <-done: // HL 45 | } 46 | wg.Done() 47 | }() 48 | // Abort the walk if done is closed. 49 | select { 50 | case <-done: // HL 51 | return errors.New("walk canceled") 52 | default: 53 | return nil 54 | } 55 | }) 56 | // Walk has returned, so all calls to wg.Add are done. Start a 57 | // goroutine to close c once all the sends are done. 58 | go func() { // HL 59 | wg.Wait() 60 | close(c) // HL 61 | }() 62 | // No select needed here, since errc is buffered. 63 | errc <- err // HL 64 | }() 65 | return c, errc 66 | } 67 | 68 | // MD5All reads all the files in the file tree rooted at root and returns a map 69 | // from file path to the MD5 sum of the file's contents. If the directory walk 70 | // fails or any read operation fails, MD5All returns an error. In that case, 71 | // MD5All does not wait for inflight read operations to complete. 72 | func MD5All(root string) (map[string][md5.Size]byte, error) { 73 | // MD5All closes the done channel when it returns; it may do so before 74 | // receiving all the values from c and errc. 75 | done := make(chan struct{}) // HLdone 76 | defer close(done) // HLdone 77 | 78 | c, errc := sumFiles(done, root) // HLdone 79 | 80 | m := make(map[string][md5.Size]byte) 81 | for r := range c { // HLrange 82 | if r.err != nil { 83 | return nil, r.err 84 | } 85 | m[r.path] = r.sum 86 | } 87 | if err := <-errc; err != nil { 88 | return nil, err 89 | } 90 | return m, nil 91 | } 92 | 93 | func main() { 94 | // Calculate the MD5 sum of all files under the specified directory, 95 | // then print the results sorted by path name. 96 | m, err := MD5All(os.Args[1]) 97 | if err != nil { 98 | fmt.Println(err) 99 | return 100 | } 101 | var paths []string 102 | for path := range m { 103 | paths = append(paths, path) 104 | } 105 | sort.Strings(paths) 106 | for _, path := range paths { 107 | fmt.Printf("%x %s\n", m[path], path) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /concurrency/parallelism.md: -------------------------------------------------------------------------------- 1 | # Parallelism Pattern 2 | 3 | [Parallelism](https://blog.golang.org/pipelines#TOC_8.) allows multiple "jobs" or tasks to be run concurrently and asynchronously. 4 | 5 | # Implementation and Example 6 | 7 | An example showing implementation and usage can be found in [parallelism.go](parallelism.go). -------------------------------------------------------------------------------- /creational/builder.md: -------------------------------------------------------------------------------- 1 | # Builder Pattern 2 | 3 | Builder pattern separates the construction of a complex object from its 4 | representation so that the same construction process can create different 5 | representations. 6 | 7 | In Go, normally a configuration struct is used to achieve the same behavior, 8 | however passing a struct to the builder method fills the code with boilerplate 9 | `if cfg.Field != nil {...}` checks. 10 | 11 | ## Implementation 12 | 13 | ```go 14 | package car 15 | 16 | type Speed float64 17 | 18 | const ( 19 | MPH Speed = 1 20 | KPH = 1.60934 21 | ) 22 | 23 | type Color string 24 | 25 | const ( 26 | BlueColor Color = "blue" 27 | GreenColor = "green" 28 | RedColor = "red" 29 | ) 30 | 31 | type Wheels string 32 | 33 | const ( 34 | SportsWheels Wheels = "sports" 35 | SteelWheels = "steel" 36 | ) 37 | 38 | type Builder interface { 39 | Color(Color) Builder 40 | Wheels(Wheels) Builder 41 | TopSpeed(Speed) Builder 42 | Build() Interface 43 | } 44 | 45 | type Interface interface { 46 | Drive() error 47 | Stop() error 48 | } 49 | ``` 50 | 51 | ## Usage 52 | 53 | ```go 54 | assembly := car.NewBuilder().Paint(car.RedColor) 55 | 56 | familyCar := assembly.Wheels(car.SportsWheels).TopSpeed(50 * car.MPH).Build() 57 | familyCar.Drive() 58 | 59 | sportsCar := assembly.Wheels(car.SteelWheels).TopSpeed(150 * car.MPH).Build() 60 | sportsCar.Drive() 61 | ``` 62 | -------------------------------------------------------------------------------- /creational/factory.md: -------------------------------------------------------------------------------- 1 | # Factory Method Pattern 2 | 3 | Factory method creational design pattern allows creating objects without having 4 | to specify the exact type of the object that will be created. 5 | 6 | ## Implementation 7 | 8 | The example implementation shows how to provide a data store with different 9 | backends such as in-memory, disk storage. 10 | 11 | ### Types 12 | 13 | ```go 14 | package data 15 | 16 | import "io" 17 | 18 | type Store interface { 19 | Open(string) (io.ReadWriteCloser, error) 20 | } 21 | ``` 22 | 23 | ### Different Implementations 24 | 25 | ```go 26 | package data 27 | 28 | type StorageType int 29 | 30 | const ( 31 | DiskStorage StorageType = 1 << iota 32 | TempStorage 33 | MemoryStorage 34 | ) 35 | 36 | func NewStore(t StorageType) Store { 37 | switch t { 38 | case MemoryStorage: 39 | return newMemoryStorage( /*...*/ ) 40 | case DiskStorage: 41 | return newDiskStorage( /*...*/ ) 42 | default: 43 | return newTempStorage( /*...*/ ) 44 | } 45 | } 46 | ``` 47 | 48 | ## Usage 49 | 50 | With the factory method, the user can specify the type of storage they want. 51 | 52 | ```go 53 | s, _ := data.NewStore(data.MemoryStorage) 54 | f, _ := s.Open("file") 55 | 56 | n, _ := f.Write([]byte("data")) 57 | defer f.Close() 58 | ``` 59 | -------------------------------------------------------------------------------- /creational/object-pool.md: -------------------------------------------------------------------------------- 1 | # Object Pool Pattern 2 | 3 | The object pool creational design pattern is used to prepare and keep multiple 4 | instances according to the demand expectation. 5 | 6 | ## Implementation 7 | 8 | ```go 9 | package pool 10 | 11 | type Pool chan *Object 12 | 13 | func New(total int) *Pool { 14 | p := make(Pool, total) 15 | 16 | for i := 0; i < total; i++ { 17 | p <- new(Object) 18 | } 19 | 20 | return &p 21 | } 22 | ``` 23 | 24 | ## Usage 25 | 26 | Given below is a simple lifecycle example on an object pool. 27 | 28 | ```go 29 | p := pool.New(2) 30 | 31 | select { 32 | case obj := <-p: 33 | obj.Do( /*...*/ ) 34 | 35 | p <- obj 36 | default: 37 | // No more objects left — retry later or fail 38 | return 39 | } 40 | ``` 41 | 42 | ## Rules of Thumb 43 | 44 | - Object pool pattern is useful in cases where object initialization is more 45 | expensive than the object maintenance. 46 | - If there are spikes in demand as opposed to a steady demand, the maintenance 47 | overhead might overweigh the benefits of an object pool. 48 | - It has positive effects on performance due to objects being initialized beforehand. 49 | -------------------------------------------------------------------------------- /creational/singleton.md: -------------------------------------------------------------------------------- 1 | # Singleton Pattern 2 | 3 | Singleton creational design pattern restricts the instantiation of a type to a single object. 4 | 5 | ## Implementation 6 | 7 | ```go 8 | package singleton 9 | 10 | type singleton map[string]string 11 | 12 | var ( 13 | once sync.Once 14 | 15 | instance singleton 16 | ) 17 | 18 | func New() singleton { 19 | once.Do(func() { 20 | instance = make(singleton) 21 | }) 22 | 23 | return instance 24 | } 25 | ``` 26 | 27 | ## Usage 28 | 29 | ```go 30 | s := singleton.New() 31 | 32 | s["this"] = "that" 33 | 34 | s2 := singleton.New() 35 | 36 | fmt.Println("This is ", s2["this"]) 37 | // This is that 38 | ``` 39 | 40 | ## Rules of Thumb 41 | 42 | - Singleton pattern represents a global state and most of the time reduces testability. 43 | -------------------------------------------------------------------------------- /gopher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fantasy1114/golang-patterns/719ce6673a365734aec98d097e4cd007edaf2289/gopher.png -------------------------------------------------------------------------------- /idiom/functional-options.md: -------------------------------------------------------------------------------- 1 | # Functional Options 2 | 3 | Functional options are a method of implementing clean/eloquent APIs in Go. 4 | Options implemented as a function set the state of that option. 5 | 6 | ## Implementation 7 | 8 | ### Options 9 | 10 | ```go 11 | package file 12 | 13 | type Options struct { 14 | UID int 15 | GID int 16 | Flags int 17 | Contents string 18 | Permissions os.FileMode 19 | } 20 | 21 | type Option func(*Options) 22 | 23 | func UID(userID int) Option { 24 | return func(args *Options) { 25 | args.UID = userID 26 | } 27 | } 28 | 29 | func GID(groupID int) Option { 30 | return func(args *Options) { 31 | args.GID = groupID 32 | } 33 | } 34 | 35 | func Contents(c string) Option { 36 | return func(args *Options) { 37 | args.Contents = c 38 | } 39 | } 40 | 41 | func Permissions(perms os.FileMode) Option { 42 | return func(args *Options) { 43 | args.Permissions = perms 44 | } 45 | } 46 | ``` 47 | 48 | ### Constructor 49 | 50 | ```go 51 | package file 52 | 53 | func New(filepath string, setters ...Option) error { 54 | // Default Options 55 | args := &Options{ 56 | UID: os.Getuid(), 57 | GID: os.Getgid(), 58 | Contents: "", 59 | Permissions: 0666, 60 | Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY, 61 | } 62 | 63 | for _, setter := range setters { 64 | setter(args) 65 | } 66 | 67 | f, err := os.OpenFile(filepath, args.Flags, args.Permissions) 68 | if err != nil { 69 | return err 70 | } else { 71 | defer f.Close() 72 | } 73 | 74 | if _, err := f.WriteString(args.Contents); err != nil { 75 | return err 76 | } 77 | 78 | return f.Chown(args.UID, args.GID) 79 | } 80 | ``` 81 | 82 | ## Usage 83 | 84 | ```go 85 | emptyFile, err := file.New("/tmp/empty.txt") 86 | if err != nil { 87 | panic(err) 88 | } 89 | 90 | fillerFile, err := file.New("/tmp/file.txt", file.UID(1000), file.Contents("Lorem Ipsum Dolor Amet")) 91 | if err != nil { 92 | panic(err) 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /messaging/fan_in.md: -------------------------------------------------------------------------------- 1 | Fan-In Messaging Patterns 2 | =================================== 3 | Fan-In is a messaging pattern used to create a funnel for work amongst workers (clients: source, server: destination). 4 | 5 | We can model fan-in using the Go channels. 6 | 7 | ```go 8 | // Merge different channels in one channel 9 | func Merge(cs ...<-chan int) <-chan int { 10 | var wg sync.WaitGroup 11 | 12 | out := make(chan int) 13 | 14 | // Start an send goroutine for each input channel in cs. send 15 | // copies values from c to out until c is closed, then calls wg.Done. 16 | send := func(c <-chan int) { 17 | for n := range c { 18 | out <- n 19 | } 20 | wg.Done() 21 | } 22 | 23 | wg.Add(len(cs)) 24 | for _, c := range cs { 25 | go send(c) 26 | } 27 | 28 | // Start a goroutine to close out once all the send goroutines are 29 | // done. This must start after the wg.Add call. 30 | go func() { 31 | wg.Wait() 32 | close(out) 33 | }() 34 | return out 35 | } 36 | ``` 37 | 38 | The `Merge` function converts a list of channels to a single channel by starting a goroutine for each inbound channel that copies the values to the sole outbound channel. 39 | 40 | Once all the output goroutines have been started, `Merge` a goroutine is started to close the main channel. 41 | -------------------------------------------------------------------------------- /messaging/fan_out.md: -------------------------------------------------------------------------------- 1 | Fan-Out Messaging Pattern 2 | ========================= 3 | Fan-Out is a messaging pattern used for distributing work amongst workers (producer: source, consumers: destination). 4 | 5 | We can model fan-out using the Go channels. 6 | 7 | ```go 8 | // Split a channel into n channels that receive messages in a round-robin fashion. 9 | func Split(ch <-chan int, n int) []<-chan int { 10 | cs := make([]chan int) 11 | for i := 0; i < n; i++ { 12 | cs = append(cs, make(chan int)) 13 | } 14 | 15 | // Distributes the work in a round robin fashion among the stated number 16 | // of channels until the main channel has been closed. In that case, close 17 | // all channels and return. 18 | distributeToChannels := func(ch <-chan int, cs []chan<- int) { 19 | // Close every channel when the execution ends. 20 | defer func(cs []chan<- int) { 21 | for _, c := range cs { 22 | close(c) 23 | } 24 | }(cs) 25 | 26 | for { 27 | for _, c := range cs { 28 | select { 29 | case val, ok := <-ch: 30 | if !ok { 31 | return 32 | } 33 | 34 | c <- val 35 | } 36 | } 37 | } 38 | } 39 | 40 | go distributeToChannels(ch, cs) 41 | 42 | return cs 43 | } 44 | ``` 45 | 46 | The `Split` function converts a single channel into a list of channels by using 47 | a goroutine to copy received values to channels in the list in a round-robin fashion. 48 | -------------------------------------------------------------------------------- /messaging/publish_subscribe.md: -------------------------------------------------------------------------------- 1 | Publish & Subscribe Messaging Pattern 2 | ============ 3 | Publish-Subscribe is a messaging pattern used to communicate messages between 4 | different components without these components knowing anything about each other's identity. 5 | 6 | It is similar to the Observer behavioral design pattern. 7 | The fundamental design principals of both Observer and Publish-Subscribe is the decoupling of 8 | those interested in being informed about `Event Messages` from the informer (Observers or Publishers). 9 | Meaning that you don't have to program the messages to be sent directly to specific receivers. 10 | 11 | To accomplish this, an intermediary, called a "message broker" or "event bus", 12 | receives published messages, and then routes them on to subscribers. 13 | 14 | 15 | There are three components **messages**, **topics**, **users**. 16 | 17 | ```go 18 | type Message struct { 19 | // Contents 20 | } 21 | 22 | 23 | type Subscription struct { 24 | ch chan<- Message 25 | 26 | Inbox chan Message 27 | } 28 | 29 | func (s *Subscription) Publish(msg Message) error { 30 | if _, ok := <-s.ch; !ok { 31 | return errors.New("Topic has been closed") 32 | } 33 | 34 | s.ch <- msg 35 | 36 | return nil 37 | } 38 | ``` 39 | 40 | ```go 41 | type Topic struct { 42 | Subscribers []Session 43 | MessageHistory []Message 44 | } 45 | 46 | func (t *Topic) Subscribe(uid uint64) (Subscription, error) { 47 | // Get session and create one if it's the first 48 | 49 | // Add session to the Topic & MessageHistory 50 | 51 | // Create a subscription 52 | } 53 | 54 | func (t *Topic) Unsubscribe(Subscription) error { 55 | // Implementation 56 | } 57 | 58 | func (t *Topic) Delete() error { 59 | // Implementation 60 | } 61 | ``` 62 | 63 | ```go 64 | type User struct { 65 | ID uint64 66 | Name string 67 | } 68 | 69 | type Session struct { 70 | User User 71 | Timestamp time.Time 72 | } 73 | ``` 74 | 75 | Improvements 76 | ============ 77 | Events can be published in a parallel fashion by utilizing stackless goroutines. 78 | 79 | Performance can be improved by dealing with straggler subscribers 80 | by using a buffered inbox and you stop sending events once the inbox is full. 81 | -------------------------------------------------------------------------------- /profiling/timing.md: -------------------------------------------------------------------------------- 1 | # Timing Functions 2 | 3 | When optimizing code, sometimes a quick and dirty time measurement is required 4 | as opposed to utilizing profiler tools/frameworks to validate assumptions. 5 | 6 | Time measurements can be performed by utilizing `time` package and `defer` statements. 7 | 8 | ## Implementation 9 | 10 | ```go 11 | package profile 12 | 13 | import ( 14 | "time" 15 | "log" 16 | ) 17 | 18 | func Duration(invocation time.Time, name string) { 19 | elapsed := time.Since(invocation) 20 | 21 | log.Printf("%s lasted %s", name, elapsed) 22 | } 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```go 28 | func BigIntFactorial(x big.Int) *big.Int { 29 | // Arguments to a defer statement is immediately evaluated and stored. 30 | // The deferred function receives the pre-evaluated values when its invoked. 31 | defer profile.Duration(time.Now(), "IntFactorial") 32 | 33 | y := big.NewInt(1) 34 | for one := big.NewInt(1); x.Sign() > 0; x.Sub(x, one) { 35 | y.Mul(y, x) 36 | } 37 | 38 | return x.Set(y) 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /stability/circuit-breaker.md: -------------------------------------------------------------------------------- 1 | # Circuit Breaker Pattern 2 | 3 | Similar to electrical fuses that prevent fires when a circuit that is connected 4 | to the electrical grid starts drawing a high amount of power which causes the 5 | wires to heat up and combust, the circuit breaker design pattern is a fail-first 6 | mechanism that shuts down the circuit, request/response relationship or a 7 | service in the case of software development, to prevent bigger failures. 8 | 9 | **Note:** The words "circuit" and "service" are used synonymously throught this 10 | document. 11 | 12 | ## Implementation 13 | 14 | Below is the implementation of a very simple circuit breaker to illustrate the purpose 15 | of the circuit breaker design pattern. 16 | 17 | ### Operation Counter 18 | 19 | `circuit.Counter` is a simple counter that records success and failure states of 20 | a circuit along with a timestamp and calculates the consecutive number of 21 | failures. 22 | 23 | ```go 24 | package circuit 25 | 26 | import ( 27 | "time" 28 | ) 29 | 30 | type State int 31 | 32 | const ( 33 | UnknownState State = iota 34 | FailureState 35 | SuccessState 36 | ) 37 | 38 | type Counter interface { 39 | Count(State) 40 | ConsecutiveFailures() uint32 41 | LastActivity() time.Time 42 | Reset() 43 | } 44 | ``` 45 | 46 | ### Circuit Breaker 47 | 48 | Circuit is wrapped using the `circuit.Breaker` closure that keeps an internal operation counter. 49 | It returns a fast error if the circuit has failed consecutively more than the specified threshold. 50 | After a while it retries the request and records it. 51 | 52 | **Note:** Context type is used here to carry deadlines, cancelation signals, and 53 | other request-scoped values across API boundaries and between processes. 54 | 55 | ```go 56 | package circuit 57 | 58 | import ( 59 | "context" 60 | "time" 61 | ) 62 | 63 | type Circuit func(context.Context) error 64 | 65 | func Breaker(c Circuit, failureThreshold uint32) Circuit { 66 | cnt := NewCounter() 67 | 68 | return func(ctx context) error { 69 | if cnt.ConsecutiveFailures() >= failureThreshold { 70 | canRetry := func(cnt Counter) { 71 | backoffLevel := Cnt.ConsecutiveFailures() - failureThreshold 72 | 73 | // Calculates when should the circuit breaker resume propagating requests 74 | // to the service 75 | shouldRetryAt := cnt.LastActivity().Add(time.Seconds * 2 << backoffLevel) 76 | 77 | return time.Now().After(shouldRetryAt) 78 | } 79 | 80 | if !canRetry(cnt) { 81 | // Fails fast instead of propagating requests to the circuit since 82 | // not enough time has passed since the last failure to retry 83 | return ErrServiceUnavailable 84 | } 85 | } 86 | 87 | // Unless the failure threshold is exceeded the wrapped service mimics the 88 | // old behavior and the difference in behavior is seen after consecutive failures 89 | if err := c(ctx); err != nil { 90 | cnt.Count(FailureState) 91 | return err 92 | } 93 | 94 | cnt.Count(SuccessState) 95 | return nil 96 | } 97 | } 98 | ``` 99 | 100 | ## Related Works 101 | 102 | - [sony/gobreaker](https://github.com/sony/gobreaker) is a well-tested and intuitive circuit breaker implementation for real-world use cases. 103 | -------------------------------------------------------------------------------- /structural/decorator.md: -------------------------------------------------------------------------------- 1 | # Decorator Pattern 2 | Decorator structural pattern allows extending the function of an existing object dynamically without altering its internals. 3 | 4 | Decorators provide a flexible method to extend functionality of objects. 5 | 6 | ## Implementation 7 | `LogDecorate` decorates a function with the signature `func(int) int` that 8 | manipulates integers and adds input/output logging capabilities. 9 | 10 | ```go 11 | type Object func(int) int 12 | 13 | func LogDecorate(fn Object) Object { 14 | return func(n int) int { 15 | log.Println("Starting the execution with the integer", n) 16 | 17 | result := fn(n) 18 | 19 | log.Println("Execution is completed with the result", result) 20 | 21 | return result 22 | } 23 | } 24 | ``` 25 | 26 | ## Usage 27 | ```go 28 | func Double(n int) int { 29 | return n * 2 30 | } 31 | 32 | f := LogDecorate(Double) 33 | 34 | f(5) 35 | // Starting execution with the integer 5 36 | // Execution is completed with the result 10 37 | ``` 38 | 39 | ## Rules of Thumb 40 | - Unlike Adapter pattern, the object to be decorated is obtained by **injection**. 41 | - Decorators should not alter the interface of an object. 42 | -------------------------------------------------------------------------------- /structural/proxy.md: -------------------------------------------------------------------------------- 1 | # Proxy Pattern 2 | 3 | The [proxy pattern](https://en.wikipedia.org/wiki/Proxy_pattern) provides an object that controls access to another object, intercepting all calls. 4 | 5 | ## Implementation 6 | 7 | The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. 8 | 9 | Short idea of implementation: 10 | ```go 11 | // To use proxy and to object they must implement same methods 12 | type IObject interface { 13 | ObjDo(action string) 14 | } 15 | 16 | // Object represents real objects which proxy will delegate data 17 | type Object struct { 18 | action string 19 | } 20 | 21 | // ObjDo implements IObject interface and handel's all logic 22 | func (obj *Object) ObjDo(action string) { 23 | // Action behavior 24 | fmt.Printf("I can, %s", action) 25 | } 26 | 27 | // ProxyObject represents proxy object with intercepts actions 28 | type ProxyObject struct { 29 | object *Object 30 | } 31 | 32 | // ObjDo are implemented IObject and intercept action before send in real Object 33 | func (p *ProxyObject) ObjDo(action string) { 34 | if p.object == nil { 35 | p.object = new(Object) 36 | } 37 | if action == "Run" { 38 | p.object.ObjDo(action) // Prints: I can, Run 39 | } 40 | } 41 | ``` 42 | 43 | ## Usage 44 | More complex usage of proxy as example: User creates "Terminal" authorizes and PROXY send execution command to real Terminal object 45 | See [proxy/main.go](proxy/main.go) or [view in the Playground](https://play.golang.org/p/mnjKCMaOVE). 46 | -------------------------------------------------------------------------------- /structural/proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // For example: 8 | // we must a execute some command 9 | // so before that we must to create new terminal session 10 | // and provide our user name and command 11 | func main() { 12 | // Create new instance of Proxy terminal 13 | t, err := NewTerminal("gopher") 14 | if err != nil { 15 | // panic: User cant be empty 16 | // Or 17 | // panic: You (badUser) are not allowed to use terminal and execute commands 18 | panic(err.Error()) 19 | } 20 | 21 | // Execute user command 22 | excResp, excErr := t.Execute("say_hi") // Proxy prints to STDOUT -> PROXY: Intercepted execution of user (gopher), asked command (say_hi) 23 | if excErr != nil { 24 | fmt.Printf("ERROR: %s\n", excErr.Error()) // Prints: ERROR: I know only how to execute commands: say_hi, man 25 | } 26 | 27 | // Show execution response 28 | fmt.Println(excResp) // Prints: gopher@go_term$: Hi gopher 29 | } 30 | 31 | /* 32 | From that it's can be different terminals realizations with different methods, propertys, yda yda... 33 | */ 34 | 35 | // ITerminal is interface, it's a public method whose implemented in Terminal(Proxy) and Gopher Terminal 36 | type ITerminal interface { 37 | Execute(cmd string) (resp string, err error) 38 | } 39 | 40 | // GopherTerminal for example: 41 | // Its a "huge" structure with different public methods 42 | type GopherTerminal struct { 43 | // user is a current authorized user 44 | User string 45 | } 46 | 47 | // Execute just runs known commands for current authorized user 48 | func (gt *GopherTerminal) Execute(cmd string) (resp string, err error) { 49 | // Set "terminal" prefix for output 50 | prefix := fmt.Sprintf("%s@go_term$:", gt.User) 51 | 52 | // Execute some asked commands if we know them 53 | switch cmd { 54 | case "say_hi": 55 | resp = fmt.Sprintf("%s Hi %s", prefix, gt.User) 56 | case "man": 57 | resp = fmt.Sprintf("%s Visit 'https://golang.org/doc/' for Golang documentation", prefix) 58 | default: 59 | err = fmt.Errorf("%s Unknown command", prefix) 60 | } 61 | 62 | return 63 | } 64 | 65 | /* 66 | And now we will create owr proxy to deliver user and commands to specific objects 67 | */ 68 | 69 | // Terminal is a implementation of Proxy, it's validates and sends data to GopherTerminal 70 | // As example before send commands, user must be authorized 71 | type Terminal struct { 72 | currentUser string 73 | gopherTerminal *GopherTerminal 74 | } 75 | 76 | // NewTerminal creates new instance of terminal 77 | func NewTerminal(user string) (t *Terminal, err error) { 78 | // Check user if given correctly 79 | if user == "" { 80 | err = fmt.Errorf("User cant be empty") 81 | return 82 | } 83 | 84 | // Before we execute user commands, we validate current user, if he have rights to do it 85 | if authErr := authorizeUser(user); authErr != nil { 86 | err = fmt.Errorf("You (%s) are not allowed to use terminal and execute commands", user) 87 | return 88 | } 89 | 90 | // Create new instance of terminal and set valid user 91 | t = &Terminal{currentUser: user} 92 | 93 | return 94 | } 95 | 96 | // Execute intercepts execution of command, implements authorizing user, validates it and 97 | // poxing command to real terminal (gopherTerminal) method 98 | func (t *Terminal) Execute(command string) (resp string, err error) { 99 | // If user allowed to execute send commands then, for example we can decide which terminal can be used, remote or local etc.. 100 | // but for example we just creating new instance of terminal, 101 | // set current user and send user command to execution in terminal 102 | t.gopherTerminal = &GopherTerminal{User: t.currentUser} 103 | 104 | // For example our proxy can log or output intercepted execution... etc 105 | fmt.Printf("PROXY: Intercepted execution of user (%s), asked command (%s)\n", t.currentUser, command) 106 | 107 | // Transfer data to original object and execute command 108 | if resp, err = t.gopherTerminal.Execute(command); err != nil { 109 | err = fmt.Errorf("I know only how to execute commands: say_hi, man") 110 | return 111 | } 112 | 113 | return 114 | } 115 | 116 | // authorize validates user right to execute commands 117 | func authorizeUser(user string) (err error) { 118 | // As we use terminal like proxy, then 119 | // we will intercept user name to validate if it's allowed to execute commands 120 | if user != "gopher" { 121 | // Do some logs, notifications etc... 122 | err = fmt.Errorf("User %s in black list", user) 123 | return 124 | } 125 | 126 | return 127 | } 128 | -------------------------------------------------------------------------------- /synchronization/semaphore.md: -------------------------------------------------------------------------------- 1 | # Semaphore Pattern 2 | A semaphore is a synchronization pattern/primitive that imposes mutual exclusion on a limited number of resources. 3 | 4 | ## Implementation 5 | 6 | ```go 7 | package semaphore 8 | 9 | var ( 10 | ErrNoTickets = errors.New("semaphore: could not aquire semaphore") 11 | ErrIllegalRelease = errors.New("semaphore: can't release the semaphore without acquiring it first") 12 | ) 13 | 14 | // Interface contains the behavior of a semaphore that can be acquired and/or released. 15 | type Interface interface { 16 | Acquire() error 17 | Release() error 18 | } 19 | 20 | type implementation struct { 21 | sem chan struct{} 22 | timeout time.Duration 23 | } 24 | 25 | func (s *implementation) Acquire() error { 26 | select { 27 | case s.sem <- struct{}{}: 28 | return nil 29 | case <-time.After(s.timeout): 30 | return ErrNoTickets 31 | } 32 | } 33 | 34 | func (s *implementation) Release() error { 35 | select { 36 | case _ = <-s.sem: 37 | return nil 38 | case <-time.After(s.timeout): 39 | return ErrIllegalRelease 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func New(tickets int, timeout time.Duration) Interface { 46 | return &implementation{ 47 | sem: make(chan struct{}, tickets), 48 | timeout: timeout, 49 | } 50 | } 51 | ``` 52 | 53 | ## Usage 54 | ### Semaphore with Timeouts 55 | 56 | ```go 57 | tickets, timeout := 1, 3*time.Second 58 | s := semaphore.New(tickets, timeout) 59 | 60 | if err := s.Acquire(); err != nil { 61 | panic(err) 62 | } 63 | 64 | // Do important work 65 | 66 | if err := s.Release(); err != nil { 67 | panic(err) 68 | } 69 | ``` 70 | ### Semaphore without Timeouts (Non-Blocking) 71 | 72 | ```go 73 | tickets, timeout := 0, 0 74 | s := semaphore.New(tickets, timeout) 75 | 76 | if err := s.Acquire(); err != nil { 77 | if err != semaphore.ErrNoTickets { 78 | panic(err) 79 | } 80 | 81 | // No tickets left, can't work :( 82 | os.Exit(1) 83 | } 84 | ``` 85 | --------------------------------------------------------------------------------