Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add design patterns #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions adapter/adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Adapter pattern
Adapter pattern is for one interface to adapt to other interfaces.

There should be no intention to add any logic to it. There is decorator pattern for this.

Video explanation:
https://www.youtube.com/watch?v=2PKQtcJjYvc
52 changes: 52 additions & 0 deletions adapter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import "fmt"

// ------------------Adaptee and adapter are coupled-----------------------------
type Adaptee1 struct{}

func (a Adaptee1) SpecificMethod() {
fmt.Println("Specific Adaptee 1 method")
}

type Adapter1 struct{ Adaptee Adaptee1 } // has an adaptee
func (a Adapter1) Request() {
a.Adaptee.SpecificMethod()
}

// -----------------------------------------------
type Adaptee2 struct{}

func (a Adaptee2) SpecificMethod() {
fmt.Println("Specific Adaptee 2 method")
}

type Adapter2 struct {
// has an adaptee
Adaptee Adaptee2
}

func (a Adapter2) Request() {
a.Adaptee.SpecificMethod()
}

// ---------------------Target client----------------------------
type TargetClient struct{}

func (t TargetClient) Anything(targetInterface TargetInterface) {
targetInterface.Request()
}

// All adapters will implement Target interface
type TargetInterface interface {
Request()
}

func main() {
adapter1 := Adapter1{Adaptee: Adaptee1{}}
adapter2 := Adapter2{Adaptee: Adaptee2{}}

client := TargetClient{}
client.Anything(adapter1)
client.Anything(adapter2)
}
15 changes: 15 additions & 0 deletions decorator/decorator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
### Decorator pattern
Decorator Pattern is used to attach the additional responsibility to a class dynamically.
Without this, we might have
- Class explosion
- Contradict design principles

Decorator pattern has a component interface (abstract class) and as many decorators as we want (abstract of component).
Decorator is `is-a` Component and `has-a` component.

It works something like recursion where base concrete component acts as basecase.

Also, order doesnot matter in decorator pattern.

Explanation video -
https://www.youtube.com/watch?v=GCraGHx6gso
45 changes: 45 additions & 0 deletions decorator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import "fmt"

type Beverage interface {
GetCost() int
}

// Concrete components
type espresso struct{}
func (e espresso) GetCost() int {
return 1
}

type cafelatte struct{}
func (c cafelatte) GetCost() int {
return 2
}

// Decorators
type AddOnBeverageDecorator interface {
GetCost() int
}

type creamMilk struct {
beverage Beverage
}
func (c creamMilk) GetCost() int {
return c.beverage.GetCost() + 10
}

type chocolate struct {
beverage Beverage
}
func (c chocolate) GetCost() int {
return c.beverage.GetCost() + 12
}

func main() {
espresso := espresso{}
espressoWithCream := creamMilk{beverage: espresso}
fmt.Println("espressoWithCream price is", espressoWithCream.GetCost())
espressoWithCreamAndChocolate := chocolate{beverage: espressoWithCream}
fmt.Println("espressoWithCreamAndChocolate price is", espressoWithCreamAndChocolate.GetCost())
}
21 changes: 21 additions & 0 deletions facade/facade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Facade pattern
Structural design patterns
- Adapter
- Facade
- Proxy
- Decorator
- Composite

Facade pattern hides the complexities of underlying classes and provides a unified interface to the client to access any underlying interfaces.

In the code example, we want to show that all complex business logic is hidden in Facade class and we just need to call methods given by facade.

Video explanation:
https://www.youtube.com/watch?v=K4FkHVO5iac

Golang example:
https://golangbyexample.com/facade-design-pattern-in-golang/

### Principle of least knowledge
For loose coupling, the ones using a function should be the only one dependent on it and the ones using the previous ones should not be affected by change.

33 changes: 33 additions & 0 deletions facade/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import "fmt"

type facade struct {
account *account
}
type Facade interface {
AddMoney(accountID, amount int) bool
}
func (f facade) AddMoney(accountID, amount int) bool {
// Any complex logic
if f.account.IsValid(accountID) {
return true
}
fmt.Println("amount to add is ", amount)
return true
}

type account struct {}
func (a account) IsValid(accountID int) bool {
return true
}

func main() {
facadeInstance := facade{
account: &account{},
}

if facadeInstance.AddMoney(123, 100) {
fmt.Println("Money added")
}
}
56 changes: 56 additions & 0 deletions factory-method/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import "fmt"

type Vehicle interface {
GetName() string
}

// inheritance is not possible in Golang
type RoadVehicle interface {
Vehicle
GetTiresNum() int
}

type WaterVehicle interface {
Logistics
GetWaterCap() int
}

type truck struct{}

func (t truck) GetName() string {
return "truck"
}
func (t truck) GetTiresNum() int {
return 6
}

type boat struct{}

func (b boat) GetName() string {
return "boat"
}

type Logistics interface {
CreateVehicle() Vehicle
}

type roadLogistics struct{}
func (r roadLogistics) CreateVehicle() Vehicle {
// returns Road vehicle
return truck{}
}

type waterLogistics struct {}
func (w waterLogistics) CreateVehicle() Vehicle {
// returns Water vehicle
return boat{}
}

func main() {
r := roadLogistics{}
fmt.Println(r.CreateVehicle().GetName())
w := waterLogistics{}
fmt.Println(w.CreateVehicle().GetName())
}
48 changes: 48 additions & 0 deletions factory-method/factory-method.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"fmt"
"math/rand"
)

type Animal interface {
GetName() string
}

type duck struct {}
func (d duck) GetName() string {
return "duck"
}

type dog struct {}
func (d dog) GetName() string {
return "dog"
}

type bird struct {}
func (b bird) GetName() string {
return "bird"
}

type AnimalFactory interface {
Create() Animal
}

type randomAnimalFactory struct {}
func (r randomAnimalFactory) Create() Animal {
i := rand.Int31n(3)
if i == 0 {
return duck{}
} else if i == 1 {
return dog{}
} else {
return bird{}
}
}

func main() {
randomAnimalFactory := randomAnimalFactory{}
for i := 0; i<10 ;i++ {
fmt.Println(randomAnimalFactory.Create().GetName())
}
}
19 changes: 19 additions & 0 deletions factory-method/factory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Factory Method pattern
Factory Method pattern defines an interface for creating an object. It will let its subclass instantiate the objects according to some logic they want.

A good usecase is in game design where we have to generate enemies in space.
- Factory for generating enemies
- We can use multiple factories for multi levels too or we can pass some params to a factory too

Another usecase is when we are creating a memory intensive objects eg. db connections.
We will keep a pool of connections and using DB Factory, we can return connections that are available using round robin.

Explanation Video:
https://www.youtube.com/watch?v=EcFVTgRHJLM

Good resource:
https://refactoring.guru/design-patterns/factory-method

- Use the Factory Method when you don’t know beforehand the exact types and dependencies of the objects your code should work with.
- Use the Factory Method when you want to provide users of your library or framework with a way to extend its internal components. Android uses this.
- Use the Factory Method when you want to save system resources by reusing existing objects instead of rebuilding them each time.
61 changes: 61 additions & 0 deletions iterator/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

type collection interface {
GetIterator() iterator
}

type user struct {
name string
}

type usercollection struct {
users []*user
}
func NewUserCollection(users []*user) collection {
return usercollection{users: users}
}
func (uc usercollection) GetIterator() iterator {
return NewUserIterator(uc.users)
}

// -------------------------------------------
type iterator interface {
HasNext() bool
// get current
// Next()
}
type userIterator struct {
index int
users []*user
}

func NewUserIterator(users []*user) iterator {
return userIterator{
index: 0,
users: users,
}
}

func (u userIterator) HasNext() bool {
if u.index < len(u.users) {
return true
}
return false
}

// other methods

func main() {
u1 := &user{name: "u1"}
u2 := &user{name: "u2"}
users := []*user{u1, u2}

usercollection := NewUserCollection(users)
iter := usercollection.GetIterator()
for iter.HasNext() {
// do something

// break for program to run
break
}
}
12 changes: 12 additions & 0 deletions iterator/iterator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Iterator pattern
Iterator pattern is used to extract the iteration logic for any iterable data structure. This iterator provides a generic method of iterating over a collection independent of its type.

Using this:
- We can generate indefinite sequence.
- Implement lazy logic. Eg. if we dont need all of the data at once.
- When we have abstract data type. Eg. Binary search tree iterator with `inorder`, `preorder` and `postorder` iteration.

video link: https://www.youtube.com/watch?v=uNTNEfwYXhI

blog link for golang: https://refactoring.guru/design-patterns/iterator/go/example#example-0

Loading