├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── behavioral
├── chain-of-responsibility
│ ├── README.md
│ ├── cashier.go
│ ├── department.go
│ ├── diagram.puml
│ ├── doctor.go
│ ├── medical.go
│ ├── patient.go
│ ├── reception.go
│ └── responsibility_test.go
├── command
│ ├── README.md
│ ├── button.go
│ ├── command.go
│ ├── device.go
│ ├── diagram.puml
│ ├── offcommand.go
│ ├── oncommand.go
│ ├── tv.go
│ └── tv_test.go
├── iterator
│ ├── README.md
│ ├── collection.go
│ ├── diagram.puml
│ ├── iterator.go
│ ├── user.go
│ ├── user_test.go
│ ├── usercollection.go
│ └── useriterator.go
├── mediator
│ ├── README.md
│ ├── diagram.puml
│ ├── goodstrain.go
│ ├── mediator.go
│ ├── passengertrain.go
│ ├── stationmanager.go
│ ├── train.go
│ └── train_test.go
├── memento
│ ├── README.md
│ ├── caretaker.go
│ ├── diagram.puml
│ ├── memento.go
│ ├── memmento_test.go
│ └── originator.go
├── observer
│ ├── README.md
│ ├── customer.go
│ ├── diagram.puml
│ ├── item.go
│ ├── observer.go
│ ├── observer_test.go
│ └── subject.go
├── state
│ ├── README.md
│ ├── diagram.puml
│ ├── hasitemstate.go
│ ├── hasmoneystate.go
│ ├── itemrequestedstate.go
│ ├── noitemstate.go
│ ├── state.go
│ ├── state_test.go
│ └── vendingmachine.go
├── strategy
│ ├── README.md
│ ├── cache.go
│ ├── cache_test.go
│ ├── diagram.puml
│ ├── evictionalgo.go
│ ├── fifo.go
│ ├── lfu.go
│ └── lru.go
├── template-method
│ ├── README.md
│ ├── diagram.puml
│ ├── email.go
│ ├── opt_test.go
│ ├── otp.go
│ └── sms.go
└── vistor
│ ├── README.md
│ ├── areacalc.go
│ ├── circle.go
│ ├── diagram.puml
│ ├── middlecoordinates.go
│ ├── rectangle.go
│ ├── shape.go
│ ├── square.go
│ ├── visitor.go
│ └── vistor_test.go
├── creational
├── abstract-factory
│ ├── README.md
│ ├── adidas.go
│ ├── diagram.puml
│ ├── nike.go
│ ├── shoe.go
│ ├── short.go
│ ├── sports-factory.go
│ └── sports-factory_test.go
├── builder
│ ├── README.md
│ ├── builder_test.go
│ ├── diagram.puml
│ ├── director.go
│ ├── house.go
│ ├── ibuilder.go
│ ├── iglooBuilder.go
│ └── normalBuilder.go
├── factory-method
│ ├── README.md
│ ├── ak47.go
│ ├── diagram.puml
│ ├── gun.go
│ ├── gunfactory.go
│ ├── gunfactory_test.go
│ ├── igun.go
│ └── maverick.go
├── prototype
│ ├── README.md
│ ├── diagram.puml
│ ├── file.go
│ ├── file_test.go
│ ├── folder.go
│ └── inode.go
└── singleton
│ ├── README.md
│ ├── diagram.puml
│ ├── singleton.go
│ └── singleton_test.go
├── image
├── Adapter-Design-Pattern.jpg
├── Bridge-Design-Pattern.jpg
├── Builder-Design-Patter.jpg
├── Chain-of-Responsibility-Design-Pattern.jpg
├── Command-Design-Pattern.jpg
├── Composite-Design-Pattern.jpg
├── Facade-Design-Pattern.jpg
├── Factory-Design-Pattern.jpg
├── Flyweight-Design-Pattern.png
├── Iterator-Design-Pattern.jpg
├── Observer-Design-Pattern.jpg
├── Prototype-Pattern.jpg
├── Proxy-Design-Pattern.jpg
├── State-Design-Pattern.jpg
├── Strategy-Design-Pattern.jpg
└── Visitor-Design-Pattern.jpg
├── images
├── behavioral
│ ├── chain-of-responsibility
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── command
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── iterator
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── mediator
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── memento
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── observer
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── state
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── strategy
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── template-method
│ │ └── diagram
│ │ │ └── diagram.svg
│ └── vistor
│ │ └── diagram
│ │ └── diagram.svg
├── creational
│ ├── abstract-factory
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── builder
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── factory-method
│ │ └── diagram
│ │ │ └── diagram.svg
│ ├── prototype
│ │ └── diagram
│ │ │ └── diagram.svg
│ └── singleton
│ │ └── diagram
│ │ └── diagram.svg
└── structural
│ ├── adapter
│ └── diagram
│ │ └── diagram.svg
│ ├── bridge
│ └── diagram
│ │ └── diagram.svg
│ ├── composite
│ └── diagram
│ │ └── diagram.svg
│ ├── facade
│ └── diagram
│ │ └── diagram.svg
│ ├── flyweight
│ └── diagram
│ │ └── diagram.svg
│ └── proxy
│ └── diagram
│ └── diagram.svg
└── structural
├── adapter
├── README.md
├── adapter_test.go
├── client.go
├── computer.go
├── diagram.puml
├── mac.go
├── windows.go
└── windowsadapter.go
├── bridge
├── README.md
├── bridge_test.go
├── computer.go
├── diagram.puml
├── epson.go
├── hp.go
├── mac.go
├── printer.go
└── win.go
├── composite
├── README.md
├── component.go
├── composite_test.go
├── diagram.puml
├── file.go
└── folder.go
├── decorator
├── README.md
└── diagram.puml
├── facade
├── README.md
├── account.go
├── diagram.puml
├── facade_test.go
├── ledger.go
├── notification.go
├── securitycode.go
├── wallet.go
└── walletfacade.go
├── flyweight
├── README.md
├── counterterroristdress.go
├── diagram.puml
├── dress.go
├── dressfactory.go
├── flyweight_test.go
├── player.go
└── terroristdress.go
└── proxy
├── README.md
├── application.go
├── diagram.puml
├── nginx.go
├── proxy_test.go
└── server.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # macOS Finder
2 | .DS_Store
3 |
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.dll
7 | *.so
8 | *.dylib
9 |
10 | # Test binary, build with `go test -c`
11 | *.test
12 |
13 | # Output of the go coverage tool, specifically when used with LiteIDE
14 | *.out
15 |
16 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
17 | .glide/
18 |
19 | # Temp Files
20 | *~
21 |
22 | # IntelliJ IDEA
23 | .idea/
24 | *.iml
25 |
26 | # Maven
27 | target/
28 | logs/
29 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "plantuml.diagramsRoot": "./",
3 | "plantuml.exportOutDir": "images"
4 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Go 语言设计模式
2 |
3 | 在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学的。
4 |
5 | 设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类别或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类别或对象。设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。
6 |
7 | 并非所有的软件模式都是设计模式,设计模式特指软件“设计”层次上的问题。还有其他非设计模式的模式,如架构模式。同时,算法不能算是一种设计模式,因为算法主要是用来解决计算上的问题,而非设计上的问题。
8 |
9 | 随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且沃德·坎宁安(Ward Cunningham)为此发明了WikiWiki用来交流设计模式的经验。
10 |
11 | -- 摘录 wikipedia
12 |
13 | ## 设计模式类型
14 |
15 | 《设计模式》一书原先把设计模式分为创建型模式、结构型模式、行为型模式,把它们通过授权、聚合、诊断的概念来描述。
16 |
17 | ### 创建型模式
18 |
19 | - [抽象工厂模式(Abstract Factory)](./creational/abstract-factory/)
20 | - [工厂方法模式(Factory Method)](./creational/factory-method/)
21 | - [生成器模式(Builder)](./creational/builder/)
22 | - [惰性初始模式]()
23 | - [对象池模式]()
24 | - [原型模式(Prototype)](./creational/prototype/)
25 | - [单例模式(Singleton)](./creational/singleton/)
26 | - [多例模式]()
27 |
28 | ### 结构型模式
29 |
30 | - [适配器模式(Adapter)](./structural/adapter/)
31 | - [桥接模式(Bridge)](./structural/bridge/)
32 | - [组合模式(Composite)](./structural/composite/)
33 | - [修饰模式(Decorator)](./structural/decorator/)
34 | - [外观模式(Facade)](./structural/facade/)
35 | - [享元模式(Flywight)](./structural/flyweight/)
36 | - [代理模式(Proxy)](./structural/proxy/)
37 |
38 | ### 行为型模式
39 |
40 | - [责任链模式(Chain of Responsibility)](./behavioral/chain-of-responsibility/)
41 | - [命令模式(Command)](./behavioral/command/)
42 | - [解释器模式]()
43 | - [迭代器模式(Iterator)](./behavioral/iterator/)
44 | - [中介者模式(Mediator)](./behavioral/mediator/)
45 | - [备忘录模式(Memento)](./behavioral/memento/)
46 | - [空对象模式]()
47 | - [观察者模式(Observer)](./behavioral/observer/)
48 | - [策略模式(Strategy)](./behavioral/strategy/)
49 | - [状态模式(State)](./behavioral/state/)
50 | - [访问者模式(Vistor)](./behavioral/vistor/)
51 | - [模版方法模式(Template Method)](./behavioral/template-method/)
52 |
53 | ## 设计模式的关系
54 |
55 | ## 设计模式六大原则
56 |
57 | ### 开闭原则(Open Close Principle)
58 |
59 | 开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
60 |
61 | ### 里氏代换原则(Liskov Substitution Principle)
62 |
63 | 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
64 |
65 | ### 依赖倒转原则(Dependence Inversion Principle)
66 |
67 | 该原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
68 |
69 | ### 接口隔离原则(Interface Segregation Principle)
70 |
71 | 该原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
72 |
73 | ### 迪米特法则,又称最少知道原则(Demeter Principle)
74 |
75 | 最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
76 |
77 | ### 合成复用原则(Composite Reuse Principle)
78 |
79 | 合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
80 |
81 | ## 参考链接
82 |
83 | - [Design Patterns](https://sourcemaking.com/design_patterns)
84 | - [Design Patterns](https://refactoring.guru/design-patterns)
85 | - [All Design Patterns in Go](https://golangbyexample.com/all-design-patterns-golang/)
86 | - [设计模式](https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F_(%E8%AE%A1%E7%AE%97%E6%9C%BA))
87 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/README.md:
--------------------------------------------------------------------------------
1 | # Chain of Responsibility
2 |
3 | Chain of Responsibility Design Pattern is a behavioral design pattern. It lets you create a chain of request handlers. For every incoming request, it is passed through the chain and each of the handler:
4 |
5 | - Processes the request or skips the processing.
6 | - Decides whether to pass the request to the next handler in the chain or not
7 |
8 | When to Use?
9 | - The pattern is applicable when there are multiple candidates to process the same request.
10 | - When you don’t want the client to choose the receiver as multiple objects can handle the request. Also, you want to decouple the client from receivers. The Client only needs to know the first element in the chain.
11 |
12 | UML Diagram of example:
13 |
14 |
15 | 
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/cashier.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | import "fmt"
4 |
5 | type cashier struct {
6 | next department
7 | }
8 |
9 | func (m *cashier) execute(p *patient) {
10 | if p.paymentDone {
11 | fmt.Println("Payment Done")
12 | }
13 | fmt.Println("Cashier getting money from patient patient")
14 | }
15 |
16 | func (m *cashier) setNext(next department) {
17 | m.next = next
18 | }
19 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/department.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | type department interface {
4 | execute(*patient)
5 | setNext(department)
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace responsibility {
3 | class cashier << (S,Aquamarine) >> {
4 | - next department
5 |
6 | - execute(p *patient)
7 | - setNext(next department)
8 |
9 | }
10 | interface department {
11 | - execute( *patient)
12 | - setNext( department)
13 |
14 | }
15 | class doctor << (S,Aquamarine) >> {
16 | - next department
17 |
18 | - execute(p *patient)
19 | - setNext(next department)
20 |
21 | }
22 | class medical << (S,Aquamarine) >> {
23 | - next department
24 |
25 | - execute(p *patient)
26 | - setNext(next department)
27 |
28 | }
29 | class patient << (S,Aquamarine) >> {
30 | - name string
31 | - registrationDone bool
32 | - doctorCheckUpDone bool
33 | - medicineDone bool
34 | - paymentDone bool
35 |
36 | }
37 | class reception << (S,Aquamarine) >> {
38 | - next department
39 |
40 | - execute(p *patient)
41 | - setNext(next department)
42 |
43 | }
44 | }
45 |
46 | "responsibility.department" <|-- "responsibility.cashier"
47 | "responsibility.department" <|-- "responsibility.doctor"
48 | "responsibility.department" <|-- "responsibility.medical"
49 | "responsibility.department" <|-- "responsibility.reception"
50 |
51 | @enduml
52 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/doctor.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | import "fmt"
4 |
5 | type doctor struct {
6 | next department
7 | }
8 |
9 | func (d *doctor) execute(p *patient) {
10 | if p.doctorCheckUpDone {
11 | fmt.Println("Doctor checkup already done")
12 | d.next.execute(p)
13 | return
14 | }
15 | fmt.Println("Doctor checking patient")
16 | p.doctorCheckUpDone = true
17 | d.next.execute(p)
18 | }
19 |
20 | func (d *doctor) setNext(next department) {
21 | d.next = next
22 | }
23 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/medical.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | import "fmt"
4 |
5 | type medical struct {
6 | next department
7 | }
8 |
9 | func (m *medical) execute(p *patient) {
10 | if p.medicineDone {
11 | fmt.Println("Medicine already given to patient")
12 | m.next.execute(p)
13 | return
14 | }
15 | fmt.Println("Medical giving medicine to patient")
16 | p.medicineDone = true
17 | m.next.execute(p)
18 | }
19 |
20 | func (m *medical) setNext(next department) {
21 | m.next = next
22 | }
23 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/patient.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | type patient struct {
4 | name string
5 | registrationDone bool
6 | doctorCheckUpDone bool
7 | medicineDone bool
8 | paymentDone bool
9 | }
10 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/reception.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | import "fmt"
4 |
5 | type reception struct {
6 | next department
7 | }
8 |
9 | func (r *reception) execute(p *patient) {
10 | if p.registrationDone {
11 | fmt.Println("Patient registration already done")
12 | r.next.execute(p)
13 | return
14 | }
15 | fmt.Println("Reception registering patient")
16 | p.registrationDone = true
17 | r.next.execute(p)
18 | }
19 |
20 | func (r *reception) setNext(next department) {
21 | r.next = next
22 | }
23 |
--------------------------------------------------------------------------------
/behavioral/chain-of-responsibility/responsibility_test.go:
--------------------------------------------------------------------------------
1 | package responsibility
2 |
3 | import "testing"
4 |
5 | func TestResponsibility(t *testing.T) {
6 | cashier := &cashier{}
7 | //Set next for medical department
8 | medical := &medical{}
9 | medical.setNext(cashier)
10 | //Set next for doctor department
11 | doctor := &doctor{}
12 | doctor.setNext(medical)
13 | //Set next for reception department
14 | reception := &reception{}
15 | reception.setNext(doctor)
16 | patient := &patient{name: "abc"}
17 | //Patient visiting
18 | reception.execute(patient)
19 | }
20 |
--------------------------------------------------------------------------------
/behavioral/command/README.md:
--------------------------------------------------------------------------------
1 | # Command
2 |
3 | Command Design Pattern is a behavioral design pattern. It suggests encapsulating the request as a standalone object. The created object has all the information about the request and thus can execute it independently.
4 |
5 | The basic components that are used in the command design pattern are:
6 |
7 | - Receiver – It is the class which contains the business logic. The command object only delays its requests to the receiver.
8 | - Command – embeds receiver and binds a particular action of the receiver.
9 | - Invoker – It embeds the command and envokes the command by calling the command’s execute method.
10 | - Client – It creates the command with the appropriate receiver bypassing the receiver to the command’s constructor. After that, it also associates the resulting command with an invoker.
11 |
12 |
13 | UML Diagram
14 |
15 |
16 | 
--------------------------------------------------------------------------------
/behavioral/command/button.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | type button struct {
4 | command command
5 | }
6 |
7 | func (c *button) press() {
8 | c.command.execute()
9 | }
10 |
--------------------------------------------------------------------------------
/behavioral/command/command.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | type command interface {
4 | execute()
5 | }
6 |
--------------------------------------------------------------------------------
/behavioral/command/device.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | type device interface {
4 | on()
5 | off()
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/command/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace command {
3 | class button << (S,Aquamarine) >> {
4 | - command command
5 |
6 | - press()
7 |
8 | }
9 | interface command {
10 | - execute()
11 |
12 | }
13 | interface device {
14 | - on()
15 | - off()
16 |
17 | }
18 | class offCommand << (S,Aquamarine) >> {
19 | - device device
20 |
21 | - execute()
22 |
23 | }
24 | class onCommand << (S,Aquamarine) >> {
25 | - device device
26 |
27 | - execute()
28 |
29 | }
30 | class tv << (S,Aquamarine) >> {
31 | - isRunning bool
32 |
33 | - on()
34 | - off()
35 |
36 | }
37 | }
38 |
39 | "command.command" <|-- "command.offCommand"
40 | "command.command" <|-- "command.onCommand"
41 | "command.device" <|-- "command.tv"
42 |
43 | @enduml
44 |
--------------------------------------------------------------------------------
/behavioral/command/offcommand.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | type offCommand struct {
4 | device device
5 | }
6 |
7 | func (c *offCommand) execute() {
8 | c.device.off()
9 | }
10 |
--------------------------------------------------------------------------------
/behavioral/command/oncommand.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | type onCommand struct {
4 | device device
5 | }
6 |
7 | func (c *onCommand) execute() {
8 | c.device.on()
9 | }
10 |
--------------------------------------------------------------------------------
/behavioral/command/tv.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import "fmt"
4 |
5 | type tv struct {
6 | isRunning bool
7 | }
8 |
9 | func (t *tv) on() {
10 | t.isRunning = true
11 | fmt.Println("Turning tv on")
12 | }
13 |
14 | func (t *tv) off() {
15 | t.isRunning = false
16 | fmt.Println("Turning tv off")
17 | }
18 |
--------------------------------------------------------------------------------
/behavioral/command/tv_test.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import "testing"
4 |
5 | func TestTV(t *testing.T) {
6 | tv := &tv{}
7 | onCommand := &onCommand{
8 | device: tv,
9 | }
10 | offCommand := &offCommand{
11 | device: tv,
12 | }
13 | onButton := &button{
14 | command: onCommand,
15 | }
16 | onButton.press()
17 | offButton := &button{
18 | command: offCommand,
19 | }
20 | offButton.press()
21 | }
22 |
--------------------------------------------------------------------------------
/behavioral/iterator/README.md:
--------------------------------------------------------------------------------
1 | # Iterator
2 |
3 | Iterator design pattern is a behavioral design pattern. In this pattern, the collection struct provides an iterator which lets it go through each element in the collection struct in sequence without exposing its underlying implementation.
4 |
5 | Below are basic components of the Iterator Design Pattern
6 |
7 | - **Iterator Interface**: this interface provides basic operations such as hasNext(), getNext() etc. These operations as the name suggests lets you traverse a collection, restarting iteration, etc
8 | - **Collection interface**: this interface represents the collection that needs to be traversed. This interface defines a method createIterator() which returns iterator type
9 | - **Concrete Iterator**: the concrete implementation of iterator interface
10 | - **Concrete Collection**: the concrete implementation of Collection interface
11 |
12 |
13 | UML Diagram:
14 |
15 |
16 | 
17 |
--------------------------------------------------------------------------------
/behavioral/iterator/collection.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | type collection interface {
4 | createIterator() iterator
5 | }
6 |
--------------------------------------------------------------------------------
/behavioral/iterator/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace iterator {
3 | interface collection {
4 | - createIterator() iterator
5 |
6 | }
7 | interface iterator {
8 | - hasNext() bool
9 | - getNext() *user
10 |
11 | }
12 | class user << (S,Aquamarine) >> {
13 | - name string
14 | - age int
15 |
16 | }
17 | class userCollection << (S,Aquamarine) >> {
18 | - users []*user
19 |
20 | - createIterator() iterator
21 |
22 | }
23 | class userIterator << (S,Aquamarine) >> {
24 | - index int
25 | - users []*user
26 |
27 | - hasNext() bool
28 | - getNext() *user
29 |
30 | }
31 | }
32 |
33 | "iterator.collection" <|-- "iterator.userCollection"
34 | "iterator.iterator" <|-- "iterator.userIterator"
35 |
36 | @enduml
37 |
--------------------------------------------------------------------------------
/behavioral/iterator/iterator.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | type iterator interface {
4 | hasNext() bool
5 | getNext() *user
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/iterator/user.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | type user struct {
4 | name string
5 | age int
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/iterator/user_test.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestUser(t *testing.T) {
9 | user1 := &user{
10 | name: "a",
11 | age: 30,
12 | }
13 | user2 := &user{
14 | name: "b",
15 | age: 20,
16 | }
17 | userCollection := &userCollection{
18 | users: []*user{user1, user2},
19 | }
20 | iterator := userCollection.createIterator()
21 | for iterator.hasNext() {
22 | user := iterator.getNext()
23 | fmt.Printf("User is %+v\n", user)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/behavioral/iterator/usercollection.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | type userCollection struct {
4 | users []*user
5 | }
6 |
7 | func (u *userCollection) createIterator() iterator {
8 | return &userIterator{
9 | users: u.users,
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/behavioral/iterator/useriterator.go:
--------------------------------------------------------------------------------
1 | package iterator
2 |
3 | type userIterator struct {
4 | index int
5 | users []*user
6 | }
7 |
8 | func (u *userIterator) hasNext() bool {
9 | if u.index < len(u.users) {
10 | return true
11 | }
12 |
13 | return false
14 | }
15 |
16 | func (u *userIterator) getNext() *user {
17 | if u.hasNext() {
18 | user := u.users[u.index]
19 | u.index++
20 |
21 | return user
22 | }
23 |
24 | return nil
25 | }
26 |
--------------------------------------------------------------------------------
/behavioral/mediator/README.md:
--------------------------------------------------------------------------------
1 | # Mediator
2 |
3 | Mediator design pattern is a behavioral design pattern. This pattern suggests creating a mediator object to prevent direct communication among objects so that direct dependencies between them is avoided.
4 |
5 | One very good example of a mediator patter is the railway system platform. Two trains never communicate between themselves for the availability of the platform. The stationManager acts as a mediator and makes the platform available to only one of the trains. The train connects with stationManager and acts accordingly. It maintains a queue of waiting trains. In case of any train leaving a platform, it notifies one of the train to arrive on the platform next.
6 |
7 | UML Diagram:
8 |
9 | 
10 |
--------------------------------------------------------------------------------
/behavioral/mediator/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace mediator {
3 | class goodsTrain << (S,Aquamarine) >> {
4 | - mediator mediator
5 |
6 | - requestArrival()
7 | - departure()
8 | - permitArrival()
9 |
10 | }
11 | interface mediator {
12 | - canLand( train) bool
13 | - notifyFree()
14 |
15 | }
16 | class passengerTrain << (S,Aquamarine) >> {
17 | - mediator mediator
18 |
19 | - requestArrival()
20 | - departure()
21 | - permitArrival()
22 |
23 | }
24 | class stationManager << (S,Aquamarine) >> {
25 | - isPlatformFree bool
26 | - lock *sync.Mutex
27 | - trainQueue []train
28 |
29 | - canLand(t train) bool
30 | - notifyFree()
31 |
32 | }
33 | interface train {
34 | - requestArrival()
35 | - departure()
36 | - permitArrival()
37 |
38 | }
39 | }
40 |
41 | "mediator.train" <|-- "mediator.goodsTrain"
42 | "mediator.train" <|-- "mediator.passengerTrain"
43 | "mediator.mediator" <|-- "mediator.stationManager"
44 |
45 | @enduml
46 |
--------------------------------------------------------------------------------
/behavioral/mediator/goodstrain.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | import "fmt"
4 |
5 | type goodsTrain struct {
6 | mediator mediator
7 | }
8 |
9 | func (g *goodsTrain) requestArrival() {
10 | if g.mediator.canLand(g) {
11 | fmt.Println("GoodsTrain: Landing")
12 | } else {
13 | fmt.Println("GoodsTrain: Waiting")
14 | }
15 | }
16 |
17 | func (g *goodsTrain) departure() {
18 | g.mediator.notifyFree()
19 | fmt.Println("GoodsTrain: Leaving")
20 | }
21 |
22 | func (g *goodsTrain) permitArrival() {
23 | fmt.Println("GoodsTrain: Arrival Permitted. Landing")
24 | }
25 |
--------------------------------------------------------------------------------
/behavioral/mediator/mediator.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | type mediator interface {
4 | canLand(train) bool
5 | notifyFree()
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/mediator/passengertrain.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | import "fmt"
4 |
5 | type passengerTrain struct {
6 | mediator mediator
7 | }
8 |
9 | func (g *passengerTrain) requestArrival() {
10 | if g.mediator.canLand(g) {
11 | fmt.Println("PassengerTrain: Landing")
12 | } else {
13 | fmt.Println("PassengerTrain: Waiting")
14 | }
15 | }
16 |
17 | func (g *passengerTrain) departure() {
18 | fmt.Println("PassengerTrain: Leaving")
19 | g.mediator.notifyFree()
20 | }
21 |
22 | func (g *passengerTrain) permitArrival() {
23 | fmt.Println("PassengerTrain: Arrival Permitted. Landing")
24 | }
25 |
--------------------------------------------------------------------------------
/behavioral/mediator/stationmanager.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | import "sync"
4 |
5 | type stationManager struct {
6 | isPlatformFree bool
7 | lock *sync.Mutex
8 | trainQueue []train
9 | }
10 |
11 | func newStationManger() *stationManager {
12 | return &stationManager{
13 | isPlatformFree: true,
14 | lock: &sync.Mutex{},
15 | }
16 | }
17 |
18 | func (s *stationManager) canLand(t train) bool {
19 | s.lock.Lock()
20 | defer s.lock.Unlock()
21 | if s.isPlatformFree {
22 | s.isPlatformFree = false
23 | return true
24 | }
25 | s.trainQueue = append(s.trainQueue, t)
26 | return false
27 | }
28 |
29 | func (s *stationManager) notifyFree() {
30 | s.lock.Lock()
31 | defer s.lock.Unlock()
32 | if !s.isPlatformFree {
33 | s.isPlatformFree = true
34 | }
35 | if len(s.trainQueue) > 0 {
36 | firstTrainInQueue := s.trainQueue[0]
37 | s.trainQueue = s.trainQueue[1:]
38 | firstTrainInQueue.permitArrival()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/behavioral/mediator/train.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | type train interface {
4 | requestArrival()
5 | departure()
6 | permitArrival()
7 | }
8 |
--------------------------------------------------------------------------------
/behavioral/mediator/train_test.go:
--------------------------------------------------------------------------------
1 | package mediator
2 |
3 | import "testing"
4 |
5 | func TestTrain(t *testing.T) {
6 | stationManager := newStationManger()
7 | passengerTrain := &passengerTrain{
8 | mediator: stationManager,
9 | }
10 | goodsTrain := &goodsTrain{
11 | mediator: stationManager,
12 | }
13 | passengerTrain.requestArrival()
14 | goodsTrain.requestArrival()
15 | passengerTrain.departure()
16 | }
17 |
--------------------------------------------------------------------------------
/behavioral/memento/README.md:
--------------------------------------------------------------------------------
1 | # Memento
2 |
3 | Memento design pattern is a behavioral design pattern. It allows us to save checkpoints for an object and thus allow an object to revert to its previous state. Basically it helps in undo-redo operations on an object. Below are the design components of the Memento Design Pattern.
4 |
5 | - **Originator**: It is the actual object whose state is saved as a memento.
6 | - **Memento**: This is the object which saves the state of the originator
7 | - **Caretaker**: This is the object that saves multiple mementos. Given an index, it returns the corresponding memento.
8 |
9 | UML Diagram:
10 |
11 | 
--------------------------------------------------------------------------------
/behavioral/memento/caretaker.go:
--------------------------------------------------------------------------------
1 | package memento
2 |
3 | type caretaker struct {
4 | mementoArray []*memento
5 | }
6 |
7 | func (c *caretaker) addMemento(m *memento) {
8 | c.mementoArray = append(c.mementoArray, m)
9 | }
10 |
11 | func (c *caretaker) getMenento(index int) *memento {
12 | return c.mementoArray[index]
13 | }
14 |
--------------------------------------------------------------------------------
/behavioral/memento/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace memento {
3 | class caretaker << (S,Aquamarine) >> {
4 | - mementoArray []*memento
5 |
6 | - addMemento(m *memento)
7 | - getMenento(index int) *memento
8 |
9 | }
10 | class memento << (S,Aquamarine) >> {
11 | - state string
12 |
13 | - getSavedState() string
14 |
15 | }
16 | class originator << (S,Aquamarine) >> {
17 | - state string
18 |
19 | - createMemento() *memento
20 | - restorememento(m *memento)
21 | - setState(state string)
22 | - getState() string
23 |
24 | }
25 | }
26 |
27 |
28 | @enduml
29 |
--------------------------------------------------------------------------------
/behavioral/memento/memento.go:
--------------------------------------------------------------------------------
1 | package memento
2 |
3 | type memento struct {
4 | state string
5 | }
6 |
7 | func (m *memento) getSavedState() string {
8 | return m.state
9 | }
10 |
--------------------------------------------------------------------------------
/behavioral/memento/memmento_test.go:
--------------------------------------------------------------------------------
1 | package memento
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestMemento(t *testing.T) {
9 | caretaker := &caretaker{
10 | mementoArray: make([]*memento, 0),
11 | }
12 | originator := &originator{
13 | state: "A",
14 | }
15 | fmt.Printf("Originator Current State: %s\n", originator.getState())
16 | caretaker.addMemento(originator.createMemento())
17 |
18 | originator.setState("B")
19 | fmt.Printf("Originator Current State: %s\n", originator.getState())
20 |
21 | caretaker.addMemento(originator.createMemento())
22 | originator.setState("C")
23 |
24 | fmt.Printf("Originator Current State: %s\n", originator.getState())
25 | caretaker.addMemento(originator.createMemento())
26 |
27 | originator.restorememento(caretaker.getMenento(1))
28 | fmt.Printf("Restored to State: %s\n", originator.getState())
29 |
30 | originator.restorememento(caretaker.getMenento(0))
31 | fmt.Printf("Restored to State: %s\n", originator.getState())
32 | }
33 |
--------------------------------------------------------------------------------
/behavioral/memento/originator.go:
--------------------------------------------------------------------------------
1 | package memento
2 |
3 | type originator struct {
4 | state string
5 | }
6 |
7 | func (e *originator) createMemento() *memento {
8 | return &memento{state: e.state}
9 | }
10 |
11 | func (e *originator) restorememento(m *memento) {
12 | e.state = m.getSavedState()
13 | }
14 |
15 | func (e *originator) setState(state string) {
16 | e.state = state
17 | }
18 |
19 | func (e *originator) getState() string {
20 | return e.state
21 | }
22 |
--------------------------------------------------------------------------------
/behavioral/observer/README.md:
--------------------------------------------------------------------------------
1 | # Observer
2 |
3 | Observer Design Pattern is a behavioral design pattern. This pattern allows an instance (called subject) to publish events to other multiple instances (called observers). These observers subscribe to the subject and hence get notified by events in case of any change happening in the subject.
4 |
5 | UML Diagram:
6 |
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/behavioral/observer/customer.go:
--------------------------------------------------------------------------------
1 | package observer
2 |
3 | import "fmt"
4 |
5 | type customer struct {
6 | id string
7 | }
8 |
9 | func (c *customer) update(itemName string) {
10 | fmt.Printf("Sending email to customer %s for item %s\n", c.id, itemName)
11 | }
12 |
13 | func (c *customer) getID() string {
14 | return c.id
15 | }
16 |
--------------------------------------------------------------------------------
/behavioral/observer/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace observer {
3 | class customer << (S,Aquamarine) >> {
4 | - id string
5 |
6 | - update(itemName string)
7 | - getID() string
8 |
9 | }
10 | class item << (S,Aquamarine) >> {
11 | - observerList []observer
12 | - name string
13 | - inStock bool
14 |
15 | - updateAvailability()
16 | - register(o observer)
17 | - deregister(o observer)
18 | - notifyAll()
19 |
20 | }
21 | interface observer {
22 | - update( string)
23 | - getID() string
24 |
25 | }
26 | interface subject {
27 | - register(Observer observer)
28 | - deregister(Observer observer)
29 | - notifyAll()
30 |
31 | }
32 | }
33 |
34 | "observer.observer" <|-- "observer.customer"
35 | "observer.subject" <|-- "observer.item"
36 |
37 | @enduml
38 |
--------------------------------------------------------------------------------
/behavioral/observer/item.go:
--------------------------------------------------------------------------------
1 | package observer
2 |
3 | import "fmt"
4 |
5 | type item struct {
6 | observerList []observer
7 | name string
8 | inStock bool
9 | }
10 |
11 | func newItem(name string) *item {
12 | return &item{
13 | name: name,
14 | }
15 | }
16 |
17 | func (i *item) updateAvailability() {
18 | fmt.Printf("Item %s is now in stock\n", i.name)
19 | i.inStock = true
20 | i.notifyAll()
21 | }
22 |
23 | func (i *item) register(o observer) {
24 | i.observerList = append(i.observerList, o)
25 | }
26 |
27 | func (i *item) deregister(o observer) {
28 | i.observerList = removeFromslice(i.observerList, o)
29 | }
30 |
31 | func (i *item) notifyAll() {
32 | for _, observer := range i.observerList {
33 | observer.update(i.name)
34 | }
35 | }
36 |
37 | func removeFromslice(observerList []observer, observerToRemove observer) []observer {
38 | observerListLength := len(observerList)
39 | for i, observer := range observerList {
40 | if observerToRemove.getID() == observer.getID() {
41 | observerList[observerListLength-1], observerList[i] = observerList[i], observerList[observerListLength-1]
42 | return observerList[:observerListLength-1]
43 | }
44 | }
45 | return observerList
46 | }
47 |
--------------------------------------------------------------------------------
/behavioral/observer/observer.go:
--------------------------------------------------------------------------------
1 | package observer
2 |
3 | type observer interface {
4 | update(string)
5 | getID() string
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/observer/observer_test.go:
--------------------------------------------------------------------------------
1 | package observer
2 |
3 | import "testing"
4 |
5 | func TestObserver(t *testing.T) {
6 | shirtItem := newItem("Nike Shirt")
7 |
8 | observerFirst := &customer{id: "abc@gmail.com"}
9 | observerSecond := &customer{id: "xyz@gmail.com"}
10 |
11 | shirtItem.register(observerFirst)
12 | shirtItem.register(observerSecond)
13 |
14 | shirtItem.updateAvailability()
15 | }
16 |
--------------------------------------------------------------------------------
/behavioral/observer/subject.go:
--------------------------------------------------------------------------------
1 | package observer
2 |
3 | type subject interface {
4 | register(Observer observer)
5 | deregister(Observer observer)
6 | notifyAll()
7 | }
8 |
--------------------------------------------------------------------------------
/behavioral/state/README.md:
--------------------------------------------------------------------------------
1 | # State
2 |
3 | State design pattern is a behavioral design pattern that is based on Finite State Machine. We will explain the State Design Pattern in the context of an example of a Vending Machine. For simplicity, let’s assume that vending machine only has one type of item or product. Also for simplicity lets assume that a Vending Machine can be in 4 different states
4 |
5 | - hasItem
6 | - noItem
7 | - itemRequested
8 | - hasMoney
9 |
10 | A vending machine will also have different actions. Again for simplicity lets assume that there are only four actions:
11 |
12 | - Select the item
13 | - Add the item
14 | - Insert Money
15 | - Dispense Item
16 |
17 | When To Use?
18 |
19 | - Use the State design pattern when the object can be in many different states. Depending upon current request the object needs to change its current state
20 | - Use when an object will have different responses to the same request depending upon the current state. Using state design pattern here will prevent a lot of conditional statements
21 |
22 | UML Diagram:
23 |
24 |
25 | 
--------------------------------------------------------------------------------
/behavioral/state/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace state {
3 | class hasItemState << (S,Aquamarine) >> {
4 | - vendingMachine *vendingMachine
5 |
6 | - requestItem() error
7 | - addItem(count int) error
8 | - insertMoney(money int) error
9 | - dispenseItem() error
10 |
11 | }
12 | class hasMoneyState << (S,Aquamarine) >> {
13 | - vendingMachine *vendingMachine
14 |
15 | - requestItem() error
16 | - addItem(count int) error
17 | - insertMoney(money int) error
18 | - dispenseItem() error
19 |
20 | }
21 | class itemRequestedState << (S,Aquamarine) >> {
22 | - vendingMachine *vendingMachine
23 |
24 | - requestItem() error
25 | - addItem(count int) error
26 | - insertMoney(money int) error
27 | - dispenseItem() error
28 |
29 | }
30 | class noItemState << (S,Aquamarine) >> {
31 | - vendingMachine *vendingMachine
32 |
33 | - requestItem() error
34 | - addItem(count int) error
35 | - insertMoney(money int) error
36 | - dispenseItem() error
37 |
38 | }
39 | interface state {
40 | - addItem( int) error
41 | - requestItem() error
42 | - insertMoney(money int) error
43 | - dispenseItem() error
44 |
45 | }
46 | class vendingMachine << (S,Aquamarine) >> {
47 | - hasItem state
48 | - itemRequested state
49 | - hasMoney state
50 | - noItem state
51 | - currentState state
52 | - itemCount int
53 | - itemPrice int
54 |
55 | - requestItem() error
56 | - addItem(count int) error
57 | - insertMoney(money int) error
58 | - dispenseItem() error
59 | - setState(s state)
60 | - incrementItemCount(count int)
61 |
62 | }
63 | }
64 |
65 | "state.state" <|-- "state.hasItemState"
66 | "state.state" <|-- "state.hasMoneyState"
67 | "state.state" <|-- "state.itemRequestedState"
68 | "state.state" <|-- "state.noItemState"
69 | "state.state" <|-- "state.vendingMachine"
70 |
71 | @enduml
72 |
--------------------------------------------------------------------------------
/behavioral/state/hasitemstate.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import "fmt"
4 |
5 | type hasItemState struct {
6 | vendingMachine *vendingMachine
7 | }
8 |
9 | func (i *hasItemState) requestItem() error {
10 | if i.vendingMachine.itemCount == 0 {
11 | i.vendingMachine.setState(i.vendingMachine.noItem)
12 | return fmt.Errorf("No item present")
13 | }
14 | fmt.Printf("Item requestd\n")
15 | i.vendingMachine.setState(i.vendingMachine.itemRequested)
16 | return nil
17 | }
18 |
19 | func (i *hasItemState) addItem(count int) error {
20 | fmt.Printf("%d items added\n", count)
21 | i.vendingMachine.incrementItemCount(count)
22 | return nil
23 | }
24 |
25 | func (i *hasItemState) insertMoney(money int) error {
26 | return fmt.Errorf("Please select item first")
27 | }
28 | func (i *hasItemState) dispenseItem() error {
29 | return fmt.Errorf("Please select item first")
30 | }
31 |
--------------------------------------------------------------------------------
/behavioral/state/hasmoneystate.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import "fmt"
4 |
5 | type hasMoneyState struct {
6 | vendingMachine *vendingMachine
7 | }
8 |
9 | func (i *hasMoneyState) requestItem() error {
10 | return fmt.Errorf("Item dispense in progress")
11 | }
12 |
13 | func (i *hasMoneyState) addItem(count int) error {
14 | return fmt.Errorf("Item dispense in progress")
15 | }
16 |
17 | func (i *hasMoneyState) insertMoney(money int) error {
18 | return fmt.Errorf("Item out of stock")
19 | }
20 |
21 | func (i *hasMoneyState) dispenseItem() error {
22 | fmt.Println("Dispensing Item")
23 | i.vendingMachine.itemCount = i.vendingMachine.itemCount - 1
24 | if i.vendingMachine.itemCount == 0 {
25 | i.vendingMachine.setState(i.vendingMachine.noItem)
26 | } else {
27 | i.vendingMachine.setState(i.vendingMachine.hasItem)
28 | }
29 | return nil
30 | }
31 |
--------------------------------------------------------------------------------
/behavioral/state/itemrequestedstate.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import "fmt"
4 |
5 | type itemRequestedState struct {
6 | vendingMachine *vendingMachine
7 | }
8 |
9 | func (i *itemRequestedState) requestItem() error {
10 | return fmt.Errorf("Item already requested")
11 | }
12 |
13 | func (i *itemRequestedState) addItem(count int) error {
14 | return fmt.Errorf("Item Dispense in progress")
15 | }
16 |
17 | func (i *itemRequestedState) insertMoney(money int) error {
18 | if money < i.vendingMachine.itemPrice {
19 | fmt.Errorf("Inserted money is less. Please insert %d", i.vendingMachine.itemPrice)
20 | }
21 | fmt.Println("Money entered is ok")
22 | i.vendingMachine.setState(i.vendingMachine.hasMoney)
23 | return nil
24 | }
25 |
26 | func (i *itemRequestedState) dispenseItem() error {
27 | return fmt.Errorf("Please insert money first")
28 | }
29 |
--------------------------------------------------------------------------------
/behavioral/state/noitemstate.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import "fmt"
4 |
5 | type noItemState struct {
6 | vendingMachine *vendingMachine
7 | }
8 |
9 | func (i *noItemState) requestItem() error {
10 | return fmt.Errorf("Item out of stock")
11 | }
12 |
13 | func (i *noItemState) addItem(count int) error {
14 | i.vendingMachine.incrementItemCount(count)
15 | i.vendingMachine.setState(i.vendingMachine.hasItem)
16 | return nil
17 | }
18 |
19 | func (i *noItemState) insertMoney(money int) error {
20 | return fmt.Errorf("Item out of stock")
21 | }
22 | func (i *noItemState) dispenseItem() error {
23 | return fmt.Errorf("Item out of stock")
24 | }
25 |
--------------------------------------------------------------------------------
/behavioral/state/state.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | type state interface {
4 | addItem(int) error
5 | requestItem() error
6 | insertMoney(money int) error
7 | dispenseItem() error
8 | }
9 |
--------------------------------------------------------------------------------
/behavioral/state/state_test.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "testing"
7 | )
8 |
9 | func TestState(t *testing.T) {
10 | vendingMachine := newVendingMachine(1, 10)
11 | err := vendingMachine.requestItem()
12 | if err != nil {
13 | log.Fatalf(err.Error())
14 | }
15 | err = vendingMachine.insertMoney(10)
16 | if err != nil {
17 | log.Fatalf(err.Error())
18 | }
19 | err = vendingMachine.dispenseItem()
20 | if err != nil {
21 | log.Fatalf(err.Error())
22 | }
23 |
24 | fmt.Println()
25 | err = vendingMachine.addItem(2)
26 | if err != nil {
27 | log.Fatalf(err.Error())
28 | }
29 |
30 | fmt.Println()
31 |
32 | err = vendingMachine.requestItem()
33 | if err != nil {
34 | log.Fatalf(err.Error())
35 | }
36 |
37 | err = vendingMachine.insertMoney(10)
38 | if err != nil {
39 | log.Fatalf(err.Error())
40 | }
41 |
42 | err = vendingMachine.dispenseItem()
43 | if err != nil {
44 | log.Fatalf(err.Error())
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/behavioral/state/vendingmachine.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import "fmt"
4 |
5 | type vendingMachine struct {
6 | hasItem state
7 | itemRequested state
8 | hasMoney state
9 | noItem state
10 |
11 | currentState state
12 |
13 | itemCount int
14 | itemPrice int
15 | }
16 |
17 | func newVendingMachine(itemCount, itemPrice int) *vendingMachine {
18 | v := &vendingMachine{
19 | itemCount: itemCount,
20 | itemPrice: itemPrice,
21 | }
22 | hasItemState := &hasItemState{
23 | vendingMachine: v,
24 | }
25 | itemRequestedState := &itemRequestedState{
26 | vendingMachine: v,
27 | }
28 | hasMoneyState := &hasMoneyState{
29 | vendingMachine: v,
30 | }
31 | noItemState := &noItemState{
32 | vendingMachine: v,
33 | }
34 |
35 | v.setState(hasItemState)
36 | v.hasItem = hasItemState
37 | v.itemRequested = itemRequestedState
38 | v.hasMoney = hasMoneyState
39 | v.noItem = noItemState
40 | return v
41 | }
42 |
43 | func (v *vendingMachine) requestItem() error {
44 | return v.currentState.requestItem()
45 | }
46 |
47 | func (v *vendingMachine) addItem(count int) error {
48 | return v.currentState.addItem(count)
49 | }
50 |
51 | func (v *vendingMachine) insertMoney(money int) error {
52 | return v.currentState.insertMoney(money)
53 | }
54 |
55 | func (v *vendingMachine) dispenseItem() error {
56 | return v.currentState.dispenseItem()
57 | }
58 |
59 | func (v *vendingMachine) setState(s state) {
60 | v.currentState = s
61 | }
62 |
63 | func (v *vendingMachine) incrementItemCount(count int) {
64 | fmt.Printf("Adding %d items\n", count)
65 | v.itemCount = v.itemCount + count
66 | }
67 |
--------------------------------------------------------------------------------
/behavioral/strategy/README.md:
--------------------------------------------------------------------------------
1 | # Strategy
2 |
3 | Strategy design pattern is a behavioral design pattern. This design pattern allows you to change the behavior of an object at run time without any change in the class of that object.
4 |
5 | Let’s understand the strategy pattern with an example. Suppose you are building an In-Memory-Cache. Since it is an In-Memory-Cache it is of limited size. Whenever it reaches its maximum size that some old entries from the cache need to be evicted. This eviction can happen via several algorithms. Some of the popular algorithms are
6 |
7 | - LRU – Least Recently Used: Remove the entry which has been used least recently.
8 | - FIFO – First In First Out: Remove the entry, which was created first.
9 | - LFU – Least Frequently Used: Remove the entry which was least frequently used.
10 |
11 | When to Use?
12 | - When an object needs to support different behavior and you want to change the behavior at run time.
13 | - When you want to avoid a lot of conditionals of choosing the runtime behavior.
14 | - When you have different algorithms that are similar and they only differ in the way they execute some behavior.
15 |
16 | UML Diagram:
17 |
18 | 
--------------------------------------------------------------------------------
/behavioral/strategy/cache.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | type cache struct {
4 | storage map[string]string
5 | evictionAlgo evictionAlgo
6 | capacity int
7 | maxCapacity int
8 | }
9 |
10 | func initCache(e evictionAlgo) *cache {
11 | storage := make(map[string]string)
12 | return &cache{
13 | storage: storage,
14 | evictionAlgo: e,
15 | capacity: 0,
16 | maxCapacity: 2,
17 | }
18 | }
19 |
20 | func (c *cache) setEvictionAlgo(e evictionAlgo) {
21 | c.evictionAlgo = e
22 | }
23 |
24 | func (c *cache) add(key, value string) {
25 | if c.capacity == c.maxCapacity {
26 | c.evict()
27 | }
28 | c.capacity++
29 | c.storage[key] = value
30 | }
31 |
32 | func (c *cache) get(key string) {
33 | delete(c.storage, key)
34 | }
35 |
36 | func (c *cache) evict() {
37 | c.evictionAlgo.evict(c)
38 | c.capacity--
39 | }
40 |
--------------------------------------------------------------------------------
/behavioral/strategy/cache_test.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | import "testing"
4 |
5 | func TestCache(t *testing.T) {
6 | lfu := &lfu{}
7 | cache := initCache(lfu)
8 | cache.add("a", "1")
9 | cache.add("b", "2")
10 | cache.add("c", "3")
11 |
12 | lru := &lru{}
13 | cache.setEvictionAlgo(lru)
14 | cache.add("d", "4")
15 |
16 | fifo := &fifo{}
17 | cache.setEvictionAlgo(fifo)
18 | cache.add("e", "5")
19 |
20 | /*
21 | Evicting by lfu strtegy
22 | Evicting by lru strtegy
23 | Evicting by fifo strtegy
24 | */
25 | }
26 |
--------------------------------------------------------------------------------
/behavioral/strategy/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace strategy {
3 | class cache << (S,Aquamarine) >> {
4 | - storage map[string]string
5 | - evictionAlgo evictionAlgo
6 | - capacity int
7 | - maxCapacity int
8 |
9 | - setEvictionAlgo(e evictionAlgo)
10 | - add(key string, value string)
11 | - get(key string)
12 | - evict()
13 |
14 | }
15 | interface evictionAlgo {
16 | - evict(c *cache)
17 |
18 | }
19 | class fifo << (S,Aquamarine) >> {
20 | - evict(c *cache)
21 |
22 | }
23 | class lfu << (S,Aquamarine) >> {
24 | - evict(c *cache)
25 |
26 | }
27 | class lru << (S,Aquamarine) >> {
28 | - evict(c *cache)
29 |
30 | }
31 | }
32 |
33 | "strategy.evictionAlgo" <|-- "strategy.fifo"
34 | "strategy.evictionAlgo" <|-- "strategy.lfu"
35 | "strategy.evictionAlgo" <|-- "strategy.lru"
36 |
37 | @enduml
38 |
--------------------------------------------------------------------------------
/behavioral/strategy/evictionalgo.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | type evictionAlgo interface {
4 | evict(c *cache)
5 | }
6 |
--------------------------------------------------------------------------------
/behavioral/strategy/fifo.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | import "fmt"
4 |
5 | type fifo struct {
6 | }
7 |
8 | func (l *fifo) evict(c *cache) {
9 | fmt.Println("Evicting by fifo strtegy")
10 | }
11 |
--------------------------------------------------------------------------------
/behavioral/strategy/lfu.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | import "fmt"
4 |
5 | type lfu struct {
6 | }
7 |
8 | func (l *lfu) evict(c *cache) {
9 | fmt.Println("Evicting by lfu strtegy")
10 | }
11 |
--------------------------------------------------------------------------------
/behavioral/strategy/lru.go:
--------------------------------------------------------------------------------
1 | package strategy
2 |
3 | import "fmt"
4 |
5 | type lru struct {
6 | }
7 |
8 | func (l *lru) evict(c *cache) {
9 | fmt.Println("Evicting by lru strtegy")
10 | }
11 |
--------------------------------------------------------------------------------
/behavioral/template-method/README.md:
--------------------------------------------------------------------------------
1 | # Template Method
2 |
3 | Template Method Design Pattern is a behavioral design pattern that lets you define a template or algorithm for a particular operation. Let’s understand the template design pattern with an example.
4 |
5 | UML Diagram:
6 |
7 | 
--------------------------------------------------------------------------------
/behavioral/template-method/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace templatemethod {
3 | class email << (S,Aquamarine) >> {
4 | - genRandomOTP(len int) string
5 | - saveOTPCache(otp string)
6 | - getMessage(otp string) string
7 | - sendNotification(message string) error
8 | - publishMetric()
9 |
10 | }
11 | interface iOtp {
12 | - genRandomOTP( int) string
13 | - saveOTPCache( string)
14 | - getMessage( string) string
15 | - sendNotification( string) error
16 | - publishMetric()
17 |
18 | }
19 | class otp << (S,Aquamarine) >> {
20 | - iOtp iOtp
21 |
22 | - genAndSendOTP(otpLength int) error
23 |
24 | }
25 | class sms << (S,Aquamarine) >> {
26 | - genRandomOTP(len int) string
27 | - saveOTPCache(otp string)
28 | - getMessage(otp string) string
29 | - sendNotification(message string) error
30 | - publishMetric()
31 |
32 | }
33 | }
34 | "templatemethod.otp" *-- "templatemethod.email"
35 | "templatemethod.otp" *-- "templatemethod.sms"
36 |
37 | "templatemethod.iOtp" <|-- "templatemethod.email"
38 | "templatemethod.iOtp" <|-- "templatemethod.sms"
39 |
40 | @enduml
41 |
--------------------------------------------------------------------------------
/behavioral/template-method/email.go:
--------------------------------------------------------------------------------
1 | package templatemethod
2 |
3 | import "fmt"
4 |
5 | type email struct {
6 | otp
7 | }
8 |
9 | func (s *email) genRandomOTP(len int) string {
10 | randomOTP := "1234"
11 | fmt.Printf("EMAIL: generating random otp %s\n", randomOTP)
12 | return randomOTP
13 | }
14 |
15 | func (s *email) saveOTPCache(otp string) {
16 | fmt.Printf("EMAIL: saving otp: %s to cache\n", otp)
17 | }
18 |
19 | func (s *email) getMessage(otp string) string {
20 | return "EMAIL OTP for login is " + otp
21 | }
22 |
23 | func (s *email) sendNotification(message string) error {
24 | fmt.Printf("EMAIL: sending email: %s\n", message)
25 | return nil
26 | }
27 |
28 | func (s *email) publishMetric() {
29 | fmt.Printf("EMAIL: publishing metrics\n")
30 | }
31 |
--------------------------------------------------------------------------------
/behavioral/template-method/opt_test.go:
--------------------------------------------------------------------------------
1 | package templatemethod
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestOpt(t *testing.T) {
9 | smsOTP := &sms{}
10 | o := otp{
11 | iOtp: smsOTP,
12 | }
13 | o.genAndSendOTP(4)
14 | fmt.Println("")
15 | emailOTP := &email{}
16 | o = otp{
17 | iOtp: emailOTP,
18 | }
19 | o.genAndSendOTP(4)
20 |
21 | /*
22 | MS: generating random otp 1234
23 | SMS: saving otp: 1234 to cache
24 | SMS: sending sms: SMS OTP for login is 1234
25 | SMS: publishing metrics
26 |
27 | EMAIL: generating random otp 1234
28 | EMAIL: saving otp: 1234 to cache
29 | EMAIL: sending email: EMAIL OTP for login is 1234
30 | EMAIL: publishing metrics
31 | */
32 | }
33 |
--------------------------------------------------------------------------------
/behavioral/template-method/otp.go:
--------------------------------------------------------------------------------
1 | package templatemethod
2 |
3 | type iOtp interface {
4 | genRandomOTP(int) string
5 | saveOTPCache(string)
6 | getMessage(string) string
7 | sendNotification(string) error
8 | publishMetric()
9 | }
10 |
11 | type otp struct {
12 | iOtp iOtp
13 | }
14 |
15 | func (o *otp) genAndSendOTP(otpLength int) error {
16 | otp := o.iOtp.genRandomOTP(otpLength)
17 | o.iOtp.saveOTPCache(otp)
18 | message := o.iOtp.getMessage(otp)
19 | err := o.iOtp.sendNotification(message)
20 | if err != nil {
21 | return err
22 | }
23 | o.iOtp.publishMetric()
24 | return nil
25 | }
26 |
--------------------------------------------------------------------------------
/behavioral/template-method/sms.go:
--------------------------------------------------------------------------------
1 | package templatemethod
2 |
3 | import "fmt"
4 |
5 | type sms struct {
6 | otp
7 | }
8 |
9 | func (s *sms) genRandomOTP(len int) string {
10 | randomOTP := "1234"
11 | fmt.Printf("SMS: generating random otp %s\n", randomOTP)
12 | return randomOTP
13 | }
14 |
15 | func (s *sms) saveOTPCache(otp string) {
16 | fmt.Printf("SMS: saving otp: %s to cache\n", otp)
17 | }
18 |
19 | func (s *sms) getMessage(otp string) string {
20 | return "SMS OTP for login is " + otp
21 | }
22 |
23 | func (s *sms) sendNotification(message string) error {
24 | fmt.Printf("SMS: sending sms: %s\n", message)
25 | return nil
26 | }
27 |
28 | func (s *sms) publishMetric() {
29 | fmt.Printf("SMS: publishing metrics\n")
30 | }
31 |
--------------------------------------------------------------------------------
/behavioral/vistor/README.md:
--------------------------------------------------------------------------------
1 | # Visitor
2 |
3 | Visitor Design Pattern is a Behavioural design pattern that lets you add behaviour to a struct without actually modifying the struct.
4 |
5 | UML Diagram:
6 |
7 |
8 | 
--------------------------------------------------------------------------------
/behavioral/vistor/areacalc.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | import "fmt"
4 |
5 | type areaCalculator struct {
6 | area int
7 | }
8 |
9 | func (a *areaCalculator) visitForSquare(s *square) {
10 | //Calculate area for square. After calculating the area assign in to the area instance variable
11 | fmt.Println("Calculating area for square")
12 | }
13 |
14 | func (a *areaCalculator) visitForCircle(s *circle) {
15 | //Calculate are for circle. After calculating the area assign in to the area instance variable
16 | fmt.Println("Calculating area for circle")
17 | }
18 |
19 | func (a *areaCalculator) visitForrectangle(s *rectangle) {
20 | //Calculate are for rectangle. After calculating the area assign in to the area instance variable
21 | fmt.Println("Calculating area for rectangle")
22 | }
23 |
--------------------------------------------------------------------------------
/behavioral/vistor/circle.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | type circle struct {
4 | radius int
5 | }
6 |
7 | func (c *circle) accept(v visitor) {
8 | v.visitForCircle(c)
9 | }
10 |
11 | func (c *circle) getType() string {
12 | return "Circle"
13 | }
14 |
--------------------------------------------------------------------------------
/behavioral/vistor/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace vistor {
3 | class areaCalculator << (S,Aquamarine) >> {
4 | - area int
5 |
6 | - visitForSquare(s *square)
7 | - visitForCircle(s *circle)
8 | - visitForrectangle(s *rectangle)
9 |
10 | }
11 | class circle << (S,Aquamarine) >> {
12 | - radius int
13 |
14 | - accept(v visitor)
15 | - getType() string
16 |
17 | }
18 | class middleCoordinates << (S,Aquamarine) >> {
19 | - x int
20 | - y int
21 |
22 | - visitForSquare(s *square)
23 | - visitForCircle(c *circle)
24 | - visitForrectangle(t *rectangle)
25 |
26 | }
27 | class rectangle << (S,Aquamarine) >> {
28 | - l int
29 | - b int
30 |
31 | - accept(v visitor)
32 | - getType() string
33 |
34 | }
35 | interface shape {
36 | - getType() string
37 | - accept( visitor)
38 |
39 | }
40 | class square << (S,Aquamarine) >> {
41 | - side int
42 |
43 | - accept(v visitor)
44 | - getType() string
45 |
46 | }
47 | interface visitor {
48 | - visitForSquare( *square)
49 | - visitForCircle( *circle)
50 | - visitForrectangle( *rectangle)
51 |
52 | }
53 | }
54 |
55 | "vistor.visitor" <|-- "vistor.areaCalculator"
56 | "vistor.shape" <|-- "vistor.circle"
57 | "vistor.visitor" <|-- "vistor.middleCoordinates"
58 | "vistor.shape" <|-- "vistor.rectangle"
59 | "vistor.shape" <|-- "vistor.square"
60 |
61 | @enduml
62 |
--------------------------------------------------------------------------------
/behavioral/vistor/middlecoordinates.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | import "fmt"
4 |
5 | type middleCoordinates struct {
6 | x int
7 | y int
8 | }
9 |
10 | func (a *middleCoordinates) visitForSquare(s *square) {
11 | //Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.
12 | fmt.Println("Calculating middle point coordinates for square")
13 | }
14 |
15 | func (a *middleCoordinates) visitForCircle(c *circle) {
16 | //Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.
17 | fmt.Println("Calculating middle point coordinates for circle")
18 | }
19 |
20 | func (a *middleCoordinates) visitForrectangle(t *rectangle) {
21 | //Calculate middle point coordinates for square. After calculating the area assign in to the x and y instance variable.
22 | fmt.Println("Calculating middle point coordinates for rectangle")
23 | }
24 |
--------------------------------------------------------------------------------
/behavioral/vistor/rectangle.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | type rectangle struct {
4 | l int
5 | b int
6 | }
7 |
8 | func (t *rectangle) accept(v visitor) {
9 | v.visitForrectangle(t)
10 | }
11 |
12 | func (t *rectangle) getType() string {
13 | return "rectangle"
14 | }
15 |
--------------------------------------------------------------------------------
/behavioral/vistor/shape.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | type shape interface {
4 | getType() string
5 | accept(visitor)
6 | }
7 |
--------------------------------------------------------------------------------
/behavioral/vistor/square.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | type square struct {
4 | side int
5 | }
6 |
7 | func (s *square) accept(v visitor) {
8 | v.visitForSquare(s)
9 | }
10 |
11 | func (s *square) getType() string {
12 | return "Square"
13 | }
14 |
--------------------------------------------------------------------------------
/behavioral/vistor/visitor.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | type visitor interface {
4 | visitForSquare(*square)
5 | visitForCircle(*circle)
6 | visitForrectangle(*rectangle)
7 | }
8 |
--------------------------------------------------------------------------------
/behavioral/vistor/vistor_test.go:
--------------------------------------------------------------------------------
1 | package vistor
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestVisitor(t *testing.T) {
9 | square := &square{side: 2}
10 | circle := &circle{radius: 3}
11 | rectangle := &rectangle{l: 2, b: 3}
12 |
13 | areaCalculator := &areaCalculator{}
14 | square.accept(areaCalculator)
15 | circle.accept(areaCalculator)
16 | rectangle.accept(areaCalculator)
17 |
18 | fmt.Println()
19 |
20 | middleCoordinates := &middleCoordinates{}
21 |
22 | square.accept(middleCoordinates)
23 | circle.accept(middleCoordinates)
24 | rectangle.accept(middleCoordinates)
25 |
26 | /*
27 | Calculating area for square
28 | Calculating area for circle
29 | Calculating area for rectangle
30 |
31 | Calculating middle point coordinates for square
32 | Calculating middle point coordinates for circle
33 | Calculating middle point coordinates for rectangle
34 | */
35 | }
36 |
--------------------------------------------------------------------------------
/creational/abstract-factory/README.md:
--------------------------------------------------------------------------------
1 | # Abstract Factory
2 |
3 | Abstract Factory Design Pattern is a creational design pattern that lets you create a family of related objects. It is an abstraction over the factory pattern. It is best explained with an example.
4 |
5 | UML Diagram:
6 |
7 | 
8 |
--------------------------------------------------------------------------------
/creational/abstract-factory/adidas.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | type adidas struct{}
4 |
5 | type adidasShoe struct {
6 | shoeItem
7 | }
8 |
9 | func (a adidasShoe) getLogo() string {
10 | return a.shoeItem.logo
11 | }
12 |
13 | type adidasShort struct {
14 | shortItem
15 | }
16 |
17 | func (a adidasShort) getLogo() string {
18 | return a.shortItem.logo
19 | }
20 |
21 | func (c *adidas) makeShoe() shoe {
22 | return &adidasShoe{
23 | shoeItem: shoeItem{
24 | logo: "adidas",
25 | size: 40,
26 | },
27 | }
28 | }
29 |
30 | func (c *adidas) makeShort() short {
31 | return &adidasShort{
32 | shortItem: shortItem{
33 | logo: "adidas",
34 | size: 170,
35 | },
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/creational/abstract-factory/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace abstractfactory {
3 | class adidas << (S,Aquamarine) >> {
4 | - makeShoe() shoe
5 | - makeShort() short
6 |
7 | }
8 | class adidasShoe << (S,Aquamarine) >> {
9 | - getLogo() string
10 |
11 | }
12 | class adidasShort << (S,Aquamarine) >> {
13 | - getLogo() string
14 |
15 | }
16 | class nike << (S,Aquamarine) >> {
17 | - makeShoe() shoe
18 | - makeShort() short
19 |
20 | }
21 | class nikeShoe << (S,Aquamarine) >> {
22 | - getLogo() string
23 |
24 | }
25 | class nikeShort << (S,Aquamarine) >> {
26 | - getLogo() string
27 |
28 | }
29 | interface shoe {
30 | - setLogo(logo string)
31 | - setSize(size int)
32 | - getLogo() string
33 | - getSize() int
34 |
35 | }
36 | class shoeItem << (S,Aquamarine) >> {
37 | - logo string
38 | - size int
39 |
40 | - setLogo(logo string)
41 | - getLost() string
42 | - setSize(size int)
43 | - getSize() int
44 |
45 | }
46 | interface short {
47 | - setLogo(logo string)
48 | - setSize(size int)
49 | - getLogo() string
50 | - getSize() int
51 |
52 | }
53 | class shortItem << (S,Aquamarine) >> {
54 | - logo string
55 | - size int
56 |
57 | - setLogo(logo string)
58 | - getLost() string
59 | - setSize(size int)
60 | - getSize() int
61 |
62 | }
63 | interface sportFactory {
64 | - makeShoe() shoe
65 | - makeShort() short
66 |
67 | }
68 | }
69 | "abstractfactory.shoeItem" *-- "abstractfactory.adidasShoe"
70 | "abstractfactory.shortItem" *-- "abstractfactory.adidasShort"
71 | "abstractfactory.shoeItem" *-- "abstractfactory.nikeShoe"
72 | "abstractfactory.shortItem" *-- "abstractfactory.nikeShort"
73 |
74 | "abstractfactory.sportFactory" <|-- "abstractfactory.adidas"
75 | "abstractfactory.sportFactory" <|-- "abstractfactory.nike"
76 |
77 | @enduml
78 |
--------------------------------------------------------------------------------
/creational/abstract-factory/nike.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | type nike struct{}
4 |
5 | type nikeShoe struct {
6 | shoeItem
7 | }
8 |
9 | func (n nikeShoe) getLogo() string {
10 | return n.shoeItem.logo
11 | }
12 |
13 | type nikeShort struct {
14 | shortItem
15 | }
16 |
17 | func (n nikeShort) getLogo() string {
18 | return n.shortItem.logo
19 | }
20 |
21 | func (c *nike) makeShoe() shoe {
22 | return &nikeShoe{
23 | shoeItem: shoeItem{
24 | logo: "nike",
25 | size: 40,
26 | },
27 | }
28 | }
29 |
30 | func (c *nike) makeShort() short {
31 | return &nikeShort{
32 | shortItem: shortItem{
33 | logo: "nike",
34 | size: 170,
35 | },
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/creational/abstract-factory/shoe.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | type shoe interface {
4 | setLogo(logo string)
5 | setSize(size int)
6 | getLogo() string
7 | getSize() int
8 | }
9 |
10 | type shoeItem struct {
11 | logo string
12 | size int
13 | }
14 |
15 | func (c *shoeItem) setLogo(logo string) {
16 | c.logo = logo
17 | }
18 |
19 | func (c *shoeItem) getLost() string {
20 | return c.logo
21 | }
22 |
23 | func (c *shoeItem) setSize(size int) {
24 | c.size = size
25 | }
26 |
27 | func (c *shoeItem) getSize() int {
28 | return c.size
29 | }
30 |
--------------------------------------------------------------------------------
/creational/abstract-factory/short.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | type short interface {
4 | setLogo(logo string)
5 | setSize(size int)
6 | getLogo() string
7 | getSize() int
8 | }
9 |
10 | type shortItem struct {
11 | logo string
12 | size int
13 | }
14 |
15 | func (c *shortItem) setLogo(logo string) {
16 | c.logo = logo
17 | }
18 |
19 | func (c *shortItem) getLost() string {
20 | return c.logo
21 | }
22 |
23 | func (c *shortItem) setSize(size int) {
24 | c.size = size
25 | }
26 |
27 | func (c *shortItem) getSize() int {
28 | return c.size
29 | }
30 |
--------------------------------------------------------------------------------
/creational/abstract-factory/sports-factory.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | type sportFactory interface {
8 | makeShoe() shoe
9 | makeShort() short
10 | }
11 |
12 | func getSportFactory(brand string) (sportFactory, error) {
13 | if brand == "adidas" {
14 | return &adidas{}, nil
15 | } else if brand == "nike" {
16 | return &nike{}, nil
17 | }
18 |
19 | return nil, errors.New("Wrong brand type passed")
20 | }
21 |
--------------------------------------------------------------------------------
/creational/abstract-factory/sports-factory_test.go:
--------------------------------------------------------------------------------
1 | package abstractfactory
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | const (
9 | adidasExample string = "adidas"
10 | nickExample string = "nike"
11 | )
12 |
13 | func TestGetSportFactory(t *testing.T) {
14 | adidas, _ := getSportFactory(adidasExample)
15 | nick, _ := getSportFactory(nickExample)
16 |
17 | adidasShoe := adidas.makeShoe()
18 | adidasShort := adidas.makeShort()
19 |
20 | nickShoe := nick.makeShoe()
21 | nickShort := nick.makeShort()
22 |
23 | printShoeDetail(adidasShoe)
24 | printShoeDetail(nickShoe)
25 |
26 | printShortDetail(adidasShort)
27 | printShortDetail(nickShort)
28 | }
29 |
30 | func printShoeDetail(s shoe) {
31 | fmt.Printf("Logo: %v\n", s.getLogo())
32 | fmt.Printf("Size: %v\n", s.getSize())
33 | }
34 |
35 | func printShortDetail(s short) {
36 | fmt.Printf("Logo: %v\n", s.getLogo())
37 | fmt.Printf("Size: %v\n", s.getSize())
38 | }
39 |
--------------------------------------------------------------------------------
/creational/builder/README.md:
--------------------------------------------------------------------------------
1 | # Builder Pattern
2 |
3 | Builder Pattern is a creational design pattern used for constructing complex objects. Below is the UML diagram.
4 |
5 | Use Builder pattern when the object constructed is big and requires multiple steps. It helps in less size of the constructor. The construction of the house becomes simple and it does not require a large constructor
6 |
7 | **UML Diagram**
8 |
9 |
10 | 
11 |
12 |
--------------------------------------------------------------------------------
/creational/builder/builder_test.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestBuilder(t *testing.T) {
9 |
10 | normalBuilder := getBuilder("normal")
11 | iglooBuilder := getBuilder("igloo")
12 |
13 | director := newDirector(normalBuilder)
14 | normalHouse := director.buildHouse()
15 |
16 | fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
17 | fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
18 | fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)
19 |
20 | director.setBuilder(iglooBuilder)
21 | iglooHouse := director.buildHouse()
22 |
23 | fmt.Printf("\nIgloo House Door Type: %s\n", iglooHouse.doorType)
24 | fmt.Printf("Igloo House Window Type: %s\n", iglooHouse.windowType)
25 | fmt.Printf("Igloo House Num Floor: %d\n", iglooHouse.floor)
26 | }
27 |
--------------------------------------------------------------------------------
/creational/builder/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace builder {
3 | class director << (S,Aquamarine) >> {
4 | - builder iBuilder
5 |
6 | - setBuilder(b iBuilder)
7 | - buildHouse() house
8 |
9 | }
10 | class house << (S,Aquamarine) >> {
11 | - windowType string
12 | - doorType string
13 | - floor int
14 |
15 | }
16 | interface iBuilder {
17 | - setWindowType()
18 | - setDoorType()
19 | - setNumFloor()
20 | - getHouse() house
21 |
22 | }
23 | class iglooBuilder << (S,Aquamarine) >> {
24 | - widowType string
25 | - doorType string
26 | - floor int
27 |
28 | - setWindowType()
29 | - setDoorType()
30 | - setNumFloor()
31 | - getHouse() house
32 |
33 | }
34 | class normalBuilder << (S,Aquamarine) >> {
35 | - widowType string
36 | - doorType string
37 | - floor int
38 |
39 | - setWindowType()
40 | - setDoorType()
41 | - setNumFloor()
42 | - getHouse() house
43 |
44 | }
45 | }
46 |
47 | "builder.iBuilder" <|-- "builder.iglooBuilder"
48 | "builder.iBuilder" <|-- "builder.normalBuilder"
49 |
50 | @enduml
51 |
--------------------------------------------------------------------------------
/creational/builder/director.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | type director struct {
4 | builder iBuilder
5 | }
6 |
7 | func newDirector(c iBuilder) *director {
8 | return &director{
9 | builder: c,
10 | }
11 | }
12 |
13 | func (c *director) setBuilder(b iBuilder) {
14 | c.builder = b
15 | }
16 |
17 | func (c *director) buildHouse() house {
18 | c.builder.setDoorType()
19 | c.builder.setWindowType()
20 | c.builder.setNumFloor()
21 |
22 | return c.builder.getHouse()
23 | }
24 |
--------------------------------------------------------------------------------
/creational/builder/house.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | type house struct {
4 | windowType string
5 | doorType string
6 | floor int
7 | }
8 |
--------------------------------------------------------------------------------
/creational/builder/ibuilder.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | type iBuilder interface {
4 | setWindowType()
5 | setDoorType()
6 | setNumFloor()
7 | getHouse() house
8 | }
9 |
10 | func getBuilder(builderType string) iBuilder {
11 | if builderType == "normal" {
12 | return &normalBuilder{}
13 | } else if builderType == "igloo" {
14 | return &iglooBuilder{}
15 | }
16 |
17 | return nil
18 | }
19 |
--------------------------------------------------------------------------------
/creational/builder/iglooBuilder.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | type iglooBuilder struct {
4 | widowType string
5 | doorType string
6 | floor int
7 | }
8 |
9 | func newIglooBuilder() *iglooBuilder {
10 | return &iglooBuilder{}
11 | }
12 |
13 | func (c *iglooBuilder) setWindowType() {
14 | c.widowType = "Wooden Window"
15 | }
16 |
17 | func (c *iglooBuilder) setDoorType() {
18 | c.doorType = "Wooden Door"
19 | }
20 |
21 | func (c *iglooBuilder) setNumFloor() {
22 | c.floor = 2
23 | }
24 |
25 | func (c *iglooBuilder) getHouse() house {
26 | return house{
27 | doorType: c.doorType,
28 | windowType: c.widowType,
29 | floor: c.floor,
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/creational/builder/normalBuilder.go:
--------------------------------------------------------------------------------
1 | package builder
2 |
3 | type normalBuilder struct {
4 | widowType string
5 | doorType string
6 | floor int
7 | }
8 |
9 | func newNormalBuilder() *normalBuilder {
10 | return &normalBuilder{}
11 | }
12 |
13 | func (c *normalBuilder) setWindowType() {
14 | c.widowType = "Wooden Window"
15 | }
16 |
17 | func (c *normalBuilder) setDoorType() {
18 | c.doorType = "Wooden Door"
19 | }
20 |
21 | func (c *normalBuilder) setNumFloor() {
22 | c.floor = 2
23 | }
24 |
25 | func (c *normalBuilder) getHouse() house {
26 | return house{
27 | doorType: c.doorType,
28 | windowType: c.widowType,
29 | floor: c.floor,
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/creational/factory-method/README.md:
--------------------------------------------------------------------------------
1 | # Factory Method
2 |
3 | Factory design pattern is a creational design pattern and it is also one of the most commonly used pattern. This pattern provides a way to hide the creation logic of the instances being created.
4 |
5 | **UML Diagram**
6 |
7 |
8 | 
--------------------------------------------------------------------------------
/creational/factory-method/ak47.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | type ak47 struct {
4 | gun // Go中不存在继承 所以使用匿名组合来实现
5 | }
6 |
7 | func newAk47() iGun {
8 | return &ak47{
9 | gun: gun{
10 | name: "AK47 gun",
11 | power: 4,
12 | },
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/creational/factory-method/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace factorymethod {
3 | class ak47 << (S,Aquamarine) >> {
4 | }
5 | class gun << (S,Aquamarine) >> {
6 | - name string
7 | - power int
8 |
9 | - setName(name string)
10 | - getName() string
11 | - setPower(power int)
12 | - getPower() int
13 |
14 | }
15 | interface iGun {
16 | - setName(name string)
17 | - setPower(power int)
18 | - getName() string
19 | - getPower() int
20 |
21 | }
22 | class maverick << (S,Aquamarine) >> {
23 | }
24 | }
25 | "factorymethod.gun" *-- "factorymethod.ak47"
26 | "factorymethod.gun" *-- "factorymethod.maverick"
27 |
28 | "factorymethod.iGun" <|-- "factorymethod.gun"
29 |
30 | @enduml
31 |
--------------------------------------------------------------------------------
/creational/factory-method/gun.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | type gun struct {
4 | name string
5 | power int
6 | }
7 |
8 | func (c *gun) setName(name string) {
9 | c.name = name
10 | }
11 |
12 | func (c *gun) getName() string {
13 | return c.name
14 | }
15 |
16 | func (c *gun) setPower(power int) {
17 | c.power = power
18 | }
19 |
20 | func (c *gun) getPower() int {
21 | return c.power
22 | }
23 |
--------------------------------------------------------------------------------
/creational/factory-method/gunfactory.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | import (
4 | "errors"
5 | )
6 |
7 | func getGun(gunType string) (iGun, error) {
8 | if gunType == "ak47" {
9 | return newAk47(), nil
10 | } else if gunType == "maverick" {
11 | return newMaverick(), nil
12 | }
13 |
14 | return nil, errors.New("Wrong gun type passed")
15 | }
16 |
--------------------------------------------------------------------------------
/creational/factory-method/gunfactory_test.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestGunFactory(t *testing.T) {
9 | ak47, _ := getGun("ak47")
10 | maverick, _ := getGun("maverick")
11 | printDetails(ak47)
12 | printDetails(maverick)
13 | }
14 |
15 | func printDetails(g iGun) {
16 | fmt.Printf("Gun: %s", g.getName())
17 | fmt.Println()
18 | fmt.Printf("Power: %d", g.getPower())
19 | fmt.Println()
20 | }
21 |
--------------------------------------------------------------------------------
/creational/factory-method/igun.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | type iGun interface {
4 | setName(name string)
5 | setPower(power int)
6 | getName() string
7 | getPower() int
8 | }
9 |
--------------------------------------------------------------------------------
/creational/factory-method/maverick.go:
--------------------------------------------------------------------------------
1 | package factorymethod
2 |
3 | type maverick struct {
4 | gun // Go中不存在继承 所以使用匿名组合来实现
5 | }
6 |
7 | func newMaverick() iGun {
8 | return &maverick{
9 | gun: gun{
10 | name: "Maverick gun",
11 | power: 4,
12 | },
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/creational/prototype/README.md:
--------------------------------------------------------------------------------
1 | # Prototype
2 |
3 | It is a creational design pattern that lets you create copies of objects. In this pattern, the responsibility of creating the clone objects is delegated to the actual object to clone.
4 |
5 | The object to be cloned exposes a clone method which returns a clone copy of the object
6 |
7 | **UML Diagram**
8 |
9 |
10 | 
11 |
12 |
--------------------------------------------------------------------------------
/creational/prototype/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace prototype {
3 | class file << (S,Aquamarine) >> {
4 | - name string
5 |
6 | - print(indentation string)
7 | - clone() inode
8 |
9 | }
10 | class folder << (S,Aquamarine) >> {
11 | - childrens []inode
12 | - name string
13 |
14 | - print(indentation string)
15 | - clone() inode
16 |
17 | }
18 | interface inode {
19 | - print( string)
20 | - clone() inode
21 |
22 | }
23 | }
24 |
25 | "prototype.inode" <|-- "prototype.file"
26 | "prototype.inode" <|-- "prototype.folder"
27 |
28 | @enduml
29 |
--------------------------------------------------------------------------------
/creational/prototype/file.go:
--------------------------------------------------------------------------------
1 | package prototype
2 |
3 | import "fmt"
4 |
5 | type file struct {
6 | name string
7 | }
8 |
9 | func (f *file) print(indentation string) {
10 | fmt.Println(indentation + f.name + "_clone")
11 | }
12 |
13 | func (f *file) clone() inode {
14 | return &file{name: f.name}
15 | }
16 |
--------------------------------------------------------------------------------
/creational/prototype/file_test.go:
--------------------------------------------------------------------------------
1 | package prototype
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestFile(t *testing.T) {
9 | file1 := &file{name: "File1"}
10 | file2 := &file{name: "File2"}
11 | file3 := &file{name: "File3"}
12 | folder1 := &folder{
13 | childrens: []inode{file1},
14 | name: "Folder1",
15 | }
16 | folder2 := &folder{
17 | childrens: []inode{folder1, file2, file3},
18 | name: "Folder2",
19 | }
20 | fmt.Println("\nPrinting hierarchy for Folder2")
21 | folder2.print(" ")
22 | cloneFolder := folder2.clone()
23 | fmt.Println("\nPrinting hierarchy for clone Folder")
24 | cloneFolder.print(" ")
25 | }
26 |
--------------------------------------------------------------------------------
/creational/prototype/folder.go:
--------------------------------------------------------------------------------
1 | package prototype
2 |
3 | import "fmt"
4 |
5 | type folder struct {
6 | childrens []inode
7 | name string
8 | }
9 |
10 | func (f *folder) print(indentation string) {
11 | fmt.Println(indentation + f.name + "_clone")
12 | for _, i := range f.childrens {
13 | i.print(indentation + indentation)
14 | }
15 | }
16 |
17 | func (f *folder) clone() inode {
18 | cloneFolder := &folder{name: f.name}
19 | var tempChildrens []inode
20 | for _, i := range f.childrens {
21 | copy := i.clone()
22 | tempChildrens = append(tempChildrens, copy)
23 | }
24 | cloneFolder.childrens = tempChildrens
25 | return cloneFolder
26 | }
27 |
--------------------------------------------------------------------------------
/creational/prototype/inode.go:
--------------------------------------------------------------------------------
1 | package prototype
2 |
3 | type inode interface {
4 | print(string)
5 | clone() inode
6 | }
7 |
--------------------------------------------------------------------------------
/creational/singleton/README.md:
--------------------------------------------------------------------------------
1 | # Singleton
2 |
3 | Singleton Design Pattern is a creational design pattern and also one of the most commonly used design pattern. This pattern is used when only a single instance of the struct should exist. This single instance is called a singleton object. Some of the cases where the singleton object is applicable:
4 |
5 | 1. **DB instance** – we only want to create only one instance of DB object and that instance will be used throughout the application.
6 | 2. **Logger instance** – again only one instance of the logger should be created and it should be used throughout the application.
7 |
8 | The singleton instance is created when the struct is first initialized. Usually, there is getInstance() method defined on the struct for which only one instance needs to be created. Once created then the same singleton instance is returned every time by the getInstance().
9 |
10 | UML Diagram
11 |
12 | 
--------------------------------------------------------------------------------
/creational/singleton/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace singleton {
3 | class single << (S,Aquamarine) >> {
4 | }
5 | }
6 |
7 |
8 | @enduml
9 |
--------------------------------------------------------------------------------
/creational/singleton/singleton.go:
--------------------------------------------------------------------------------
1 | package singleton
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var once sync.Once
8 |
9 | type single struct{}
10 |
11 | var singleInstance *single
12 |
13 | func getInstance() *single {
14 | if singleInstance == nil {
15 | once.Do(func() {
16 | singleInstance = &single{}
17 | })
18 | }
19 | return singleInstance
20 | }
21 |
22 | /*
23 |
24 | // var lock = &sync.Mutex{}
25 |
26 | func getInstance() *single {
27 | if singleInstance == nil {
28 | lock.Lock()
29 | defer lock.Unlock()
30 | if singleInstance == nil {
31 | fmt.Println("Creting Single Instance Now")
32 | singleInstance = &single{}
33 | } else {
34 | fmt.Println("Single Instance already created-1")
35 | }
36 | } else {
37 | fmt.Println("Single Instance already created-2")
38 | }
39 | return singleInstance
40 | }*/
41 |
--------------------------------------------------------------------------------
/creational/singleton/singleton_test.go:
--------------------------------------------------------------------------------
1 | package singleton
2 |
3 | import (
4 | "sync"
5 | "testing"
6 | )
7 |
8 | func TestSingleton(t *testing.T) {
9 | bar := getInstance()
10 | baz := getInstance()
11 |
12 | if bar != baz {
13 | t.Fatal("getInstance is not equal")
14 | }
15 | }
16 |
17 | func TestParalleSingleton(t *testing.T) {
18 | wg := sync.WaitGroup{}
19 | num := 100
20 | instances := make([]*single, num)
21 | wg.Add(num)
22 | for i := 0; i < num; i++ {
23 | go func(idx int) {
24 | instances[idx] = getInstance()
25 | wg.Done()
26 | }(i)
27 | }
28 | wg.Wait()
29 | for i := 1; i < num; i++ {
30 | if instances[i-1] != instances[i] {
31 | t.Fatal("instance is not equal")
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/image/Adapter-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Adapter-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Bridge-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Bridge-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Builder-Design-Patter.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Builder-Design-Patter.jpg
--------------------------------------------------------------------------------
/image/Chain-of-Responsibility-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Chain-of-Responsibility-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Command-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Command-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Composite-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Composite-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Facade-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Facade-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Factory-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Factory-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Flyweight-Design-Pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Flyweight-Design-Pattern.png
--------------------------------------------------------------------------------
/image/Iterator-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Iterator-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Observer-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Observer-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Prototype-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Prototype-Pattern.jpg
--------------------------------------------------------------------------------
/image/Proxy-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Proxy-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/State-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/State-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Strategy-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Strategy-Design-Pattern.jpg
--------------------------------------------------------------------------------
/image/Visitor-Design-Pattern.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aierui/design-pattern-in-go/d867a5bfd0cb32aa87f45a20ef1fd03b469ae7b4/image/Visitor-Design-Pattern.jpg
--------------------------------------------------------------------------------
/images/behavioral/iterator/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/behavioral/memento/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/behavioral/observer/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/creational/factory-method/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/creational/prototype/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/creational/singleton/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/structural/adapter/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/structural/composite/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/structural/flyweight/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/structural/proxy/diagram/diagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/structural/adapter/README.md:
--------------------------------------------------------------------------------
1 | # Adapter
2 |
3 | This design pattern is a Structural Design Pattern. The patter is best understood with an example. Let’s say you have two laptops
4 |
5 | MacBook Pro
6 | Windows Laptop
7 |
8 | MacBook Pro has a USB port that is square in shape and Windows have a USB port that is circular in shape. You as a client have a USB cable that is square in shape so it can only be inserted in the mac laptop. So you see the problem here
9 |
10 | Problem:
11 |
12 | We have a class (Client) that is expecting some features of an object (square USB port here), but we have another object called adaptee (Windows Laptop here) which offers the same functionality but through a different interface( circular port)
13 | This is where Adapter Pattern comes into the picture. We create a class known as Adapter that will
14 |
15 | Adhere to the same interface which client expects ( Square USB port here)
16 | Translate the request from the client to the adaptee in the form that adaptee expects. Basically, in our example act as an adapter that accepts USB in square port and then inserts into circular port in windows laptop.
17 |
18 | When to Use?
19 | - Use this design pattern when the objects implement a different interface as required by the client.
20 |
21 | UML Diagram:
22 |
23 | 
24 |
25 |
--------------------------------------------------------------------------------
/structural/adapter/adapter_test.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import "testing"
4 |
5 | func TestAdapter(t *testing.T) {
6 | client := &client{}
7 | mac := &mac{}
8 | client.insertSquareUsbInComputer(mac)
9 | windowsMachine := &windows{}
10 | windowsMachineAdapter := &windowsAdapter{
11 | windowMachine: windowsMachine,
12 | }
13 | client.insertSquareUsbInComputer(windowsMachineAdapter)
14 |
15 | /*
16 | Insert square port into mac machine
17 | Insert circle port into windows machine
18 | */
19 | }
20 |
--------------------------------------------------------------------------------
/structural/adapter/client.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | type client struct {
4 | }
5 |
6 | func (c *client) insertSquareUsbInComputer(com computer) {
7 | com.insertInSquarePort()
8 | }
9 |
--------------------------------------------------------------------------------
/structural/adapter/computer.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | type computer interface {
4 | insertInSquarePort()
5 | }
6 |
--------------------------------------------------------------------------------
/structural/adapter/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace adapter {
3 | class client << (S,Aquamarine) >> {
4 | - insertSquareUsbInComputer(com computer)
5 |
6 | }
7 | interface computer {
8 | - insertInSquarePort()
9 |
10 | }
11 | class mac << (S,Aquamarine) >> {
12 | - insertInSquarePort()
13 |
14 | }
15 | class windows << (S,Aquamarine) >> {
16 | - insertInCirclePort()
17 |
18 | }
19 | class windowsAdapter << (S,Aquamarine) >> {
20 | - windowMachine *windows
21 |
22 | - insertInSquarePort()
23 |
24 | }
25 | }
26 |
27 | "adapter.computer" <|-- "adapter.mac"
28 | "adapter.computer" <|-- "adapter.windowsAdapter"
29 |
30 | @enduml
31 |
--------------------------------------------------------------------------------
/structural/adapter/mac.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import "fmt"
4 |
5 | type mac struct {
6 | }
7 |
8 | func (m *mac) insertInSquarePort() {
9 | fmt.Println("Insert square port into mac machine")
10 | }
11 |
--------------------------------------------------------------------------------
/structural/adapter/windows.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | import "fmt"
4 |
5 | type windows struct{}
6 |
7 | func (w *windows) insertInCirclePort() {
8 | fmt.Println("Insert circle port into windows machine")
9 | }
10 |
--------------------------------------------------------------------------------
/structural/adapter/windowsadapter.go:
--------------------------------------------------------------------------------
1 | package adapter
2 |
3 | type windowsAdapter struct {
4 | windowMachine *windows
5 | }
6 |
7 | func (w *windowsAdapter) insertInSquarePort() {
8 | w.windowMachine.insertInCirclePort()
9 | }
10 |
--------------------------------------------------------------------------------
/structural/bridge/README.md:
--------------------------------------------------------------------------------
1 | # Bridge
2 |
3 | Bridge design pattern is a structural design pattern that allows the separation of abstraction from its implementation. Sounds confusing? Don’t worry, it will be more clear as we go along.
4 |
5 | This pattern suggests dividing a large class into two separate hierarchy
6 |
7 | - Abstraction – It is an interface and children of the Abstraction are referred to as Refined Abstraction. The abstraction contains a reference to the implementation.
8 | - Implementation – It is also an interface and children’s of the Implementation are referred to as Concrete Implementation
9 |
10 | UML Diagram:
11 |
12 | 
--------------------------------------------------------------------------------
/structural/bridge/bridge_test.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestBridge(t *testing.T) {
9 | hpPrinter := &hp{}
10 | epsonPrinter := &epson{}
11 | macComputer := &mac{}
12 | macComputer.setPrinter(hpPrinter)
13 | macComputer.print()
14 |
15 | fmt.Println()
16 |
17 | macComputer.setPrinter(epsonPrinter)
18 | macComputer.print()
19 |
20 | fmt.Println()
21 |
22 | winComputer := &windows{}
23 | winComputer.setPrinter(hpPrinter)
24 | winComputer.print()
25 |
26 | fmt.Println()
27 |
28 | winComputer.setPrinter(epsonPrinter)
29 | winComputer.print()
30 | fmt.Println()
31 |
32 | /*
33 | Print request for mac
34 | Printing by a HP Printer
35 |
36 | Print request for mac
37 | Printing by a EPSON Printer
38 |
39 | Print request for windows
40 | Printing by a HP Printer
41 |
42 | Print request for windows
43 | Printing by a EPSON Printer
44 | */
45 | }
46 |
--------------------------------------------------------------------------------
/structural/bridge/computer.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | type computer interface {
4 | print()
5 | setPrinter(printer)
6 | }
7 |
--------------------------------------------------------------------------------
/structural/bridge/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace bridge {
3 | interface computer {
4 | - print()
5 | - setPrinter( printer)
6 |
7 | }
8 | class epson << (S,Aquamarine) >> {
9 | - printFile()
10 |
11 | }
12 | class hp << (S,Aquamarine) >> {
13 | - printFile()
14 |
15 | }
16 | class mac << (S,Aquamarine) >> {
17 | - printer printer
18 |
19 | - print()
20 | - setPrinter(p printer)
21 |
22 | }
23 | interface printer {
24 | - printFile()
25 |
26 | }
27 | class windows << (S,Aquamarine) >> {
28 | - printer printer
29 |
30 | - print()
31 | - setPrinter(p printer)
32 |
33 | }
34 | }
35 |
36 | "bridge.printer" <|-- "bridge.epson"
37 | "bridge.printer" <|-- "bridge.hp"
38 | "bridge.computer" <|-- "bridge.mac"
39 | "bridge.computer" <|-- "bridge.windows"
40 |
41 | @enduml
42 |
--------------------------------------------------------------------------------
/structural/bridge/epson.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | import "fmt"
4 |
5 | type epson struct {
6 | }
7 |
8 | func (p *epson) printFile() {
9 | fmt.Println("Printing by a EPSON Printer")
10 | }
11 |
--------------------------------------------------------------------------------
/structural/bridge/hp.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | import "fmt"
4 |
5 | type hp struct {
6 | }
7 |
8 | func (p *hp) printFile() {
9 | fmt.Println("Printing by a HP Printer")
10 | }
11 |
--------------------------------------------------------------------------------
/structural/bridge/mac.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | import "fmt"
4 |
5 | type mac struct {
6 | printer printer
7 | }
8 |
9 | func (m *mac) print() {
10 | fmt.Println("Print request for mac")
11 | m.printer.printFile()
12 | }
13 |
14 | func (m *mac) setPrinter(p printer) {
15 | m.printer = p
16 | }
17 |
--------------------------------------------------------------------------------
/structural/bridge/printer.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | type printer interface {
4 | printFile()
5 | }
6 |
--------------------------------------------------------------------------------
/structural/bridge/win.go:
--------------------------------------------------------------------------------
1 | package bridge
2 |
3 | import "fmt"
4 |
5 | type windows struct {
6 | printer printer
7 | }
8 |
9 | func (w *windows) print() {
10 | fmt.Println("Print request for windows")
11 | w.printer.printFile()
12 | }
13 |
14 | func (w *windows) setPrinter(p printer) {
15 | w.printer = p
16 | }
17 |
--------------------------------------------------------------------------------
/structural/composite/README.md:
--------------------------------------------------------------------------------
1 | # Composite
2 |
3 | This is a structural design pattern. Composition design pattern is used when we want a Group of objects called ‘composite’ is treated in a similar way as a single object. It comes under structural design pattern as it allows you to compose objects into a tree structure. Each of the individual objects in the tree structure can be treated in the same way irrespective of whether they are Complex or Primitive.
4 |
5 | When to Use?
6 |
7 | - Composite Design pattern makes sense to use in cases when the composite and individual object needs to be treated in the same way from a client perspective.
8 | - Use this pattern when the composite and individual object form a tree-like structure
9 |
10 | UML Diagram:
11 |
12 |
13 | 
--------------------------------------------------------------------------------
/structural/composite/component.go:
--------------------------------------------------------------------------------
1 | package composite
2 |
3 | type component interface {
4 | search(string)
5 | }
6 |
--------------------------------------------------------------------------------
/structural/composite/composite_test.go:
--------------------------------------------------------------------------------
1 | package composite
2 |
3 | import "testing"
4 |
5 | func TestComposite(t *testing.T) {
6 | file1 := &file{name: "File1"}
7 | file2 := &file{name: "File2"}
8 | file3 := &file{name: "File3"}
9 | folder1 := &folder{
10 | name: "Folder1",
11 | }
12 | folder1.add(file1)
13 |
14 | folder2 := &folder{
15 | name: "Folder2",
16 | }
17 | folder2.add(file2)
18 | folder2.add(file3)
19 | folder2.add(folder1)
20 |
21 | folder2.search("rose")
22 |
23 | /*
24 | Searching recursively for keyword rose in folder Folder2
25 | Searching for keyword rose in file File2
26 | Searching for keyword rose in file File3
27 | Searching recursively for keyword rose in folder Folder1
28 | Searching for keyword rose in file File1
29 | */
30 | }
31 |
--------------------------------------------------------------------------------
/structural/composite/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace composite {
3 | interface component {
4 | - search( string)
5 |
6 | }
7 | class file << (S,Aquamarine) >> {
8 | - name string
9 |
10 | - search(keyword string)
11 | - getName() string
12 |
13 | }
14 | class folder << (S,Aquamarine) >> {
15 | - components []component
16 | - name string
17 |
18 | - search(keyword string)
19 | - add(c component)
20 |
21 | }
22 | }
23 |
24 | "composite.component" <|-- "composite.file"
25 | "composite.component" <|-- "composite.folder"
26 |
27 | @enduml
28 |
--------------------------------------------------------------------------------
/structural/composite/file.go:
--------------------------------------------------------------------------------
1 | package composite
2 |
3 | import "fmt"
4 |
5 | type file struct {
6 | name string
7 | }
8 |
9 | func (f *file) search(keyword string) {
10 | fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
11 | }
12 |
13 | func (f *file) getName() string {
14 | return f.name
15 | }
16 |
--------------------------------------------------------------------------------
/structural/composite/folder.go:
--------------------------------------------------------------------------------
1 | package composite
2 |
3 | import "fmt"
4 |
5 | type folder struct {
6 | components []component
7 | name string
8 | }
9 |
10 | func (f *folder) search(keyword string) {
11 | fmt.Printf("Searching recursively for keyword %s in folder %s\n", keyword, f.name)
12 | for _, composite := range f.components {
13 | composite.search(keyword)
14 | }
15 | }
16 |
17 | func (f *folder) add(c component) {
18 | f.components = append(f.components, c)
19 | }
20 |
--------------------------------------------------------------------------------
/structural/decorator/README.md:
--------------------------------------------------------------------------------
1 | # Decorator
2 |
--------------------------------------------------------------------------------
/structural/decorator/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | @enduml
3 |
--------------------------------------------------------------------------------
/structural/facade/README.md:
--------------------------------------------------------------------------------
1 | # Facade
2 |
3 | Facade Pattern is classified as a structural design pattern. This design pattern is meant to hide the complexities of the underlying system and provide a simple interface to the client. It provides a unified interface to underlying many interfaces in the system so that from the client perspective it is easier to use. Basically it provides a higher level abstraction over a complicated system.
4 |
5 | The term Facade itself means
6 |
7 | the principal front of a building, that faces on to a street or open space
8 |
9 | Only the front face of the building is shown all the underlying complexity is hidden behind.
10 |
11 | When to Use?
12 | - When you want to expose a complex system in a simplified way.
13 |
14 | UML Diagram:
15 |
16 |
17 | 
--------------------------------------------------------------------------------
/structural/facade/account.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type account struct {
6 | name string
7 | }
8 |
9 | func newAccount(accountName string) *account {
10 | return &account{
11 | name: accountName,
12 | }
13 | }
14 |
15 | func (a *account) checkAccount(accountName string) error {
16 | if a.name != accountName {
17 | return fmt.Errorf("Account Name is incorrect")
18 | }
19 | fmt.Println("Account Verified")
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/structural/facade/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace facade {
3 | class account << (S,Aquamarine) >> {
4 | - name string
5 |
6 | - checkAccount(accountName string) error
7 |
8 | }
9 | class ledger << (S,Aquamarine) >> {
10 | - makeEntry(accountID string, txnType string, amount int)
11 |
12 | }
13 | class notification << (S,Aquamarine) >> {
14 | - sendWalletCreditNotification()
15 | - sendWalletDebitNotification()
16 |
17 | }
18 | class securityCode << (S,Aquamarine) >> {
19 | - code int
20 |
21 | - checkCode(incomingCode int) error
22 |
23 | }
24 | class wallet << (S,Aquamarine) >> {
25 | - balance int
26 |
27 | - creditBalance(amount int)
28 | - debitBalance(amount int) error
29 |
30 | }
31 | class walletFacade << (S,Aquamarine) >> {
32 | - account *account
33 | - wallet *wallet
34 | - securityCode *securityCode
35 | - notification *notification
36 | - ledger *ledger
37 |
38 | - addMoneyToWallet(accountID string, securityCode int, amount int) error
39 | - deductMoneyFromWallet(accountID string, securityCode int, amount int) error
40 |
41 | }
42 | }
43 |
44 |
45 | @enduml
46 |
--------------------------------------------------------------------------------
/structural/facade/facade_test.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "testing"
7 | )
8 |
9 | func TestFacade(t *testing.T) {
10 | fmt.Println()
11 | walletFacade := newWalletFacade("abc", 1234)
12 | fmt.Println()
13 | err := walletFacade.addMoneyToWallet("abc", 1234, 10)
14 | if err != nil {
15 | log.Fatalf("Error: %s\n", err.Error())
16 | }
17 | fmt.Println()
18 | err = walletFacade.deductMoneyFromWallet("ab", 1234, 5)
19 | if err != nil {
20 | log.Fatalf("Error: %s\n", err.Error())
21 | }
22 |
23 | /*
24 | Starting create account
25 | Account created
26 |
27 | Starting add money to wallet
28 | Account Verified
29 | SecurityCode Verified
30 | Wallet balance added successfully
31 | Sending wallet credit notification
32 | Make ledger entry for accountId abc with txnType credit for amount 10
33 | Starting debit money from wallet
34 | 2020/06/26 00:59:27 Error: Account Name is incorrect
35 | */
36 | }
37 |
--------------------------------------------------------------------------------
/structural/facade/ledger.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type ledger struct {
6 | }
7 |
8 | func (s *ledger) makeEntry(accountID, txnType string, amount int) {
9 | fmt.Printf("Make ledger entry for accountId %s with txnType %s for amount %d", accountID, txnType, amount)
10 | return
11 | }
12 |
--------------------------------------------------------------------------------
/structural/facade/notification.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type notification struct {
6 | }
7 |
8 | func (n *notification) sendWalletCreditNotification() {
9 | fmt.Println("Sending wallet credit notification")
10 | }
11 |
12 | func (n *notification) sendWalletDebitNotification() {
13 | fmt.Println("Sending wallet debit notification")
14 | }
15 |
--------------------------------------------------------------------------------
/structural/facade/securitycode.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type securityCode struct {
6 | code int
7 | }
8 |
9 | func newSecurityCode(code int) *securityCode {
10 | return &securityCode{
11 | code: code,
12 | }
13 | }
14 |
15 | func (s *securityCode) checkCode(incomingCode int) error {
16 | if s.code != incomingCode {
17 | return fmt.Errorf("Security Code is incorrect")
18 | }
19 | fmt.Println("SecurityCode Verified")
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/structural/facade/wallet.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type wallet struct {
6 | balance int
7 | }
8 |
9 | func newWallet() *wallet {
10 | return &wallet{
11 | balance: 0,
12 | }
13 | }
14 |
15 | func (w *wallet) creditBalance(amount int) {
16 | w.balance += amount
17 | fmt.Println("Wallet balance added successfully")
18 | return
19 | }
20 |
21 | func (w *wallet) debitBalance(amount int) error {
22 | if w.balance < amount {
23 | return fmt.Errorf("Balance is not sufficient")
24 | }
25 | fmt.Println("Wallet balance is Sufficient")
26 | w.balance = w.balance - amount
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/structural/facade/walletfacade.go:
--------------------------------------------------------------------------------
1 | package facade
2 |
3 | import "fmt"
4 |
5 | type walletFacade struct {
6 | account *account
7 | wallet *wallet
8 | securityCode *securityCode
9 | notification *notification
10 | ledger *ledger
11 | }
12 |
13 | func newWalletFacade(accountID string, code int) *walletFacade {
14 | fmt.Println("Starting create account")
15 | walletFacacde := &walletFacade{
16 | account: newAccount(accountID),
17 | securityCode: newSecurityCode(code),
18 | wallet: newWallet(),
19 | notification: ¬ification{},
20 | ledger: &ledger{},
21 | }
22 | fmt.Println("Account created")
23 | return walletFacacde
24 | }
25 |
26 | func (w *walletFacade) addMoneyToWallet(accountID string, securityCode int, amount int) error {
27 | fmt.Println("Starting add money to wallet")
28 | err := w.account.checkAccount(accountID)
29 | if err != nil {
30 | return err
31 | }
32 | err = w.securityCode.checkCode(securityCode)
33 | if err != nil {
34 | return err
35 | }
36 | w.wallet.creditBalance(amount)
37 | w.notification.sendWalletCreditNotification()
38 | w.ledger.makeEntry(accountID, "credit", amount)
39 | return nil
40 | }
41 |
42 | func (w *walletFacade) deductMoneyFromWallet(accountID string, securityCode int, amount int) error {
43 | fmt.Println("Starting debit money from wallet")
44 | err := w.account.checkAccount(accountID)
45 | if err != nil {
46 | return err
47 | }
48 | err = w.securityCode.checkCode(securityCode)
49 | if err != nil {
50 | return err
51 | }
52 | err = w.wallet.debitBalance(amount)
53 | if err != nil {
54 | return err
55 | }
56 | w.notification.sendWalletDebitNotification()
57 | w.ledger.makeEntry(accountID, "credit", amount)
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/structural/flyweight/README.md:
--------------------------------------------------------------------------------
1 | # Flyweight
2 |
3 | It is a structural design pattern. This pattern is used when a large number of similar objects need to be created. These objects are called flyweight objects and are immutable.
4 |
5 | When to Use?
6 | When the objects have some intrinsic properties which can be shared.
7 | As in the above example, dress is the intrinsic property that was taken out and shared.
8 | Use flyweight when a large number of objects needs to be created which can cause memory issue. In case figure out all the common or intrinsic state and create flyweight objects for that.
9 |
10 | UML Diagram:
11 |
12 |
13 | 
--------------------------------------------------------------------------------
/structural/flyweight/counterterroristdress.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | type counterTerroristDress struct {
4 | color string
5 | }
6 |
7 | func (c *counterTerroristDress) getColor() string {
8 | return c.color
9 | }
10 |
11 | func newCounterTerroristDress() *counterTerroristDress {
12 | return &counterTerroristDress{color: "green"}
13 | }
14 |
--------------------------------------------------------------------------------
/structural/flyweight/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace flyweight {
3 | class counterTerroristDress << (S,Aquamarine) >> {
4 | - color string
5 |
6 | - getColor() string
7 |
8 | }
9 | interface dress {
10 | - getColor() string
11 |
12 | }
13 | class dressFactory << (S,Aquamarine) >> {
14 | - dressMap map[string]dress
15 | - dressMap map[string]dress
16 |
17 | - getDressByType(dressType string) (dress, error)
18 | - getDressByType(dressType string) (dress, error)
19 |
20 | }
21 | class player << (S,Aquamarine) >> {
22 | - dress dress
23 | - playerType string
24 | - lat int
25 | - long int
26 |
27 | - newLocation(lat int, long int)
28 |
29 | }
30 | }
31 |
32 | "flyweight.dress" <|-- "flyweight.counterTerroristDress"
33 |
34 | @enduml
35 |
--------------------------------------------------------------------------------
/structural/flyweight/dress.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | type dress interface {
4 | getColor() string
5 | }
6 |
--------------------------------------------------------------------------------
/structural/flyweight/dressfactory.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | import "fmt"
4 |
5 | const (
6 | //TerroristDressType terrorist dress type
7 | TerroristDressType = "tDress"
8 | //CounterTerrroristDressType terrorist dress type
9 | CounterTerrroristDressType = "ctDress"
10 | )
11 |
12 | var (
13 | dressFactorySingleInstance = &dressFactory{
14 | dressMap: make(map[string]dress),
15 | }
16 | )
17 |
18 | type dressFactory struct {
19 | dressMap map[string]dress
20 | }
21 |
22 | func (d *dressFactory) getDressByType(dressType string) (dress, error) {
23 | if d.dressMap[dressType] != nil {
24 | return d.dressMap[dressType], nil
25 | }
26 | if dressType == TerroristDressType {
27 | d.dressMap[dressType] = newTerroristDress()
28 | return d.dressMap[dressType], nil
29 | }
30 | if dressType == CounterTerrroristDressType {
31 | d.dressMap[dressType] = newCounterTerroristDress()
32 | return d.dressMap[dressType], nil
33 | }
34 | return nil, fmt.Errorf("Wrong dress type passed")
35 | }
36 |
37 | func getDressFactorySingleInstance() *dressFactory {
38 | return dressFactorySingleInstance
39 | }
40 |
--------------------------------------------------------------------------------
/structural/flyweight/flyweight_test.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestFlyweight(t *testing.T) {
9 | game := newGame()
10 | //Add Terrorist
11 | game.addTerrorist(TerroristDressType)
12 | game.addTerrorist(TerroristDressType)
13 | game.addTerrorist(TerroristDressType)
14 | game.addTerrorist(TerroristDressType)
15 | //Add CounterTerrorist
16 | game.addCounterTerrorist(CounterTerrroristDressType)
17 | game.addCounterTerrorist(CounterTerrroristDressType)
18 | game.addCounterTerrorist(CounterTerrroristDressType)
19 | dressFactoryInstance := getDressFactorySingleInstance()
20 | for dressType, dress := range dressFactoryInstance.dressMap {
21 | fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/structural/flyweight/player.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | type player struct {
4 | dress dress
5 | playerType string
6 | lat int
7 | long int
8 | }
9 |
10 | func newPlayer(playerType, dressType string) *player {
11 | dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
12 | return &player{
13 | playerType: playerType,
14 | dress: dress,
15 | }
16 | }
17 |
18 | func (p *player) newLocation(lat, long int) {
19 | p.lat = lat
20 | p.long = long
21 | }
22 |
--------------------------------------------------------------------------------
/structural/flyweight/terroristdress.go:
--------------------------------------------------------------------------------
1 | package flyweight
2 |
3 | import "fmt"
4 |
5 | const (
6 | //TerroristDressType terrorist dress type
7 | TerroristDressType = "tDress"
8 | //CounterTerrroristDressType terrorist dress type
9 | CounterTerrroristDressType = "ctDress"
10 | )
11 |
12 | var (
13 | dressFactorySingleInstance = &dressFactory{
14 | dressMap: make(map[string]dress),
15 | }
16 | )
17 |
18 | type dressFactory struct {
19 | dressMap map[string]dress
20 | }
21 |
22 | func (d *dressFactory) getDressByType(dressType string) (dress, error) {
23 | if d.dressMap[dressType] != nil {
24 | return d.dressMap[dressType], nil
25 | }
26 | if dressType == TerroristDressType {
27 | d.dressMap[dressType] = newTerroristDress()
28 | return d.dressMap[dressType], nil
29 | }
30 | if dressType == CounterTerrroristDressType {
31 | d.dressMap[dressType] = newCounterTerroristDress()
32 | return d.dressMap[dressType], nil
33 | }
34 | return nil, fmt.Errorf("Wrong dress type passed")
35 | }
36 |
37 | func getDressFactorySingleInstance() *dressFactory {
38 | return dressFactorySingleInstance
39 | }
40 |
--------------------------------------------------------------------------------
/structural/proxy/README.md:
--------------------------------------------------------------------------------
1 | # Proxy
2 |
3 | Proxy Design Pattern is a structural design pattern. This pattern suggests providing an extra layer of indirection for controlled and intelligent access to the main object.
4 |
5 | In this pattern, a new proxy class is created that implements the same interface as the main object. This lets you execute some behavior before the actual logic of the main object. Let’s understand it more with an example
6 |
7 | 1. A debit card is a proxy for your bank account. It follows the same interface as the bank account, and it is easier to use.
8 | 2. A web server such as Nginx can act as a proxy for your application server. It provides
9 | - Controlled access to your application server – For example, it can do rate limiting
10 | - Additional behavior – For example, it can do some caching
11 |
12 | UML Diagram:
13 |
14 |
15 | 
--------------------------------------------------------------------------------
/structural/proxy/application.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | type application struct {
4 | }
5 |
6 | func (a *application) handleRequest(url, method string) (int, string) {
7 | if url == "/app/status" && method == "GET" {
8 | return 200, "Ok"
9 | }
10 | if url == "/create/user" && method == "POST" {
11 | return 201, "User Created"
12 | }
13 | return 404, "Not Ok"
14 | }
15 |
--------------------------------------------------------------------------------
/structural/proxy/diagram.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | namespace proxy {
3 | class application << (S,Aquamarine) >> {
4 | - handleRequest(url string, method string) (int, string)
5 |
6 | }
7 | class nginx << (S,Aquamarine) >> {
8 | - application *application
9 | - maxAllowedRequest int
10 | - rateLimiter map[string]int
11 |
12 | - handleRequest(url string, method string) (int, string)
13 | - checkRateLimiting(url string) bool
14 |
15 | }
16 | interface server {
17 | - handleRequest( string, string) (int, string)
18 |
19 | }
20 | }
21 |
22 | "proxy.server" <|-- "proxy.application"
23 | "proxy.server" <|-- "proxy.nginx"
24 |
25 | @enduml
26 |
--------------------------------------------------------------------------------
/structural/proxy/nginx.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | type nginx struct {
4 | application *application
5 | maxAllowedRequest int
6 | rateLimiter map[string]int
7 | }
8 |
9 | func newNginxServer() *nginx {
10 | return &nginx{
11 | application: &application{},
12 | maxAllowedRequest: 2,
13 | rateLimiter: make(map[string]int),
14 | }
15 | }
16 |
17 | func (n *nginx) handleRequest(url, method string) (int, string) {
18 | allowed := n.checkRateLimiting(url)
19 | if !allowed {
20 | return 403, "Not Allowed"
21 | }
22 | return n.application.handleRequest(url, method)
23 | }
24 |
25 | func (n *nginx) checkRateLimiting(url string) bool {
26 | if n.rateLimiter[url] == 0 {
27 | n.rateLimiter[url] = 1
28 | }
29 | if n.rateLimiter[url] > n.maxAllowedRequest {
30 | return false
31 | }
32 | n.rateLimiter[url] = n.rateLimiter[url] + 1
33 | return true
34 | }
35 |
--------------------------------------------------------------------------------
/structural/proxy/proxy_test.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestProxy(t *testing.T) {
9 |
10 | nginxServer := newNginxServer()
11 | appStatusURL := "/app/status"
12 | createuserURL := "/create/user"
13 | httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
14 | fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
15 |
16 | httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
17 | fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
18 |
19 | httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
20 | fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
21 |
22 | httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
23 | fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
24 |
25 | httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
26 | fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
27 |
28 | /*
29 | Url: /app/status
30 | HttpCode: 200
31 | Body: Ok
32 |
33 | Url: /app/status
34 | HttpCode: 200
35 | Body: Ok
36 |
37 | Url: /app/status
38 | HttpCode: 403
39 | Body: Not Allowed
40 |
41 | Url: /app/status
42 | HttpCode: 201
43 | Body: User Created
44 |
45 | Url: /app/status
46 | HttpCode: 404
47 | Body: Not Ok
48 | */
49 | }
50 |
--------------------------------------------------------------------------------
/structural/proxy/server.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | type server interface {
4 | handleRequest(string, string) (int, string)
5 | }
6 |
--------------------------------------------------------------------------------