1
0
mirror of https://github.com/tmrts/go-patterns.git synced 2024-11-25 22:46:05 +03:00
This commit is contained in:
lesheng 2020-08-18 09:22:50 +08:00
parent f978e42036
commit ecf1b0fb1d
30 changed files with 1855 additions and 1837 deletions

66
.gitignore vendored
View File

@ -1,34 +1,34 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects) # Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o *.o
*.a *.a
*.so *.so
# Folders # Folders
_obj _obj
_test _test
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]
[568vq].out [568vq].out
*.cgo1.go *.cgo1.go
*.cgo2.c *.cgo2.c
_cgo_defun.c _cgo_defun.c
_cgo_gotypes.go _cgo_gotypes.go
_cgo_export.* _cgo_export.*
_testmain.go _testmain.go
*.prof *.prof
# Test binary, build with `go test -c` # Test binary, build with `go test -c`
*.test *.test
# Binaries for programs and plugins # Binaries for programs and plugins
*.exe *.exe
*.dll *.dll
*.dylib *.dylib
# JetBrains project files # JetBrains project files
.idea/ .idea/
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out

View File

@ -1,25 +1,25 @@
language: node_js language: node_js
env: env:
global: global:
- GH_REPO="github.com/tmrts/go-patterns" - GH_REPO="github.com/tmrts/go-patterns"
- 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= - 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=
install: install:
- npm install gitbook-cli - npm install gitbook-cli
- gitbook install - gitbook install
script: script:
- gitbook build . out - gitbook build . out
after_success: after_success:
- echo -e "Deploying updates to GitHub..." - echo -e "Deploying updates to GitHub..."
- MSG=$(git log -1 --oneline) - MSG=$(git log -1 --oneline)
- cd out - cd out
- git config --global user.email "contact@tmrts.com" - git config --global user.email "contact@tmrts.com"
- git config --global user.name "Tamer Tas" - git config --global user.name "Tamer Tas"
- git init - git init
- git checkout -b gh-pages - git checkout -b gh-pages
- git add -A :/ - git add -A :/
- git commit -m "Travis CI | ${MSG}" - git commit -m "Travis CI | ${MSG}"
- git push "https://${GH_TOKEN}@${GH_REPO}" gh-pages -f - git push "https://${GH_TOKEN}@${GH_REPO}" gh-pages -f

View File

@ -1,31 +1,31 @@
# Contribution Guidelines # Contribution Guidelines
Please ensure your pull request adheres to the following guidelines: Please ensure your pull request adheres to the following guidelines:
- Make an individual pull request for each suggestion. - Make an individual pull request for each suggestion.
- Choose the corresponding patterns section for your suggestion. - Choose the corresponding patterns section for your suggestion.
- List, after your addition, should be in lexicographical order. - List, after your addition, should be in lexicographical order.
## Commit Messages Guidelines ## Commit Messages Guidelines
- The message should be in imperative form and uncapitalized. - The message should be in imperative form and uncapitalized.
- If possible, please include an explanation in the commit message body - If possible, please include an explanation in the commit message body
- Use the form `<pattern-section>/<pattern-name>: <message>` (e.g. `creational/singleton: refactor singleton constructor`) - Use the form `<pattern-section>/<pattern-name>: <message>` (e.g. `creational/singleton: refactor singleton constructor`)
## Pattern Template ## Pattern Template
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. 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.
Please use the following template for adding new patterns: Please use the following template for adding new patterns:
```markdown ```markdown
# <Pattern-Name> # <Pattern-Name>
<Pattern description> <Pattern description>
## Implementation ## Implementation
## Usage ## Usage
// Optional // Optional
## Rules of Thumb ## Rules of Thumb
``` ```

352
LICENSE
View File

@ -1,176 +1,176 @@
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions. 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, "License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document. and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by "Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all "Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition, control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the "control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity. outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity "You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License. exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, "Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation including but not limited to software source code, documentation
source, and configuration files. source, and configuration files.
"Object" form shall mean any form resulting from mechanical "Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation, not limited to compiled object code, generated documentation,
and conversions to other media types. and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or "Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work copyright notice that is included in or attached to the work
(an example is provided in the Appendix below). (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object "Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of, separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof. the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including "Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted" the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution." designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity "Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work. subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of 2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual, this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of, copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form. Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of 3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual, this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made, (except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work, use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s) Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate granted to You under this License for that Work shall terminate
as of the date such litigation is filed. as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the 4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You modifications, and in Source or Object form, provided that You
meet the following conditions: meet the following conditions:
(a) You must give any other recipients of the Work or (a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices (b) You must cause any modified files to carry prominent notices
stating that You changed the files; and stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works (c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work, attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of excluding those notices that do not pertain to any part of
the Derivative Works; and the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its (d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or, documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed that such additional attribution notices cannot be construed
as modifying the License. as modifying the License.
You may add Your own copyright statement to Your modifications and You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use, for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License. the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, 5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions. this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions. with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade 6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor, names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file. origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or 7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS, Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License. risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, 8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise, whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill, Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages. has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing 9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer, the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity, and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify, of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability. of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS

220
README.md
View File

@ -1,110 +1,110 @@
<p align="center"> <p align="center">
<img src="/gopher.png" height="400"> <img src="/gopher.png" height="400">
<h1 align="center"> <h1 align="center">
Go Patterns Go Patterns
<br> <br>
<a href="http://travis-ci.org/tmrts/go-patterns"><img alt="build-status" src="https://img.shields.io/badge/build-passing-brightgreen.svg?style=flat-square" /></a> <a href="http://travis-ci.org/tmrts/go-patterns"><img alt="build-status" src="https://img.shields.io/badge/build-passing-brightgreen.svg?style=flat-square" /></a>
<a href="https://github.com/sindresorhus/awesome" ><img alt="awesome" src="https://img.shields.io/badge/awesome-%E2%9C%93-ff69b4.svg?style=flat-square" /></a> <a href="https://github.com/sindresorhus/awesome" ><img alt="awesome" src="https://img.shields.io/badge/awesome-%E2%9C%93-ff69b4.svg?style=flat-square" /></a>
<a href="https://github.com/tmrts/go-patterns/blob/master/LICENSE" ><img alt="license" src="https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square" /></a> <a href="https://github.com/tmrts/go-patterns/blob/master/LICENSE" ><img alt="license" src="https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square" /></a>
</h1> </h1>
</p> </p>
A curated collection of idiomatic design & application patterns for Go language. A curated collection of idiomatic design & application patterns for Go language.
## Creational Patterns ## Creational Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Abstract Factory](/creational/abstract_factory.md) | Provides an interface for creating families of releated objects | ✘ | | [Abstract Factory](/creational/abstract_factory.md) | Provides an interface for creating families of releated objects | ✘ |
| [Builder](/creational/builder.md) | Builds a complex object using simple objects | ✔ | | [Builder](/creational/builder.md) | Builds a complex object using simple objects | ✔ |
| [Factory Method](/creational/factory.md) | Defers instantiation of an object to a specialized function for creating instances | ✔ | | [Factory Method](/creational/factory.md) | Defers instantiation of an object to a specialized function for creating instances | ✔ |
| [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | ✔ | | [Object Pool](/creational/object-pool.md) | Instantiates and maintains a group of objects instances of the same type | ✔ |
| [Singleton](/creational/singleton.md) | Restricts instantiation of a type to one object | ✔ | | [Singleton](/creational/singleton.md) | Restricts instantiation of a type to one object | ✔ |
## Structural Patterns ## Structural Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Bridge](/structural/bridge.md) | Decouples an interface from its implementation so that the two can vary independently | ✘ | | [Bridge](/structural/bridge.md) | Decouples an interface from its implementation so that the two can vary independently | ✘ |
| [Composite](/structural/composite.md) | Encapsulates and provides access to a number of different objects | ✘ | | [Composite](/structural/composite.md) | Encapsulates and provides access to a number of different objects | ✘ |
| [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✔ | | [Decorator](/structural/decorator.md) | Adds behavior to an object, statically or dynamically | ✔ |
| [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ | | [Facade](/structural/facade.md) | Uses one type as an API to a number of others | ✘ |
| [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ | | [Flyweight](/structural/flyweight.md) | Reuses existing instances of objects with similar/identical state to minimize resource usage | ✘ |
| [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✔ | | [Proxy](/structural/proxy.md) | Provides a surrogate for an object to control it's actions | ✔ |
## Behavioral Patterns ## Behavioral Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [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 | ✘ | | [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 | ✘ |
| [Command](/behavioral/command.md) | Bundles a command and arguments to call later | ✘ | | [Command](/behavioral/command.md) | Bundles a command and arguments to call later | ✘ |
| [Mediator](/behavioral/mediator.md) | Connects objects and acts as a proxy | ✘ | | [Mediator](/behavioral/mediator.md) | Connects objects and acts as a proxy | ✘ |
| [Memento](/behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state | ✘ | | [Memento](/behavioral/memento.md) | Generate an opaque token that can be used to go back to a previous state | ✘ |
| [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ | | [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ |
| [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ | | [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ |
| [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | ✘ | | [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | ✘ |
| [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ | | [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ |
| [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | ✘ | | [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | ✘ |
| [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ | | [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ |
## Synchronization Patterns ## Synchronization Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | ✘ | | [Condition Variable](/synchronization/condition_variable.md) | Provides a mechanism for threads to temporarily give up access in order to wait for some condition | ✘ |
| [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | ✘ | | [Lock/Mutex](/synchronization/mutex.md) | Enforces mutual exclusion limit on a resource to gain exclusive access | ✘ |
| [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | ✘ | | [Monitor](/synchronization/monitor.md) | Combination of mutex and condition variable patterns | ✘ |
| [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | ✘ | | [Read-Write Lock](/synchronization/read_write_lock.md) | Allows parallel read access, but only exclusive access on write operations to a resource | ✘ |
| [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | ✔ | | [Semaphore](/synchronization/semaphore.md) | Allows controlling access to a common resource | ✔ |
## Concurrency Patterns ## Concurrency Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ | | [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ |
| [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ | | [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ |
| [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ | | [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ |
| [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ | | [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ |
| [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | ✔ | | [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | ✔ |
| [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | ✘ | | [Reactor](/concurrency/reactor.md) | Demultiplexes service requests delivered concurrently to a service handler and dispatches them syncronously to the associated request handlers | ✘ |
| [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | ✔ | | [Parallelism](/concurrency/parallelism.md) | Completes large number of independent tasks | ✔ |
| [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | ✘ | | [Producer Consumer](/concurrency/producer_consumer.md) | Separates tasks from task executions | ✘ |
## Messaging Patterns ## Messaging Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | ✔ | | [Fan-In](/messaging/fan_in.md) | Funnels tasks to a work sink (e.g. server) | ✔ |
| [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | ✔ | | [Fan-Out](/messaging/fan_out.md) | Distributes tasks among workers (e.g. producer) | ✔ |
| [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | ✘ | | [Futures & Promises](/messaging/futures_promises.md) | Acts as a place-holder of a result that is initially unknown for synchronization purposes | ✘ |
| [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | ✔ | | [Publish/Subscribe](/messaging/publish_subscribe.md) | Passes information to a collection of recipients who subscribed to a topic | ✔ |
| [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | ✘ | | [Push & Pull](/messaging/push_pull.md) | Distributes messages to multiple workers, arranged in a pipeline | ✘ |
## Stability Patterns ## Stability Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | ✘ | | [Bulkheads](/stability/bulkhead.md) | Enforces a principle of failure containment (i.e. prevents cascading failures) | ✘ |
| [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | ✔ | | [Circuit-Breaker](/stability/circuit-breaker.md) | Stops the flow of the requests when requests are likely to fail | ✔ |
| [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) | ✘ | | [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) | ✘ |
| [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 | ✘ | | [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 | ✘ |
| [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | ✘ | | [Handshaking](/stability/handshaking.md) | Asks a component if it can take any more load, if it can't, the request is declined | ✘ |
| [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | ✘ | | [Steady-State](/stability/steady_state.md) | For every service that accumulates a resource, some other service must recycle that resource | ✘ |
## Profiling Patterns ## Profiling Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | ✔ | | [Timing Functions](/profiling/timing.md) | Wraps a function and logs the execution | ✔ |
## Idioms ## Idioms
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | ✔ | | [Functional Options](/idiom/functional-options.md) | Allows creating clean APIs with sane defaults and idiomatic overrides | ✔ |
## Anti-Patterns ## Anti-Patterns
| Pattern | Description | Status | | Pattern | Description | Status |
|:-------:|:----------- |:------:| |:-------:|:----------- |:------:|
| [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 | ✘ | | [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 | ✘ |

View File

@ -1,62 +1,62 @@
# Summary # Summary
* [Go Patterns](/README.md) * [Go Patterns](/README.md)
* [Creational Patterns](/README.md#creational-patterns) * [Creational Patterns](/README.md#creational-patterns)
* [Abstract Factory](/creational/abstract_factory.md) * [Abstract Factory](/creational/abstract_factory.md)
* [Builder](/creational/builder.md) * [Builder](/creational/builder.md)
* [Factory Method](/creational/factory.md) * [Factory Method](/creational/factory.md)
* [Object Pool](/creational/object-pool.md) * [Object Pool](/creational/object-pool.md)
* [Singleton](/creational/singleton.md) * [Singleton](/creational/singleton.md)
* [Structural Patterns](/README.md#structural-patterns) * [Structural Patterns](/README.md#structural-patterns)
* [Bridge](/structural/bridge.md) * [Bridge](/structural/bridge.md)
* [Composite](/structural/composite.md) * [Composite](/structural/composite.md)
* [Decorator](/structural/decorator.md) * [Decorator](/structural/decorator.md)
* [Facade](/structural/facade.md) * [Facade](/structural/facade.md)
* [Flyweight](/structural/flyweight.md) * [Flyweight](/structural/flyweight.md)
* [Proxy](/structural/proxy.md) * [Proxy](/structural/proxy.md)
* [Behavioral Patterns](/README.md#behavioral-patterns) * [Behavioral Patterns](/README.md#behavioral-patterns)
* [Chain of Responsibility](/behavioral/chain_of_responsibility.md) * [Chain of Responsibility](/behavioral/chain_of_responsibility.md)
* [Command](/behavioral/command.md) * [Command](/behavioral/command.md)
* [Mediator](/behavioral/mediator.md) * [Mediator](/behavioral/mediator.md)
* [Memento](/behavioral/memento.md) * [Memento](/behavioral/memento.md)
* [Observer](/behavioral/observer.md) * [Observer](/behavioral/observer.md)
* [Registry](/behavioral/registry.md) * [Registry](/behavioral/registry.md)
* [State](/behavioral/state.md) * [State](/behavioral/state.md)
* [Strategy](/behavioral/strategy.md) * [Strategy](/behavioral/strategy.md)
* [Template](/behavioral/template.md) * [Template](/behavioral/template.md)
* [Visitor](/behavioral/visitor.md) * [Visitor](/behavioral/visitor.md)
* [Synchronization Patterns](/README.md#synchronization-patterns) * [Synchronization Patterns](/README.md#synchronization-patterns)
* [Condition Variable](/synchronization/condition_variable.md) * [Condition Variable](/synchronization/condition_variable.md)
* [Lock/Mutex](/synchronization/mutex.md) * [Lock/Mutex](/synchronization/mutex.md)
* [Monitor](/synchronization/monitor.md) * [Monitor](/synchronization/monitor.md)
* [Read-Write Lock](/synchronization/read_write_lock.md) * [Read-Write Lock](/synchronization/read_write_lock.md)
* [Semaphore](/synchronization/semaphore.md) * [Semaphore](/synchronization/semaphore.md)
* [Concurrency Patterns](/README.md#concurrency-patterns) * [Concurrency Patterns](/README.md#concurrency-patterns)
* [N-Barrier](/concurrency/barrier.md) * [N-Barrier](/concurrency/barrier.md)
* [Bounded Parallelism](/concurrency/bounded_parallelism.md) * [Bounded Parallelism](/concurrency/bounded_parallelism.md)
* [Broadcast](/concurrency/broadcast.md) * [Broadcast](/concurrency/broadcast.md)
* [Coroutines](/concurrency/coroutine.md) * [Coroutines](/concurrency/coroutine.md)
* [Generators](/concurrency/generator.md) * [Generators](/concurrency/generator.md)
* [Reactor](/concurrency/reactor.md) * [Reactor](/concurrency/reactor.md)
* [Parallelism](/concurrency/parallelism.md) * [Parallelism](/concurrency/parallelism.md)
* [Producer Consumer](/concurrency/producer_consumer.md) * [Producer Consumer](/concurrency/producer_consumer.md)
* [Messaging Patterns](/README.md#messaging-patterns) * [Messaging Patterns](/README.md#messaging-patterns)
* [Fan-In](/messaging/fan_in.md) * [Fan-In](/messaging/fan_in.md)
* [Fan-Out](/messaging/fan_out.md) * [Fan-Out](/messaging/fan_out.md)
* [Futures & Promises](/messaging/futures_promises.md) * [Futures & Promises](/messaging/futures_promises.md)
* [Publish/Subscribe](/messaging/publish_subscribe.md) * [Publish/Subscribe](/messaging/publish_subscribe.md)
* [Push & Pull](/messaging/push_pull.md) * [Push & Pull](/messaging/push_pull.md)
* [Stability Patterns](/README.md#stability-patterns) * [Stability Patterns](/README.md#stability-patterns)
* [Bulkheads](/stability/bulkhead.md) * [Bulkheads](/stability/bulkhead.md)
* [Circuit-Breaker](/stability/circuit-breaker.md) * [Circuit-Breaker](/stability/circuit-breaker.md)
* [Deadline](/stability/deadline.md) * [Deadline](/stability/deadline.md)
* [Fail-Fast](/stability/fail_fast.md) * [Fail-Fast](/stability/fail_fast.md)
* [Handshaking](/stability/handshaking.md) * [Handshaking](/stability/handshaking.md)
* [Steady-State](/stability/steady_state.md) * [Steady-State](/stability/steady_state.md)
* [Profiling Patterns](/README.md#profiling-patterns) * [Profiling Patterns](/README.md#profiling-patterns)
* [Timing Functions](/profiling/timing.md) * [Timing Functions](/profiling/timing.md)
* [Idioms](/README.md#idioms) * [Idioms](/README.md#idioms)
* [Functional Options](/idiom/functional-options.md) * [Functional Options](/idiom/functional-options.md)
* [Anti-Patterns](/README.md#anti-patterns) * [Anti-Patterns](/README.md#anti-patterns)
* [Cascading Failures](/anti-patterns/cascading_failures.md) * [Cascading Failures](/anti-patterns/cascading_failures.md)
* [Contributing](/CONTRIBUTING.md) * [Contributing](/CONTRIBUTING.md)

View File

@ -1,47 +1,47 @@
# Observer Pattern # Observer Pattern
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. 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.
## Implementation ## Implementation
In long-running applications&mdash;such as webservers&mdash;instances can keep a collection of observers that will receive notification of triggered events. In long-running applications&mdash;such as webservers&mdash;instances can keep a collection of observers that will receive notification of triggered events.
Implementations vary, but interfaces can be used to make standard observers and notifiers: Implementations vary, but interfaces can be used to make standard observers and notifiers:
```go ```go
type ( type (
// Event defines an indication of a point-in-time occurrence. // Event defines an indication of a point-in-time occurrence.
Event struct { Event struct {
// Data in this case is a simple int, but the actual // Data in this case is a simple int, but the actual
// implementation would depend on the application. // implementation would depend on the application.
Data int64 Data int64
} }
// Observer defines a standard interface for instances that wish to list for // Observer defines a standard interface for instances that wish to list for
// the occurrence of a specific event. // the occurrence of a specific event.
Observer interface { Observer interface {
// OnNotify allows an event to be "published" to interface implementations. // OnNotify allows an event to be "published" to interface implementations.
// In the "real world", error handling would likely be implemented. // In the "real world", error handling would likely be implemented.
OnNotify(Event) OnNotify(Event)
} }
// Notifier is the instance being observed. Publisher is perhaps another decent // Notifier is the instance being observed. Publisher is perhaps another decent
// name, but naming things is hard. // name, but naming things is hard.
Notifier interface { Notifier interface {
// Register allows an instance to register itself to listen/observe // Register allows an instance to register itself to listen/observe
// events. // events.
Register(Observer) Register(Observer)
// Deregister allows an instance to remove itself from the collection // Deregister allows an instance to remove itself from the collection
// of observers/listeners. // of observers/listeners.
Deregister(Observer) Deregister(Observer)
// Notify publishes new events to listeners. The method is not // Notify publishes new events to listeners. The method is not
// absolutely necessary, as each implementation could define this itself // absolutely necessary, as each implementation could define this itself
// without losing functionality. // without losing functionality.
Notify(Event) Notify(Event)
} }
) )
``` ```
## Usage ## Usage
For usage, see [observer/main.go](observer/main.go) or [view in the Playground](https://play.golang.org/p/cr8jEmDmw0). For usage, see [observer/main.go](observer/main.go) or [view in the Playground](https://play.golang.org/p/cr8jEmDmw0).

View File

@ -1,93 +1,93 @@
// Package main serves as an example application that makes use of the observer pattern. // Package main serves as an example application that makes use of the observer pattern.
// Playground: https://play.golang.org/p/cr8jEmDmw0 // Playground: https://play.golang.org/p/cr8jEmDmw0
package main package main
import ( import (
"fmt" "fmt"
"time" "time"
) )
type ( type (
// Event defines an indication of a point-in-time occurrence. // Event defines an indication of a point-in-time occurrence.
Event struct { Event struct {
// Data in this case is a simple int, but the actual // Data in this case is a simple int, but the actual
// implementation would depend on the application. // implementation would depend on the application.
Data int64 Data int64
} }
// Observer defines a standard interface for instances that wish to list for // Observer defines a standard interface for instances that wish to list for
// the occurrence of a specific event. // the occurrence of a specific event.
Observer interface { Observer interface {
// OnNotify allows an event to be "published" to interface implementations. // OnNotify allows an event to be "published" to interface implementations.
// In the "real world", error handling would likely be implemented. // In the "real world", error handling would likely be implemented.
OnNotify(Event) OnNotify(Event)
} }
// Notifier is the instance being observed. Publisher is perhaps another decent // Notifier is the instance being observed. Publisher is perhaps another decent
// name, but naming things is hard. // name, but naming things is hard.
Notifier interface { Notifier interface {
// Register allows an instance to register itself to listen/observe // Register allows an instance to register itself to listen/observe
// events. // events.
Register(Observer) Register(Observer)
// Deregister allows an instance to remove itself from the collection // Deregister allows an instance to remove itself from the collection
// of observers/listeners. // of observers/listeners.
Deregister(Observer) Deregister(Observer)
// Notify publishes new events to listeners. The method is not // Notify publishes new events to listeners. The method is not
// absolutely necessary, as each implementation could define this itself // absolutely necessary, as each implementation could define this itself
// without losing functionality. // without losing functionality.
Notify(Event) Notify(Event)
} }
) )
type ( type (
eventObserver struct{ eventObserver struct{
id int id int
} }
eventNotifier struct{ eventNotifier struct{
// Using a map with an empty struct allows us to keep the observers // Using a map with an empty struct allows us to keep the observers
// unique while still keeping memory usage relatively low. // unique while still keeping memory usage relatively low.
observers map[Observer]struct{} observers map[Observer]struct{}
} }
) )
func (o *eventObserver) OnNotify(e Event) { func (o *eventObserver) OnNotify(e Event) {
fmt.Printf("*** Observer %d received: %d\n", o.id, e.Data) fmt.Printf("*** Observer %d received: %d\n", o.id, e.Data)
} }
func (o *eventNotifier) Register(l Observer) { func (o *eventNotifier) Register(l Observer) {
o.observers[l] = struct{}{} o.observers[l] = struct{}{}
} }
func (o *eventNotifier) Deregister(l Observer) { func (o *eventNotifier) Deregister(l Observer) {
delete(o.observers, l) delete(o.observers, l)
} }
func (p *eventNotifier) Notify(e Event) { func (p *eventNotifier) Notify(e Event) {
for o := range p.observers { for o := range p.observers {
o.OnNotify(e) o.OnNotify(e)
} }
} }
func main() { func main() {
// Initialize a new Notifier. // Initialize a new Notifier.
n := eventNotifier{ n := eventNotifier{
observers: map[Observer]struct{}{}, observers: map[Observer]struct{}{},
} }
// Register a couple of observers. // Register a couple of observers.
n.Register(&eventObserver{id: 1}) n.Register(&eventObserver{id: 1})
n.Register(&eventObserver{id: 2}) n.Register(&eventObserver{id: 2})
// A simple loop publishing the current Unix timestamp to observers. // A simple loop publishing the current Unix timestamp to observers.
stop := time.NewTimer(10 * time.Second).C stop := time.NewTimer(10 * time.Second).C
tick := time.NewTicker(time.Second).C tick := time.NewTicker(time.Second).C
for { for {
select { select {
case <- stop: case <- stop:
return return
case t := <-tick: case t := <-tick:
n.Notify(Event{Data: t.UnixNano()}) n.Notify(Event{Data: t.UnixNano()})
} }
} }
} }

View File

@ -1,55 +1,55 @@
# Strategy Pattern # Strategy Pattern
Strategy behavioral design pattern enables an algorithm's behavior to be selected at runtime. Strategy behavioral design pattern enables an algorithm's behavior to be selected at runtime.
It defines algorithms, encapsulates them, and uses them interchangeably. It defines algorithms, encapsulates them, and uses them interchangeably.
## Implementation ## Implementation
Implementation of an interchangeable operator object that operates on integers. Implementation of an interchangeable operator object that operates on integers.
```go ```go
type Operator interface { type Operator interface {
Apply(int, int) int Apply(int, int) int
} }
type Operation struct { type Operation struct {
Operator Operator Operator Operator
} }
func (o *Operation) Operate(leftValue, rightValue int) int { func (o *Operation) Operate(leftValue, rightValue int) int {
return o.Operator.Apply(leftValue, rightValue) return o.Operator.Apply(leftValue, rightValue)
} }
``` ```
## Usage ## Usage
### Addition Operator ### Addition Operator
```go ```go
type Addition struct{} type Addition struct{}
func (Addition) Apply(lval, rval int) int { func (Addition) Apply(lval, rval int) int {
return lval + rval return lval + rval
} }
``` ```
```go ```go
add := Operation{Addition{}} add := Operation{Addition{}}
add.Operate(3, 5) // 8 add.Operate(3, 5) // 8
``` ```
### Multiplication Operator ### Multiplication Operator
```go ```go
type Multiplication struct{} type Multiplication struct{}
func (Multiplication) Apply(lval, rval int) int { func (Multiplication) Apply(lval, rval int) int {
return lval * rval return lval * rval
} }
``` ```
```go ```go
mult := Operation{Multiplication{}} mult := Operation{Multiplication{}}
mult.Operate(3, 5) // 15 mult.Operate(3, 5) // 15
``` ```
## Rules of Thumb ## Rules of Thumb
- Strategy pattern is similar to Template pattern except in its granularity. - Strategy pattern is similar to Template pattern except in its granularity.
- Strategy pattern lets you change the guts of an object. Decorator pattern lets you change the skin. - Strategy pattern lets you change the guts of an object. Decorator pattern lets you change the skin.

View File

@ -1,17 +1,17 @@
{ {
"plugins": [ "plugins": [
"-search", "-search",
"-lunr", "-lunr",
"github", "github",
"edit-link" "edit-link"
], ],
"pluginsConfig": { "pluginsConfig": {
"github": { "github": {
"url": "https://github.com/tmrts/go-patterns" "url": "https://github.com/tmrts/go-patterns"
}, },
"edit-link": { "edit-link": {
"base": "https://github.com/tmrts/go-patterns/edit/master/", "base": "https://github.com/tmrts/go-patterns/edit/master/",
"label": "" "label": ""
} }
} }
} }

View File

@ -1,121 +1,121 @@
package bounded_parallelism package bounded_parallelism
import ( import (
"crypto/md5" "crypto/md5"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"sync" "sync"
) )
// walkFiles starts a goroutine to walk the directory tree at root and send the // walkFiles starts a goroutine to walk the directory tree at root and send the
// path of each regular file on the string channel. It sends the result of the // path of each regular file on the string channel. It sends the result of the
// walk on the error channel. If done is closed, walkFiles abandons its work. // walk on the error channel. If done is closed, walkFiles abandons its work.
func walkFiles(done <-chan struct{}, root string) (<-chan string, <-chan error) { func walkFiles(done <-chan struct{}, root string) (<-chan string, <-chan error) {
paths := make(chan string) paths := make(chan string)
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { // HL go func() { // HL
// Close the paths channel after Walk returns. // Close the paths channel after Walk returns.
defer close(paths) // HL defer close(paths) // HL
// No select needed for this send, since errc is buffered. // No select needed for this send, since errc is buffered.
errc <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // HL errc <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // HL
if err != nil { if err != nil {
return err return err
} }
if !info.Mode().IsRegular() { if !info.Mode().IsRegular() {
return nil return nil
} }
select { select {
case paths <- path: // HL case paths <- path: // HL
case <-done: // HL case <-done: // HL
return errors.New("walk canceled") return errors.New("walk canceled")
} }
return nil return nil
}) })
}() }()
return paths, errc return paths, errc
} }
// A result is the product of reading and summing a file using MD5. // A result is the product of reading and summing a file using MD5.
type result struct { type result struct {
path string path string
sum [md5.Size]byte sum [md5.Size]byte
err error err error
} }
// digester reads path names from paths and sends digests of the corresponding // digester reads path names from paths and sends digests of the corresponding
// files on c until either paths or done is closed. // files on c until either paths or done is closed.
func digester(done <-chan struct{}, paths <-chan string, c chan<- result) { func digester(done <-chan struct{}, paths <-chan string, c chan<- result) {
for path := range paths { // HLpaths for path := range paths { // HLpaths
data, err := ioutil.ReadFile(path) data, err := ioutil.ReadFile(path)
select { select {
case c <- result{path, md5.Sum(data), err}: case c <- result{path, md5.Sum(data), err}:
case <-done: case <-done:
return return
} }
} }
} }
// MD5All reads all the files in the file tree rooted at root and returns a map // MD5All reads all the files in the file tree rooted at root and returns a map
// from file path to the MD5 sum of the file's contents. If the directory walk // from file path to the MD5 sum of the file's contents. If the directory walk
// fails or any read operation fails, MD5All returns an error. In that case, // fails or any read operation fails, MD5All returns an error. In that case,
// MD5All does not wait for inflight read operations to complete. // MD5All does not wait for inflight read operations to complete.
func MD5All(root string) (map[string][md5.Size]byte, error) { func MD5All(root string) (map[string][md5.Size]byte, error) {
// MD5All closes the done channel when it returns; it may do so before // MD5All closes the done channel when it returns; it may do so before
// receiving all the values from c and errc. // receiving all the values from c and errc.
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
paths, errc := walkFiles(done, root) paths, errc := walkFiles(done, root)
// Start a fixed number of goroutines to read and digest files. // Start a fixed number of goroutines to read and digest files.
c := make(chan result) // HLc c := make(chan result) // HLc
var wg sync.WaitGroup var wg sync.WaitGroup
const numDigesters = 20 const numDigesters = 20
wg.Add(numDigesters) wg.Add(numDigesters)
for i := 0; i < numDigesters; i++ { for i := 0; i < numDigesters; i++ {
go func() { go func() {
digester(done, paths, c) // HLc digester(done, paths, c) // HLc
wg.Done() wg.Done()
}() }()
} }
go func() { go func() {
wg.Wait() wg.Wait()
close(c) // HLc close(c) // HLc
}() }()
// End of pipeline. OMIT // End of pipeline. OMIT
m := make(map[string][md5.Size]byte) m := make(map[string][md5.Size]byte)
for r := range c { for r := range c {
if r.err != nil { if r.err != nil {
return nil, r.err return nil, r.err
} }
m[r.path] = r.sum m[r.path] = r.sum
} }
// Check whether the Walk failed. // Check whether the Walk failed.
if err := <-errc; err != nil { // HLerrc if err := <-errc; err != nil { // HLerrc
return nil, err return nil, err
} }
return m, nil return m, nil
} }
func main() { func main() {
// Calculate the MD5 sum of all files under the specified directory, // Calculate the MD5 sum of all files under the specified directory,
// then print the results sorted by path name. // then print the results sorted by path name.
m, err := MD5All(os.Args[1]) m, err := MD5All(os.Args[1])
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
var paths []string var paths []string
for path := range m { for path := range m {
paths = append(paths, path) paths = append(paths, path)
} }
sort.Strings(paths) sort.Strings(paths)
for _, path := range paths { for _, path := range paths {
fmt.Printf("%x %s\n", m[path], path) fmt.Printf("%x %s\n", m[path], path)
} }
} }

View File

@ -1,7 +1,7 @@
# Bounded Parallelism Pattern # Bounded Parallelism Pattern
[Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](parallelism.md), but allows limits to be placed on allocation. [Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](parallelism.md), but allows limits to be placed on allocation.
# Implementation and Example # Implementation and Example
An example showing implementation and usage can be found in [bounded_parallelism.go](bounded_parallelism.go). An example showing implementation and usage can be found in [bounded_parallelism.go](bounded_parallelism.go).

View File

@ -1,38 +1,38 @@
# Generator Pattern # Generator Pattern
[Generators](https://en.wikipedia.org/wiki/Generator_(computer_programming)) yields a sequence of values one at a time. [Generators](https://en.wikipedia.org/wiki/Generator_(computer_programming)) yields a sequence of values one at a time.
## Implementation ## Implementation
```go ```go
func Count(start int, end int) chan int { func Count(start int, end int) chan int {
ch := make(chan int) ch := make(chan int)
go func(ch chan int) { go func(ch chan int) {
for i := start; i <= end ; i++ { for i := start; i <= end ; i++ {
// Blocks on the operation // Blocks on the operation
ch <- i ch <- i
} }
close(ch) close(ch)
}(ch) }(ch)
return ch return ch
} }
``` ```
## Usage ## Usage
```go ```go
fmt.Println("No bottles of beer on the wall") fmt.Println("No bottles of beer on the wall")
for i := range Count(1, 99) { for i := range Count(1, 99) {
fmt.Println("Pass it around, put one up,", i, "bottles of beer on the wall") fmt.Println("Pass it around, put one up,", i, "bottles of beer on the wall")
// Pass it around, put one up, 1 bottles of beer on the wall // Pass it around, put one up, 1 bottles of beer on the wall
// Pass it around, put one up, 2 bottles of beer on the wall // Pass it around, put one up, 2 bottles of beer on the wall
// ... // ...
// Pass it around, put one up, 99 bottles of beer on the wall // Pass it around, put one up, 99 bottles of beer on the wall
} }
fmt.Println(100, "bottles of beer on the wall") fmt.Println(100, "bottles of beer on the wall")
``` ```

View File

@ -1,109 +1,109 @@
package parallelism package parallelism
import ( import (
"crypto/md5" "crypto/md5"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"sync" "sync"
) )
// A result is the product of reading and summing a file using MD5. // A result is the product of reading and summing a file using MD5.
type result struct { type result struct {
path string path string
sum [md5.Size]byte sum [md5.Size]byte
err error err error
} }
// sumFiles starts goroutines to walk the directory tree at root and digest each // sumFiles starts goroutines to walk the directory tree at root and digest each
// regular file. These goroutines send the results of the digests on the result // regular file. These goroutines send the results of the digests on the result
// channel and send the result of the walk on the error channel. If done is // channel and send the result of the walk on the error channel. If done is
// closed, sumFiles abandons its work. // closed, sumFiles abandons its work.
func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) { func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {
// For each regular file, start a goroutine that sums the file and sends // For each regular file, start a goroutine that sums the file and sends
// the result on c. Send the result of the walk on errc. // the result on c. Send the result of the walk on errc.
c := make(chan result) c := make(chan result)
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { // HL go func() { // HL
var wg sync.WaitGroup var wg sync.WaitGroup
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
if !info.Mode().IsRegular() { if !info.Mode().IsRegular() {
return nil return nil
} }
wg.Add(1) wg.Add(1)
go func() { // HL go func() { // HL
data, err := ioutil.ReadFile(path) data, err := ioutil.ReadFile(path)
select { select {
case c <- result{path, md5.Sum(data), err}: // HL case c <- result{path, md5.Sum(data), err}: // HL
case <-done: // HL case <-done: // HL
} }
wg.Done() wg.Done()
}() }()
// Abort the walk if done is closed. // Abort the walk if done is closed.
select { select {
case <-done: // HL case <-done: // HL
return errors.New("walk canceled") return errors.New("walk canceled")
default: default:
return nil return nil
} }
}) })
// Walk has returned, so all calls to wg.Add are done. Start a // Walk has returned, so all calls to wg.Add are done. Start a
// goroutine to close c once all the sends are done. // goroutine to close c once all the sends are done.
go func() { // HL go func() { // HL
wg.Wait() wg.Wait()
close(c) // HL close(c) // HL
}() }()
// No select needed here, since errc is buffered. // No select needed here, since errc is buffered.
errc <- err // HL errc <- err // HL
}() }()
return c, errc return c, errc
} }
// MD5All reads all the files in the file tree rooted at root and returns a map // MD5All reads all the files in the file tree rooted at root and returns a map
// from file path to the MD5 sum of the file's contents. If the directory walk // from file path to the MD5 sum of the file's contents. If the directory walk
// fails or any read operation fails, MD5All returns an error. In that case, // fails or any read operation fails, MD5All returns an error. In that case,
// MD5All does not wait for inflight read operations to complete. // MD5All does not wait for inflight read operations to complete.
func MD5All(root string) (map[string][md5.Size]byte, error) { func MD5All(root string) (map[string][md5.Size]byte, error) {
// MD5All closes the done channel when it returns; it may do so before // MD5All closes the done channel when it returns; it may do so before
// receiving all the values from c and errc. // receiving all the values from c and errc.
done := make(chan struct{}) // HLdone done := make(chan struct{}) // HLdone
defer close(done) // HLdone defer close(done) // HLdone
c, errc := sumFiles(done, root) // HLdone c, errc := sumFiles(done, root) // HLdone
m := make(map[string][md5.Size]byte) m := make(map[string][md5.Size]byte)
for r := range c { // HLrange for r := range c { // HLrange
if r.err != nil { if r.err != nil {
return nil, r.err return nil, r.err
} }
m[r.path] = r.sum m[r.path] = r.sum
} }
if err := <-errc; err != nil { if err := <-errc; err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
} }
func main() { func main() {
// Calculate the MD5 sum of all files under the specified directory, // Calculate the MD5 sum of all files under the specified directory,
// then print the results sorted by path name. // then print the results sorted by path name.
m, err := MD5All(os.Args[1]) m, err := MD5All(os.Args[1])
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
var paths []string var paths []string
for path := range m { for path := range m {
paths = append(paths, path) paths = append(paths, path)
} }
sort.Strings(paths) sort.Strings(paths)
for _, path := range paths { for _, path := range paths {
fmt.Printf("%x %s\n", m[path], path) fmt.Printf("%x %s\n", m[path], path)
} }
} }

View File

@ -1,7 +1,7 @@
# Parallelism Pattern # Parallelism Pattern
[Parallelism](https://blog.golang.org/pipelines#TOC_8.) allows multiple "jobs" or tasks to be run concurrently and asynchronously. [Parallelism](https://blog.golang.org/pipelines#TOC_8.) allows multiple "jobs" or tasks to be run concurrently and asynchronously.
# Implementation and Example # Implementation and Example
An example showing implementation and usage can be found in [parallelism.go](parallelism.go). An example showing implementation and usage can be found in [parallelism.go](parallelism.go).

View File

@ -1,61 +1,61 @@
# Builder Pattern # Builder Pattern
Builder pattern separates the construction of a complex object from its Builder pattern separates the construction of a complex object from its
representation so that the same construction process can create different representation so that the same construction process can create different
representations. representations.
In Go, normally a configuration struct is used to achieve the same behavior, In Go, normally a configuration struct is used to achieve the same behavior,
however passing a struct to the builder method fills the code with boilerplate however passing a struct to the builder method fills the code with boilerplate
`if cfg.Field != nil {...}` checks. `if cfg.Field != nil {...}` checks.
## Implementation ## Implementation
```go ```go
package car package car
type Speed float64 type Speed float64
const ( const (
MPH Speed = 1 MPH Speed = 1
KPH = 1.60934 KPH = 1.60934
) )
type Color string type Color string
const ( const (
BlueColor Color = "blue" BlueColor Color = "blue"
GreenColor = "green" GreenColor = "green"
RedColor = "red" RedColor = "red"
) )
type Wheels string type Wheels string
const ( const (
SportsWheels Wheels = "sports" SportsWheels Wheels = "sports"
SteelWheels = "steel" SteelWheels = "steel"
) )
type Builder interface { type Builder interface {
Color(Color) Builder Color(Color) Builder
Wheels(Wheels) Builder Wheels(Wheels) Builder
TopSpeed(Speed) Builder TopSpeed(Speed) Builder
Build() Interface Build() Interface
} }
type Interface interface { type Interface interface {
Drive() error Drive() error
Stop() error Stop() error
} }
``` ```
## Usage ## Usage
```go ```go
assembly := car.NewBuilder().Paint(car.RedColor) assembly := car.NewBuilder().Paint(car.RedColor)
familyCar := assembly.Wheels(car.SportsWheels).TopSpeed(50 * car.MPH).Build() familyCar := assembly.Wheels(car.SportsWheels).TopSpeed(50 * car.MPH).Build()
familyCar.Drive() familyCar.Drive()
sportsCar := assembly.Wheels(car.SteelWheels).TopSpeed(150 * car.MPH).Build() sportsCar := assembly.Wheels(car.SteelWheels).TopSpeed(150 * car.MPH).Build()
sportsCar.Drive() sportsCar.Drive()
``` ```

11
creational/factory.go Normal file
View File

@ -0,0 +1,11 @@
package creational
import "io"
// 工厂方法创建设计模式允许创建对象,而不必指定将要创建的对象的确切类型。
// 示例实现展示了如何使用不同的后端,如内存、磁盘存储。
// Store 对象类型
type Store interface {
Open(string) (io.ReadWriteCloser, error)
}

View File

@ -1,58 +1,65 @@
# Factory Method Pattern # Factory Method Pattern
Factory method creational design pattern allows creating objects without having Factory method creational design pattern allows creating objects without having
to specify the exact type of the object that will be created. to specify the exact type of the object that will be created.
工厂方法创建型设计模式允许创建对象,不用指定要创建对象的类型。
## Implementation
The example implementation shows how to provide a data store with different ## Implementation
backends such as in-memory, disk storage.
The example implementation shows how to provide a data store with different
### Types backends such as in-memory, disk storage.
示例实现展示了如何使用不同的后端,如内存、磁盘存储。
```go
package data ### Types(对象类型)
import "io" ```go
package data
type Store interface {
Open(string) (io.ReadWriteCloser, error) import "io"
}
``` type Store interface {
Open(string) (io.ReadWriteCloser, error)
### Different Implementations }
```
```go
package data ### Different Implementations(不同实现)
type StorageType int ```go
package data
const (
DiskStorage StorageType = 1 << iota type StorageType int
TempStorage
MemoryStorage // 类型实现对象枚举
) const (
DiskStorage StorageType = 1 << iota
func NewStore(t StorageType) Store { TempStorage
switch t { MemoryStorage
case MemoryStorage: )
return newMemoryStorage( /*...*/ )
case DiskStorage: // 工厂方法(实例化对象)
return newDiskStorage( /*...*/ ) func NewStore(t StorageType) Store {
default: switch t {
return newTempStorage( /*...*/ ) case MemoryStorage:
} return newMemoryStorage( /*...*/ )
} case DiskStorage:
``` return newDiskStorage( /*...*/ )
default:
## Usage return newTempStorage( /*...*/ )
}
With the factory method, the user can specify the type of storage they want. }
```
```go
s, _ := data.NewStore(data.MemoryStorage) ## Usage
f, _ := s.Open("file")
With the factory method, the user can specify the type of storage they want.
n, _ := f.Write([]byte("data")) 使用工厂方法,指定实现类型枚举。
defer f.Close()
``` ```go
// 给工厂方法提供实现类型枚举
s, _ := data.NewStore(data.MemoryStorage)
f, _ := s.Open("file")
n, _ := f.Write([]byte("data"))
defer f.Close()
```

View File

@ -1,48 +1,48 @@
# Object Pool Pattern # Object Pool Pattern
The object pool creational design pattern is used to prepare and keep multiple The object pool creational design pattern is used to prepare and keep multiple
instances according to the demand expectation. instances according to the demand expectation.
## Implementation ## Implementation
```go ```go
package pool package pool
type Pool chan *Object type Pool chan *Object
func New(total int) *Pool { func New(total int) *Pool {
p := make(Pool, total) p := make(Pool, total)
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
p <- new(Object) p <- new(Object)
} }
return &p return &p
} }
``` ```
## Usage ## Usage
Given below is a simple lifecycle example on an object pool. Given below is a simple lifecycle example on an object pool.
```go ```go
p := pool.New(2) p := pool.New(2)
select { select {
case obj := <-p: case obj := <-p:
obj.Do( /*...*/ ) obj.Do( /*...*/ )
p <- obj p <- obj
default: default:
// No more objects left — retry later or fail // No more objects left — retry later or fail
return return
} }
``` ```
## Rules of Thumb ## Rules of Thumb
- Object pool pattern is useful in cases where object initialization is more - Object pool pattern is useful in cases where object initialization is more
expensive than the object maintenance. expensive than the object maintenance.
- If there are spikes in demand as opposed to a steady demand, the maintenance - If there are spikes in demand as opposed to a steady demand, the maintenance
overhead might overweigh the benefits of an object pool. overhead might overweigh the benefits of an object pool.
- It has positive effects on performance due to objects being initialized beforehand. - It has positive effects on performance due to objects being initialized beforehand.

View File

@ -1,42 +1,42 @@
# Singleton Pattern # Singleton Pattern
Singleton creational design pattern restricts the instantiation of a type to a single object. Singleton creational design pattern restricts the instantiation of a type to a single object.
## Implementation ## Implementation
```go ```go
package singleton package singleton
type singleton map[string]string type singleton map[string]string
var ( var (
once sync.Once once sync.Once
instance singleton instance singleton
) )
func New() singleton { func New() singleton {
once.Do(func() { once.Do(func() {
instance = make(singleton) instance = make(singleton)
}) })
return instance return instance
} }
``` ```
## Usage ## Usage
```go ```go
s := singleton.New() s := singleton.New()
s["this"] = "that" s["this"] = "that"
s2 := singleton.New() s2 := singleton.New()
fmt.Println("This is ", s2["this"]) fmt.Println("This is ", s2["this"])
// This is that // This is that
``` ```
## Rules of Thumb ## Rules of Thumb
- Singleton pattern represents a global state and most of the time reduces testability. - Singleton pattern represents a global state and most of the time reduces testability.

View File

@ -1,94 +1,94 @@
# Functional Options # Functional Options
Functional options are a method of implementing clean/eloquent APIs in Go. Functional options are a method of implementing clean/eloquent APIs in Go.
Options implemented as a function set the state of that option. Options implemented as a function set the state of that option.
## Implementation ## Implementation
### Options ### Options
```go ```go
package file package file
type Options struct { type Options struct {
UID int UID int
GID int GID int
Flags int Flags int
Contents string Contents string
Permissions os.FileMode Permissions os.FileMode
} }
type Option func(*Options) type Option func(*Options)
func UID(userID int) Option { func UID(userID int) Option {
return func(args *Options) { return func(args *Options) {
args.UID = userID args.UID = userID
} }
} }
func GID(groupID int) Option { func GID(groupID int) Option {
return func(args *Options) { return func(args *Options) {
args.GID = groupID args.GID = groupID
} }
} }
func Contents(c string) Option { func Contents(c string) Option {
return func(args *Options) { return func(args *Options) {
args.Contents = c args.Contents = c
} }
} }
func Permissions(perms os.FileMode) Option { func Permissions(perms os.FileMode) Option {
return func(args *Options) { return func(args *Options) {
args.Permissions = perms args.Permissions = perms
} }
} }
``` ```
### Constructor ### Constructor
```go ```go
package file package file
func New(filepath string, setters ...Option) error { func New(filepath string, setters ...Option) error {
// Default Options // Default Options
args := &Options{ args := &Options{
UID: os.Getuid(), UID: os.Getuid(),
GID: os.Getgid(), GID: os.Getgid(),
Contents: "", Contents: "",
Permissions: 0666, Permissions: 0666,
Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY, Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY,
} }
for _, setter := range setters { for _, setter := range setters {
setter(args) setter(args)
} }
f, err := os.OpenFile(filepath, args.Flags, args.Permissions) f, err := os.OpenFile(filepath, args.Flags, args.Permissions)
if err != nil { if err != nil {
return err return err
} else { } else {
defer f.Close() defer f.Close()
} }
if _, err := f.WriteString(args.Contents); err != nil { if _, err := f.WriteString(args.Contents); err != nil {
return err return err
} }
return f.Chown(args.UID, args.GID) return f.Chown(args.UID, args.GID)
} }
``` ```
## Usage ## Usage
```go ```go
emptyFile, err := file.New("/tmp/empty.txt") emptyFile, err := file.New("/tmp/empty.txt")
if err != nil { if err != nil {
panic(err) panic(err)
} }
fillerFile, err := file.New("/tmp/file.txt", file.UID(1000), file.Contents("Lorem Ipsum Dolor Amet")) fillerFile, err := file.New("/tmp/file.txt", file.UID(1000), file.Contents("Lorem Ipsum Dolor Amet"))
if err != nil { if err != nil {
panic(err) panic(err)
} }
``` ```

View File

@ -1,40 +1,40 @@
Fan-In Messaging Patterns Fan-In Messaging Patterns
=================================== ===================================
Fan-In is a messaging pattern used to create a funnel for work amongst workers (clients: source, server: destination). Fan-In is a messaging pattern used to create a funnel for work amongst workers (clients: source, server: destination).
We can model fan-in using the Go channels. We can model fan-in using the Go channels.
```go ```go
// Merge different channels in one channel // Merge different channels in one channel
func Merge(cs ...<-chan int) <-chan int { func Merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup var wg sync.WaitGroup
out := make(chan int) out := make(chan int)
// Start an send goroutine for each input channel in cs. send // Start an send goroutine for each input channel in cs. send
// copies values from c to out until c is closed, then calls wg.Done. // copies values from c to out until c is closed, then calls wg.Done.
send := func(c <-chan int) { send := func(c <-chan int) {
for n := range c { for n := range c {
out <- n out <- n
} }
wg.Done() wg.Done()
} }
wg.Add(len(cs)) wg.Add(len(cs))
for _, c := range cs { for _, c := range cs {
go send(c) go send(c)
} }
// Start a goroutine to close out once all the send goroutines are // Start a goroutine to close out once all the send goroutines are
// done. This must start after the wg.Add call. // done. This must start after the wg.Add call.
go func() { go func() {
wg.Wait() wg.Wait()
close(out) close(out)
}() }()
return out return out
} }
``` ```
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. 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.
Once all the output goroutines have been started, `Merge` a goroutine is started to close the main channel. Once all the output goroutines have been started, `Merge` a goroutine is started to close the main channel.

View File

@ -1,47 +1,47 @@
Fan-Out Messaging Pattern Fan-Out Messaging Pattern
========================= =========================
Fan-Out is a messaging pattern used for distributing work amongst workers (producer: source, consumers: destination). Fan-Out is a messaging pattern used for distributing work amongst workers (producer: source, consumers: destination).
We can model fan-out using the Go channels. We can model fan-out using the Go channels.
```go ```go
// Split a channel into n channels that receive messages in a round-robin fashion. // Split a channel into n channels that receive messages in a round-robin fashion.
func Split(ch <-chan int, n int) []<-chan int { func Split(ch <-chan int, n int) []<-chan int {
cs := make([]chan int) cs := make([]chan int)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
cs = append(cs, make(chan int)) cs = append(cs, make(chan int))
} }
// Distributes the work in a round robin fashion among the stated number // Distributes the work in a round robin fashion among the stated number
// of channels until the main channel has been closed. In that case, close // of channels until the main channel has been closed. In that case, close
// all channels and return. // all channels and return.
distributeToChannels := func(ch <-chan int, cs []chan<- int) { distributeToChannels := func(ch <-chan int, cs []chan<- int) {
// Close every channel when the execution ends. // Close every channel when the execution ends.
defer func(cs []chan<- int) { defer func(cs []chan<- int) {
for _, c := range cs { for _, c := range cs {
close(c) close(c)
} }
}(cs) }(cs)
for { for {
for _, c := range cs { for _, c := range cs {
select { select {
case val, ok := <-ch: case val, ok := <-ch:
if !ok { if !ok {
return return
} }
c <- val c <- val
} }
} }
} }
} }
go distributeToChannels(ch, cs) go distributeToChannels(ch, cs)
return cs return cs
} }
``` ```
The `Split` function converts a single channel into a list of channels by using The `Split` function converts a single channel into a list of channels by using
a goroutine to copy received values to channels in the list in a round-robin fashion. a goroutine to copy received values to channels in the list in a round-robin fashion.

View File

@ -1,80 +1,80 @@
Publish & Subscribe Messaging Pattern Publish & Subscribe Messaging Pattern
============ ============
Publish-Subscribe is a messaging pattern used to communicate messages between Publish-Subscribe is a messaging pattern used to communicate messages between
different components without these components knowing anything about each other's identity. different components without these components knowing anything about each other's identity.
It is similar to the Observer behavioral design pattern. It is similar to the Observer behavioral design pattern.
The fundamental design principals of both Observer and Publish-Subscribe is the decoupling of The fundamental design principals of both Observer and Publish-Subscribe is the decoupling of
those interested in being informed about `Event Messages` from the informer (Observers or Publishers). those interested in being informed about `Event Messages` from the informer (Observers or Publishers).
Meaning that you don't have to program the messages to be sent directly to specific receivers. Meaning that you don't have to program the messages to be sent directly to specific receivers.
To accomplish this, an intermediary, called a "message broker" or "event bus", To accomplish this, an intermediary, called a "message broker" or "event bus",
receives published messages, and then routes them on to subscribers. receives published messages, and then routes them on to subscribers.
There are three components **messages**, **topics**, **users**. There are three components **messages**, **topics**, **users**.
```go ```go
type Message struct { type Message struct {
// Contents // Contents
} }
type Subscription struct { type Subscription struct {
ch chan<- Message ch chan<- Message
Inbox chan Message Inbox chan Message
} }
func (s *Subscription) Publish(msg Message) error { func (s *Subscription) Publish(msg Message) error {
if _, ok := <-s.ch; !ok { if _, ok := <-s.ch; !ok {
return errors.New("Topic has been closed") return errors.New("Topic has been closed")
} }
s.ch <- msg s.ch <- msg
return nil return nil
} }
``` ```
```go ```go
type Topic struct { type Topic struct {
Subscribers []Session Subscribers []Session
MessageHistory []Message MessageHistory []Message
} }
func (t *Topic) Subscribe(uid uint64) (Subscription, error) { func (t *Topic) Subscribe(uid uint64) (Subscription, error) {
// Get session and create one if it's the first // Get session and create one if it's the first
// Add session to the Topic & MessageHistory // Add session to the Topic & MessageHistory
// Create a subscription // Create a subscription
} }
func (t *Topic) Unsubscribe(Subscription) error { func (t *Topic) Unsubscribe(Subscription) error {
// Implementation // Implementation
} }
func (t *Topic) Delete() error { func (t *Topic) Delete() error {
// Implementation // Implementation
} }
``` ```
```go ```go
type User struct { type User struct {
ID uint64 ID uint64
Name string Name string
} }
type Session struct { type Session struct {
User User User User
Timestamp time.Time Timestamp time.Time
} }
``` ```
Improvements Improvements
============ ============
Events can be published in a parallel fashion by utilizing stackless goroutines. Events can be published in a parallel fashion by utilizing stackless goroutines.
Performance can be improved by dealing with straggler subscribers Performance can be improved by dealing with straggler subscribers
by using a buffered inbox and you stop sending events once the inbox is full. by using a buffered inbox and you stop sending events once the inbox is full.

View File

@ -1,40 +1,40 @@
# Timing Functions # Timing Functions
When optimizing code, sometimes a quick and dirty time measurement is required When optimizing code, sometimes a quick and dirty time measurement is required
as opposed to utilizing profiler tools/frameworks to validate assumptions. as opposed to utilizing profiler tools/frameworks to validate assumptions.
Time measurements can be performed by utilizing `time` package and `defer` statements. Time measurements can be performed by utilizing `time` package and `defer` statements.
## Implementation ## Implementation
```go ```go
package profile package profile
import ( import (
"time" "time"
"log" "log"
) )
func Duration(invocation time.Time, name string) { func Duration(invocation time.Time, name string) {
elapsed := time.Since(invocation) elapsed := time.Since(invocation)
log.Printf("%s lasted %s", name, elapsed) log.Printf("%s lasted %s", name, elapsed)
} }
``` ```
## Usage ## Usage
```go ```go
func BigIntFactorial(x big.Int) *big.Int { func BigIntFactorial(x big.Int) *big.Int {
// Arguments to a defer statement is immediately evaluated and stored. // Arguments to a defer statement is immediately evaluated and stored.
// The deferred function receives the pre-evaluated values when its invoked. // The deferred function receives the pre-evaluated values when its invoked.
defer profile.Duration(time.Now(), "IntFactorial") defer profile.Duration(time.Now(), "IntFactorial")
y := big.NewInt(1) y := big.NewInt(1)
for one := big.NewInt(1); x.Sign() > 0; x.Sub(x, one) { for one := big.NewInt(1); x.Sign() > 0; x.Sub(x, one) {
y.Mul(y, x) y.Mul(y, x)
} }
return x.Set(y) return x.Set(y)
} }
``` ```

View File

@ -1,102 +1,102 @@
# Circuit Breaker Pattern # Circuit Breaker Pattern
Similar to electrical fuses that prevent fires when a circuit that is connected Similar to electrical fuses that prevent fires when a circuit that is connected
to the electrical grid starts drawing a high amount of power which causes the to the electrical grid starts drawing a high amount of power which causes the
wires to heat up and combust, the circuit breaker design pattern is a fail-first wires to heat up and combust, the circuit breaker design pattern is a fail-first
mechanism that shuts down the circuit, request/response relationship or a mechanism that shuts down the circuit, request/response relationship or a
service in the case of software development, to prevent bigger failures. service in the case of software development, to prevent bigger failures.
**Note:** The words "circuit" and "service" are used synonymously throught this **Note:** The words "circuit" and "service" are used synonymously throught this
document. document.
## Implementation ## Implementation
Below is the implementation of a very simple circuit breaker to illustrate the purpose Below is the implementation of a very simple circuit breaker to illustrate the purpose
of the circuit breaker design pattern. of the circuit breaker design pattern.
### Operation Counter ### Operation Counter
`circuit.Counter` is a simple counter that records success and failure states of `circuit.Counter` is a simple counter that records success and failure states of
a circuit along with a timestamp and calculates the consecutive number of a circuit along with a timestamp and calculates the consecutive number of
failures. failures.
```go ```go
package circuit package circuit
import ( import (
"time" "time"
) )
type State int type State int
const ( const (
UnknownState State = iota UnknownState State = iota
FailureState FailureState
SuccessState SuccessState
) )
type Counter interface { type Counter interface {
Count(State) Count(State)
ConsecutiveFailures() uint32 ConsecutiveFailures() uint32
LastActivity() time.Time LastActivity() time.Time
Reset() Reset()
} }
``` ```
### Circuit Breaker ### Circuit Breaker
Circuit is wrapped using the `circuit.Breaker` closure that keeps an internal operation counter. Circuit is wrapped using the `circuit.Breaker` closure that keeps an internal operation counter.
It returns a fast error if the circuit has failed consecutively more than the specified threshold. It returns a fast error if the circuit has failed consecutively more than the specified threshold.
After a while it retries the request and records it. After a while it retries the request and records it.
**Note:** Context type is used here to carry deadlines, cancelation signals, and **Note:** Context type is used here to carry deadlines, cancelation signals, and
other request-scoped values across API boundaries and between processes. other request-scoped values across API boundaries and between processes.
```go ```go
package circuit package circuit
import ( import (
"context" "context"
"time" "time"
) )
type Circuit func(context.Context) error type Circuit func(context.Context) error
func Breaker(c Circuit, failureThreshold uint32) Circuit { func Breaker(c Circuit, failureThreshold uint32) Circuit {
cnt := NewCounter() cnt := NewCounter()
return func(ctx context) error { return func(ctx context) error {
if cnt.ConsecutiveFailures() >= failureThreshold { if cnt.ConsecutiveFailures() >= failureThreshold {
canRetry := func(cnt Counter) { canRetry := func(cnt Counter) {
backoffLevel := Cnt.ConsecutiveFailures() - failureThreshold backoffLevel := Cnt.ConsecutiveFailures() - failureThreshold
// Calculates when should the circuit breaker resume propagating requests // Calculates when should the circuit breaker resume propagating requests
// to the service // to the service
shouldRetryAt := cnt.LastActivity().Add(time.Seconds * 2 << backoffLevel) shouldRetryAt := cnt.LastActivity().Add(time.Seconds * 2 << backoffLevel)
return time.Now().After(shouldRetryAt) return time.Now().After(shouldRetryAt)
} }
if !canRetry(cnt) { if !canRetry(cnt) {
// Fails fast instead of propagating requests to the circuit since // Fails fast instead of propagating requests to the circuit since
// not enough time has passed since the last failure to retry // not enough time has passed since the last failure to retry
return ErrServiceUnavailable return ErrServiceUnavailable
} }
} }
// Unless the failure threshold is exceeded the wrapped service mimics the // Unless the failure threshold is exceeded the wrapped service mimics the
// old behavior and the difference in behavior is seen after consecutive failures // old behavior and the difference in behavior is seen after consecutive failures
if err := c(ctx); err != nil { if err := c(ctx); err != nil {
cnt.Count(FailureState) cnt.Count(FailureState)
return err return err
} }
cnt.Count(SuccessState) cnt.Count(SuccessState)
return nil return nil
} }
} }
``` ```
## Related Works ## Related Works
- [sony/gobreaker](https://github.com/sony/gobreaker) is a well-tested and intuitive circuit breaker implementation for real-world use cases. - [sony/gobreaker](https://github.com/sony/gobreaker) is a well-tested and intuitive circuit breaker implementation for real-world use cases.

View File

@ -1,41 +1,41 @@
# Decorator Pattern # Decorator Pattern
Decorator structural pattern allows extending the function of an existing object dynamically without altering its internals. Decorator structural pattern allows extending the function of an existing object dynamically without altering its internals.
Decorators provide a flexible method to extend functionality of objects. Decorators provide a flexible method to extend functionality of objects.
## Implementation ## Implementation
`LogDecorate` decorates a function with the signature `func(int) int` that `LogDecorate` decorates a function with the signature `func(int) int` that
manipulates integers and adds input/output logging capabilities. manipulates integers and adds input/output logging capabilities.
```go ```go
type Object func(int) int type Object func(int) int
func LogDecorate(fn Object) Object { func LogDecorate(fn Object) Object {
return func(n int) int { return func(n int) int {
log.Println("Starting the execution with the integer", n) log.Println("Starting the execution with the integer", n)
result := fn(n) result := fn(n)
log.Println("Execution is completed with the result", result) log.Println("Execution is completed with the result", result)
return result return result
} }
} }
``` ```
## Usage ## Usage
```go ```go
func Double(n int) int { func Double(n int) int {
return n * 2 return n * 2
} }
f := LogDecorate(Double) f := LogDecorate(Double)
f(5) f(5)
// Starting execution with the integer 5 // Starting execution with the integer 5
// Execution is completed with the result 10 // Execution is completed with the result 10
``` ```
## Rules of Thumb ## Rules of Thumb
- Unlike Adapter pattern, the object to be decorated is obtained by **injection**. - Unlike Adapter pattern, the object to be decorated is obtained by **injection**.
- Decorators should not alter the interface of an object. - Decorators should not alter the interface of an object.

View File

@ -1,45 +1,45 @@
# Proxy Pattern # Proxy Pattern
The [proxy pattern](https://en.wikipedia.org/wiki/Proxy_pattern) provides an object that controls access to another object, intercepting all calls. The [proxy pattern](https://en.wikipedia.org/wiki/Proxy_pattern) provides an object that controls access to another object, intercepting all calls.
## Implementation ## Implementation
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. 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.
Short idea of implementation: Short idea of implementation:
```go ```go
// To use proxy and to object they must implement same methods // To use proxy and to object they must implement same methods
type IObject interface { type IObject interface {
ObjDo(action string) ObjDo(action string)
} }
// Object represents real objects which proxy will delegate data // Object represents real objects which proxy will delegate data
type Object struct { type Object struct {
action string action string
} }
// ObjDo implements IObject interface and handel's all logic // ObjDo implements IObject interface and handel's all logic
func (obj *Object) ObjDo(action string) { func (obj *Object) ObjDo(action string) {
// Action behavior // Action behavior
fmt.Printf("I can, %s", action) fmt.Printf("I can, %s", action)
} }
// ProxyObject represents proxy object with intercepts actions // ProxyObject represents proxy object with intercepts actions
type ProxyObject struct { type ProxyObject struct {
object *Object object *Object
} }
// ObjDo are implemented IObject and intercept action before send in real Object // ObjDo are implemented IObject and intercept action before send in real Object
func (p *ProxyObject) ObjDo(action string) { func (p *ProxyObject) ObjDo(action string) {
if p.object == nil { if p.object == nil {
p.object = new(Object) p.object = new(Object)
} }
if action == "Run" { if action == "Run" {
p.object.ObjDo(action) // Prints: I can, Run p.object.ObjDo(action) // Prints: I can, Run
} }
} }
``` ```
## Usage ## Usage
More complex usage of proxy as example: User creates "Terminal" authorizes and PROXY send execution command to real Terminal object More complex usage of proxy as example: User creates "Terminal" authorizes and PROXY send execution command to real Terminal object
See [proxy/main.go](proxy/main.go) or [view in the Playground](https://play.golang.org/p/mnjKCMaOVE). See [proxy/main.go](proxy/main.go) or [view in the Playground](https://play.golang.org/p/mnjKCMaOVE).

View File

@ -1,127 +1,127 @@
package main package main
import ( import (
"fmt" "fmt"
) )
// For example: // For example:
// we must a execute some command // we must a execute some command
// so before that we must to create new terminal session // so before that we must to create new terminal session
// and provide our user name and command // and provide our user name and command
func main() { func main() {
// Create new instance of Proxy terminal // Create new instance of Proxy terminal
t, err := NewTerminal("gopher") t, err := NewTerminal("gopher")
if err != nil { if err != nil {
// panic: User cant be empty // panic: User cant be empty
// Or // Or
// panic: You (badUser) are not allowed to use terminal and execute commands // panic: You (badUser) are not allowed to use terminal and execute commands
panic(err.Error()) panic(err.Error())
} }
// Execute user command // Execute user command
excResp, excErr := t.Execute("say_hi") // Proxy prints to STDOUT -> PROXY: Intercepted execution of user (gopher), asked command (say_hi) excResp, excErr := t.Execute("say_hi") // Proxy prints to STDOUT -> PROXY: Intercepted execution of user (gopher), asked command (say_hi)
if excErr != nil { if excErr != nil {
fmt.Printf("ERROR: %s\n", excErr.Error()) // Prints: ERROR: I know only how to execute commands: say_hi, man fmt.Printf("ERROR: %s\n", excErr.Error()) // Prints: ERROR: I know only how to execute commands: say_hi, man
} }
// Show execution response // Show execution response
fmt.Println(excResp) // Prints: gopher@go_term$: Hi gopher fmt.Println(excResp) // Prints: gopher@go_term$: Hi gopher
} }
/* /*
From that it's can be different terminals realizations with different methods, propertys, yda yda... From that it's can be different terminals realizations with different methods, propertys, yda yda...
*/ */
// ITerminal is interface, it's a public method whose implemented in Terminal(Proxy) and Gopher Terminal // ITerminal is interface, it's a public method whose implemented in Terminal(Proxy) and Gopher Terminal
type ITerminal interface { type ITerminal interface {
Execute(cmd string) (resp string, err error) Execute(cmd string) (resp string, err error)
} }
// GopherTerminal for example: // GopherTerminal for example:
// Its a "huge" structure with different public methods // Its a "huge" structure with different public methods
type GopherTerminal struct { type GopherTerminal struct {
// user is a current authorized user // user is a current authorized user
User string User string
} }
// Execute just runs known commands for current authorized user // Execute just runs known commands for current authorized user
func (gt *GopherTerminal) Execute(cmd string) (resp string, err error) { func (gt *GopherTerminal) Execute(cmd string) (resp string, err error) {
// Set "terminal" prefix for output // Set "terminal" prefix for output
prefix := fmt.Sprintf("%s@go_term$:", gt.User) prefix := fmt.Sprintf("%s@go_term$:", gt.User)
// Execute some asked commands if we know them // Execute some asked commands if we know them
switch cmd { switch cmd {
case "say_hi": case "say_hi":
resp = fmt.Sprintf("%s Hi %s", prefix, gt.User) resp = fmt.Sprintf("%s Hi %s", prefix, gt.User)
case "man": case "man":
resp = fmt.Sprintf("%s Visit 'https://golang.org/doc/' for Golang documentation", prefix) resp = fmt.Sprintf("%s Visit 'https://golang.org/doc/' for Golang documentation", prefix)
default: default:
err = fmt.Errorf("%s Unknown command", prefix) err = fmt.Errorf("%s Unknown command", prefix)
} }
return return
} }
/* /*
And now we will create owr proxy to deliver user and commands to specific objects And now we will create owr proxy to deliver user and commands to specific objects
*/ */
// Terminal is a implementation of Proxy, it's validates and sends data to GopherTerminal // Terminal is a implementation of Proxy, it's validates and sends data to GopherTerminal
// As example before send commands, user must be authorized // As example before send commands, user must be authorized
type Terminal struct { type Terminal struct {
currentUser string currentUser string
gopherTerminal *GopherTerminal gopherTerminal *GopherTerminal
} }
// NewTerminal creates new instance of terminal // NewTerminal creates new instance of terminal
func NewTerminal(user string) (t *Terminal, err error) { func NewTerminal(user string) (t *Terminal, err error) {
// Check user if given correctly // Check user if given correctly
if user == "" { if user == "" {
err = fmt.Errorf("User cant be empty") err = fmt.Errorf("User cant be empty")
return return
} }
// Before we execute user commands, we validate current user, if he have rights to do it // Before we execute user commands, we validate current user, if he have rights to do it
if authErr := authorizeUser(user); authErr != nil { if authErr := authorizeUser(user); authErr != nil {
err = fmt.Errorf("You (%s) are not allowed to use terminal and execute commands", user) err = fmt.Errorf("You (%s) are not allowed to use terminal and execute commands", user)
return return
} }
// Create new instance of terminal and set valid user // Create new instance of terminal and set valid user
t = &Terminal{currentUser: user} t = &Terminal{currentUser: user}
return return
} }
// Execute intercepts execution of command, implements authorizing user, validates it and // Execute intercepts execution of command, implements authorizing user, validates it and
// poxing command to real terminal (gopherTerminal) method // poxing command to real terminal (gopherTerminal) method
func (t *Terminal) Execute(command string) (resp string, err error) { func (t *Terminal) Execute(command string) (resp string, err error) {
// If user allowed to execute send commands then, for example we can decide which terminal can be used, remote or local etc.. // If user allowed to execute send commands then, for example we can decide which terminal can be used, remote or local etc..
// but for example we just creating new instance of terminal, // but for example we just creating new instance of terminal,
// set current user and send user command to execution in terminal // set current user and send user command to execution in terminal
t.gopherTerminal = &GopherTerminal{User: t.currentUser} t.gopherTerminal = &GopherTerminal{User: t.currentUser}
// For example our proxy can log or output intercepted execution... etc // For example our proxy can log or output intercepted execution... etc
fmt.Printf("PROXY: Intercepted execution of user (%s), asked command (%s)\n", t.currentUser, command) fmt.Printf("PROXY: Intercepted execution of user (%s), asked command (%s)\n", t.currentUser, command)
// Transfer data to original object and execute command // Transfer data to original object and execute command
if resp, err = t.gopherTerminal.Execute(command); err != nil { if resp, err = t.gopherTerminal.Execute(command); err != nil {
err = fmt.Errorf("I know only how to execute commands: say_hi, man") err = fmt.Errorf("I know only how to execute commands: say_hi, man")
return return
} }
return return
} }
// authorize validates user right to execute commands // authorize validates user right to execute commands
func authorizeUser(user string) (err error) { func authorizeUser(user string) (err error) {
// As we use terminal like proxy, then // As we use terminal like proxy, then
// we will intercept user name to validate if it's allowed to execute commands // we will intercept user name to validate if it's allowed to execute commands
if user != "gopher" { if user != "gopher" {
// Do some logs, notifications etc... // Do some logs, notifications etc...
err = fmt.Errorf("User %s in black list", user) err = fmt.Errorf("User %s in black list", user)
return return
} }
return return
} }

View File

@ -1,84 +1,84 @@
# Semaphore Pattern # Semaphore Pattern
A semaphore is a synchronization pattern/primitive that imposes mutual exclusion on a limited number of resources. A semaphore is a synchronization pattern/primitive that imposes mutual exclusion on a limited number of resources.
## Implementation ## Implementation
```go ```go
package semaphore package semaphore
var ( var (
ErrNoTickets = errors.New("semaphore: could not aquire semaphore") ErrNoTickets = errors.New("semaphore: could not aquire semaphore")
ErrIllegalRelease = errors.New("semaphore: can't release the semaphore without acquiring it first") ErrIllegalRelease = errors.New("semaphore: can't release the semaphore without acquiring it first")
) )
// Interface contains the behavior of a semaphore that can be acquired and/or released. // Interface contains the behavior of a semaphore that can be acquired and/or released.
type Interface interface { type Interface interface {
Acquire() error Acquire() error
Release() error Release() error
} }
type implementation struct { type implementation struct {
sem chan struct{} sem chan struct{}
timeout time.Duration timeout time.Duration
} }
func (s *implementation) Acquire() error { func (s *implementation) Acquire() error {
select { select {
case s.sem <- struct{}{}: case s.sem <- struct{}{}:
return nil return nil
case <-time.After(s.timeout): case <-time.After(s.timeout):
return ErrNoTickets return ErrNoTickets
} }
} }
func (s *implementation) Release() error { func (s *implementation) Release() error {
select { select {
case _ = <-s.sem: case _ = <-s.sem:
return nil return nil
case <-time.After(s.timeout): case <-time.After(s.timeout):
return ErrIllegalRelease return ErrIllegalRelease
} }
return nil return nil
} }
func New(tickets int, timeout time.Duration) Interface { func New(tickets int, timeout time.Duration) Interface {
return &implementation{ return &implementation{
sem: make(chan struct{}, tickets), sem: make(chan struct{}, tickets),
timeout: timeout, timeout: timeout,
} }
} }
``` ```
## Usage ## Usage
### Semaphore with Timeouts ### Semaphore with Timeouts
```go ```go
tickets, timeout := 1, 3*time.Second tickets, timeout := 1, 3*time.Second
s := semaphore.New(tickets, timeout) s := semaphore.New(tickets, timeout)
if err := s.Acquire(); err != nil { if err := s.Acquire(); err != nil {
panic(err) panic(err)
} }
// Do important work // Do important work
if err := s.Release(); err != nil { if err := s.Release(); err != nil {
panic(err) panic(err)
} }
``` ```
### Semaphore without Timeouts (Non-Blocking) ### Semaphore without Timeouts (Non-Blocking)
```go ```go
tickets, timeout := 0, 0 tickets, timeout := 0, 0
s := semaphore.New(tickets, timeout) s := semaphore.New(tickets, timeout)
if err := s.Acquire(); err != nil { if err := s.Acquire(); err != nil {
if err != semaphore.ErrNoTickets { if err != semaphore.ErrNoTickets {
panic(err) panic(err)
} }
// No tickets left, can't work :( // No tickets left, can't work :(
os.Exit(1) os.Exit(1)
} }
``` ```