diff --git a/adapter/adapter.md b/adapter/adapter.md new file mode 100644 index 0000000..ade863b --- /dev/null +++ b/adapter/adapter.md @@ -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 \ No newline at end of file diff --git a/adapter/main.go b/adapter/main.go new file mode 100644 index 0000000..82dc223 --- /dev/null +++ b/adapter/main.go @@ -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) +} diff --git a/decorator/decorator.md b/decorator/decorator.md new file mode 100644 index 0000000..143d073 --- /dev/null +++ b/decorator/decorator.md @@ -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 \ No newline at end of file diff --git a/decorator/main.go b/decorator/main.go new file mode 100644 index 0000000..579481b --- /dev/null +++ b/decorator/main.go @@ -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()) +} diff --git a/facade/facade.md b/facade/facade.md new file mode 100644 index 0000000..edf33c4 --- /dev/null +++ b/facade/facade.md @@ -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. + \ No newline at end of file diff --git a/facade/main.go b/facade/main.go new file mode 100644 index 0000000..e85540c --- /dev/null +++ b/facade/main.go @@ -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") + } +} diff --git a/factory-method/example.go b/factory-method/example.go new file mode 100644 index 0000000..d8f7415 --- /dev/null +++ b/factory-method/example.go @@ -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()) +} \ No newline at end of file diff --git a/factory-method/factory-method.go b/factory-method/factory-method.go new file mode 100644 index 0000000..6e3d5f1 --- /dev/null +++ b/factory-method/factory-method.go @@ -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()) + } +} \ No newline at end of file diff --git a/factory-method/factory.md b/factory-method/factory.md new file mode 100644 index 0000000..2a5f702 --- /dev/null +++ b/factory-method/factory.md @@ -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. diff --git a/iterator/example.go b/iterator/example.go new file mode 100644 index 0000000..e0f4b0a --- /dev/null +++ b/iterator/example.go @@ -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 + } +} diff --git a/iterator/iterator.md b/iterator/iterator.md new file mode 100644 index 0000000..4de35e5 --- /dev/null +++ b/iterator/iterator.md @@ -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 + \ No newline at end of file diff --git a/iterator/main.go b/iterator/main.go new file mode 100644 index 0000000..13106f8 --- /dev/null +++ b/iterator/main.go @@ -0,0 +1,68 @@ +package main + +import "fmt" + +type Inventory interface { + GetIterator() InventoryIterator +} + +type HandHeldInventory struct { + LeftHandItem int + RightHandItem int +} + +func NewHandHeldInventory(LeftHandItem, RightHandItem int) Inventory { + return HandHeldInventory{ + LeftHandItem: LeftHandItem, + RightHandItem: RightHandItem, + } +} +func (h HandHeldInventory) GetIterator() InventoryIterator { + return NewHandHeldInventoryIterator(h.LeftHandItem, h.RightHandItem) +} + +type InventoryIterator interface { + // CQRS is followed. Command and query responsibilities are segregated. + HasNext() bool + Next() + GetCurrent() int +} + +type HandHeldInventoryIterator struct { + //HandHeldInventory HandHeldInventory + // we cannot have circular dependency in Golang, so, add all var of that struct. + + LeftHandItem int + RightHandItem int +} + +func NewHandHeldInventoryIterator(left, right int) InventoryIterator { + return HandHeldInventoryIterator{ + LeftHandItem: left, + RightHandItem: right, + } +} +func (h HandHeldInventoryIterator) HasNext() bool { + if h.LeftHandItem == 0 && h.RightHandItem == 0 { + return false + } + return true +} +func (h HandHeldInventoryIterator) Next() { + // do something +} +func (h HandHeldInventoryIterator) GetCurrent() int { + return 1 +} + +func main() { + var inv Inventory + inv = NewHandHeldInventory(1, 1) + iter := inv.GetIterator() + for iter.HasNext() { + fmt.Println(iter.GetCurrent()) + iter.Next() + } + + // Code from line 54 to 58 is same, so it can be made as a method and reused for any Inventory (Handheld, bag, warehouse) +} diff --git a/proxy/main.go b/proxy/main.go new file mode 100644 index 0000000..4e807b2 --- /dev/null +++ b/proxy/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "time" +) + +type BookParserInterface interface { + GetNumPages() int +} + +type BookParser struct { + book string +} + +func (b BookParser) GetNumPages() int { + return 12 +} +func NewBookParser(book string) *BookParser { + // do very heavy processing + time.Sleep(5 * time.Second) + return &BookParser{ + book: book, + } +} + +type LazyBookParserProxy struct { + book string + bookparser *BookParser +} + +func (l *LazyBookParserProxy) GetNumPages() int { + if l.bookparser == nil { + // instantiate when needed and keep it in cache + fmt.Println("instantiating") + l.bookparser = NewBookParser(l.book) + } + return 0 +} + +func NewLazyBookParserProxy(book string) LazyBookParserProxy { + // We are not instantiating bookparser as it is heavy + return LazyBookParserProxy{ + book: book, + bookparser: nil, + } +} + +func main() { + lbp := NewLazyBookParserProxy("very huge string") + fmt.Println("It will take 5 sec as it is first time") + fmt.Println(lbp.GetNumPages()) + + // now it wont take time + fmt.Println("Wont take time now") + fmt.Println(lbp.GetNumPages()) +} diff --git a/proxy/proxy.md b/proxy/proxy.md new file mode 100644 index 0000000..fe11390 --- /dev/null +++ b/proxy/proxy.md @@ -0,0 +1,11 @@ +## Proxy pattern +- Remote +- Virtual +- protection + +It adds a behaviour with intent of controlling access of underlying object. + +Proxy has exactly the same method as real object but it can control the access to the object or it can add cache to it. + +Video link: +https://www.youtube.com/watch?v=NwaabHqPHeM \ No newline at end of file diff --git a/singleton/README.md b/singleton/README.md new file mode 100644 index 0000000..671a97f --- /dev/null +++ b/singleton/README.md @@ -0,0 +1,8 @@ +## Singleton pattern +Singleton pattern ensures that a class has only one instance and provides access to it globally +But usually objects should not be global. + +Idea is to somehow restrict the instantiating of the class more than once. Check example. + +Video explanation: +https://www.youtube.com/watch?v=hUE_j6q0LTQ diff --git a/singleton/singleton.go b/singleton/singleton.go new file mode 100644 index 0000000..d988a35 --- /dev/null +++ b/singleton/singleton.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "sync" +) + +type singleton struct { + client string + // other things +} + +var instance singleton = singleton{} + +//func NewInstance() *singleton { +// if instance!=nil { +// return instance +// } +// instance = &singleton{client: "client"} +// return instance +//} + +var once sync.Once + +func Init() { + // this will only work once + once.Do(func() { + fmt.Println("initializing") + instance.client = "client2" + }) +} + +func main() { + //s := NewInstance() + Init() + fmt.Println(instance.client) + Init() + fmt.Println(instance.client) +} diff --git a/state/main.go b/state/main.go new file mode 100644 index 0000000..1d17645 --- /dev/null +++ b/state/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "reflect" +) + +//------------------------------------------------------- +type Gate struct { + state GateState +} + +func (g *Gate) PayOk() { + //fmt.Println(reflect.TypeOf(g.state)) + g.state.PayOk() +} +func (g *Gate) PayFailed() { g.state.PayFailed() } +func (g *Gate) Pay() { + //fmt.Println(reflect.TypeOf(g.state)) + g.state.Pay() +} +func (g *Gate) Enter() { g.state.Enter() } +func (g *Gate) ChangeState(state GateState) { + fmt.Printf("Changing state from %v to %v \n", reflect.TypeOf(g.state), reflect.TypeOf(state)) + g.state = state +} +func NewGate() *Gate { + g := &Gate{} + g.state = NewClosedGateState(g) + return g +} + +//------------------------------------------------------- + +type GateState interface { + PayOk() + PayFailed() + Pay() + Enter() +} + +//------------------------------------------------------- +type OpenGateState struct { + gate *Gate +} + +func (g OpenGateState) PayOk() { + // let user in + g.gate.ChangeState(NewClosedGateState(g.gate)) +} +func (g OpenGateState) PayFailed() {} +func (g OpenGateState) Pay() { + fmt.Println("gate is open") +} +func (g OpenGateState) Enter() {} +func NewOpenGateState(g *Gate) GateState { + return OpenGateState{ + gate: g, + } +} + +//------------------------------------------------------- +type ClosedGateState struct { + gate *Gate +} + +func (g ClosedGateState) PayOk() { + // let user in + g.gate.ChangeState(NewOpenGateState(g.gate)) +} +func (g ClosedGateState) PayFailed() {} +func (g ClosedGateState) Pay() { + fmt.Println("gate is closed") +} +func (g ClosedGateState) Enter() {} +func NewClosedGateState(g *Gate) GateState { + return ClosedGateState{ + gate: g, + } +} + +//------------------------------------------------------- +func main() { + gate := NewGate() + gate.Pay() + gate.PayOk() + gate.Pay() + gate.PayOk() +} diff --git a/state/state.md b/state/state.md new file mode 100644 index 0000000..a3042d3 --- /dev/null +++ b/state/state.md @@ -0,0 +1,7 @@ +## State pattern +State pattern represents the state and perform actions based on the state. This is more like polymorphism. +It encapsulates the varying behaviour of same object based on the state. + +Eg. Ticketing system, any state machines (electronic devices) + +Video link: https://www.youtube.com/watch?v=N12L5D78MAA \ No newline at end of file