├── .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 | ![](../../images/behavioral/chain-of-responsibility/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/behavioral/command/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/behavioral/iterator/diagram/diagram.svg) 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 | ![](../../images/behavioral/mediator/diagram/diagram.svg) 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 | ![](../../images/behavioral/memento/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/behavioral/observer/diagram/diagram.svg) 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 | ![](../../images/behavioral/state/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../image/../images/behavioral/strategy/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/behavioral/template-method/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/behavioral/vistor/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/creational/abstract-factory/diagram/diagram.svg) 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 | ![](../../images/creational/builder/diagram/diagram.svg) 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 | ![](../../images/creational/factory-method/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/creational/prototype/diagram/diagram.svg) 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 | ![](../../images/creational/singleton/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | iteratorcollectioncreateIterator() iteratoriteratorhasNext() boolgetNext() *userusername stringage intuserCollectionusers []*usercreateIterator() iteratoruserIteratorindex intusers []*userhasNext() boolgetNext() *user -------------------------------------------------------------------------------- /images/behavioral/memento/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | mementocaretakermementoArray []*mementoaddMemento(m *memento)getMenento(index int) *mementomementostate stringgetSavedState() stringoriginatorstate stringcreateMemento() *mementorestorememento(m *memento)setState(state string)getState() string -------------------------------------------------------------------------------- /images/behavioral/observer/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | observercustomerid stringupdate(itemName string)getID() stringitemobserverList []observername stringinStock boolupdateAvailability()register(o observer)deregister(o observer)notifyAll()observerupdate( string)getID() stringsubjectregister(Observer observer)deregister(Observer observer)notifyAll() -------------------------------------------------------------------------------- /images/creational/factory-method/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | factorymethodak47gunname stringpower intsetName(name string)getName() stringsetPower(power int)getPower() intiGunsetName(name string)setPower(power int)getName() stringgetPower() intmaverick -------------------------------------------------------------------------------- /images/creational/prototype/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | prototypefilename stringprint(indentation string)clone() inodefolderchildrens []inodename stringprint(indentation string)clone() inodeinodeprint( string)clone() inode -------------------------------------------------------------------------------- /images/creational/singleton/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | singletonsingle -------------------------------------------------------------------------------- /images/structural/adapter/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | adapterclientinsertSquareUsbInComputer(com computer)computerinsertInSquarePort()macinsertInSquarePort()windowsinsertInCirclePort()windowsAdapterwindowMachine *windowsinsertInSquarePort() -------------------------------------------------------------------------------- /images/structural/composite/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | compositecomponentsearch( string)filename stringsearch(keyword string)getName() stringfoldercomponents []componentname stringsearch(keyword string)add(c component) -------------------------------------------------------------------------------- /images/structural/flyweight/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | flyweightcounterTerroristDresscolor stringgetColor() stringdressgetColor() stringdressFactorydressMapmap[string]dressdressMapmap[string]dressgetDressByType(dressType string) (dress, error)getDressByType(dressType string) (dress, error)playerdress dressplayerType stringlat intlong intnewLocation(lat int, long int) -------------------------------------------------------------------------------- /images/structural/proxy/diagram/diagram.svg: -------------------------------------------------------------------------------- 1 | proxyapplicationhandleRequest(url string, method string) (int, string)nginxapplication *applicationmaxAllowedRequest intrateLimitermap[string]inthandleRequest(url string, method string) (int, string)checkRateLimiting(url string) boolserverhandleRequest( string, string) (int, string) -------------------------------------------------------------------------------- /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 | ![](../../images/structural/adapter/diagram/diagram.svg) 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 | ![](../../images/structural/bridge/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/structural/composite/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/structural/facade/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/structural/flyweight/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | ![](../../images/structural/proxy/diagram/diagram.svg) -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------