design pattern Archives - Welcome To Golang By Example https://vikasboss.github.io/tag/design-pattern/ Thu, 29 Apr 2021 17:21:51 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 https://i0.wp.com/golangbyexamples.com/wp-content/uploads/2021/05/cropped-go_border-1.png?fit=32%2C32&ssl=1 design pattern Archives - Welcome To Golang By Example https://vikasboss.github.io/tag/design-pattern/ 32 32 159787465 Mediator Design Pattern in Go (Golang) https://vikasboss.github.io/mediator-design-pattern-golang/ https://vikasboss.github.io/mediator-design-pattern-golang/#comments Thu, 28 Nov 2019 20:01:50 +0000 https://vikasboss.github.io/?p=680 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Introduction: Mediator design pattern is a...

The post Mediator Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Introduction:

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.

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.

Notice how stationManger acts as a mediator between the trains and the platform in the code below.

  • passengerTrain and goodsTrain implement the train interface.
  • stationManger implements the mediator interface.

Practical Example

train.go

package main

type train interface {
    requestArrival()
    departure()
    permitArrival()
}

passengerTrain.go

package main

import "fmt"

type passengerTrain struct {
    mediator mediator
}

func (g *passengerTrain) requestArrival() {
    if g.mediator.canLand(g) {
        fmt.Println("PassengerTrain: Landing")
    } else {
        fmt.Println("PassengerTrain: Waiting")
    }
}

func (g *passengerTrain) departure() {
    fmt.Println("PassengerTrain: Leaving")
    g.mediator.notifyFree()
}

func (g *passengerTrain) permitArrival() {
    fmt.Println("PassengerTrain: Arrival Permitted. Landing")
}

goodsTrain.go

package main

import "fmt"

type goodsTrain struct {
    mediator mediator
}

func (g *goodsTrain) requestArrival() {
    if g.mediator.canLand(g) {
        fmt.Println("GoodsTrain: Landing")
    } else {
        fmt.Println("GoodsTrain: Waiting")
    }
}

func (g *goodsTrain) departure() {
    g.mediator.notifyFree()
    fmt.Println("GoodsTrain: Leaving")
}

func (g *goodsTrain) permitArrival() {
    fmt.Println("GoodsTrain: Arrival Permitted. Landing")
}

mediator.go

package main

type mediator interface {
    canLand(train) bool
    notifyFree()
}

stationManager.go

package main

import "sync"

type stationManager struct {
    isPlatformFree bool
    lock           *sync.Mutex
    trainQueue     []train
}

func newStationManger() *stationManager {
    return &stationManager{
        isPlatformFree: true,
        lock:           &sync.Mutex{},
    }
}

func (s *stationManager) canLand(t train) bool {
    s.lock.Lock()
    defer s.lock.Unlock()
    if s.isPlatformFree {
        s.isPlatformFree = false
        return true
    }
    s.trainQueue = append(s.trainQueue, t)
    return false
}

func (s *stationManager) notifyFree() {
    s.lock.Lock()
    defer s.lock.Unlock()
    if !s.isPlatformFree {
        s.isPlatformFree = true
    }
    if len(s.trainQueue) > 0 {
        firstTrainInQueue := s.trainQueue[0]
        s.trainQueue = s.trainQueue[1:]
        firstTrainInQueue.permitArrival()
    }
}

main.go

package main

func main() {
    stationManager := newStationManger()
    passengerTrain := &passengerTrain{
        mediator: stationManager,
    }
    goodsTrain := &goodsTrain{
        mediator: stationManager,
    }
    passengerTrain.requestArrival()
    goodsTrain.requestArrival()
    passengerTrain.departure()
}

Output:

PassengerTrain: Landing
GoodsTrain: Waiting
PassengerTrain: Leaving
GoodsTrain: Arrival Permitted. Landing

Full Working Code:

package main

import (
    "fmt"
    "sync"
)

type train interface {
    requestArrival()
    departure()
    permitArrival()
}

type passengerTrain struct {
    mediator mediator
}

func (g *passengerTrain) requestArrival() {
    if g.mediator.canLand(g) {
        fmt.Println("PassengerTrain: Landing")
    } else {
        fmt.Println("PassengerTrain: Waiting")
    }
}

func (g *passengerTrain) departure() {
    fmt.Println("PassengerTrain: Leaving")
    g.mediator.notifyFree()
}

func (g *passengerTrain) permitArrival() {
    fmt.Println("PassengerTrain: Arrival Permitted. Landing")
}

type goodsTrain struct {
    mediator mediator
}

func (g *goodsTrain) requestArrival() {
    if g.mediator.canLand(g) {
        fmt.Println("GoodsTrain: Landing")
    } else {
        fmt.Println("GoodsTrain: Waiting")
    }
}

func (g *goodsTrain) departure() {
    g.mediator.notifyFree()
    fmt.Println("GoodsTrain: Leaving")
}

func (g *goodsTrain) permitArrival() {
    fmt.Println("GoodsTrain: Arrival Permitted. Landing")
}

type mediator interface {
    canLand(train) bool
    notifyFree()
}

type stationManager struct {
    isPlatformFree bool
    lock           *sync.Mutex
    trainQueue     []train
}

func newStationManger() *stationManager {
    return &stationManager{
        isPlatformFree: true,
        lock:           &sync.Mutex{},
    }
}

func (s *stationManager) canLand(t train) bool {
    s.lock.Lock()
    defer s.lock.Unlock()
    if s.isPlatformFree {
        s.isPlatformFree = false
        return true
    }
    s.trainQueue = append(s.trainQueue, t)
    return false
}

func (s *stationManager) notifyFree() {
    s.lock.Lock()
    defer s.lock.Unlock()
    if !s.isPlatformFree {
        s.isPlatformFree = true
    }
    if len(s.trainQueue) > 0 {
        firstTrainInQueue := s.trainQueue[0]
        s.trainQueue = s.trainQueue[1:]
        firstTrainInQueue.permitArrival()
    }
}

func main() {
    stationManager := newStationManger()
    passengerTrain := &passengerTrain{
        mediator: stationManager,
    }
    goodsTrain := &goodsTrain{
        mediator: stationManager,
    }
    passengerTrain.requestArrival()
    goodsTrain.requestArrival()
    passengerTrain.departure()
}

Output:

PassengerTrain: Landing
GoodsTrain: Waiting
PassengerTrain: Leaving
GoodsTrain: Arrival Permitted. Landing

The post Mediator Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/mediator-design-pattern-golang/feed/ 2 680
Bridge Design Pattern in Go (Golang) https://vikasboss.github.io/bridge-design-pattern-in-go/ https://vikasboss.github.io/bridge-design-pattern-in-go/#comments Sat, 16 Nov 2019 17:42:45 +0000 https://vikasboss.github.io/?p=603 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Introduction: Bridge design pattern is a...

The post Bridge Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Introduction:

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.

This pattern suggests dividing a large class into two separate hierarchy

  • Abstraction – It is an interface and children of the Abstraction are referred to as Refined Abstraction. The abstraction contains a reference to the implementation.
  • Implementation – It is also an interface and children’s of the Implementation are referred to as Concrete Implementation

Abstraction hierarchy is being referred to by clients without worrying about the implementation. Let’s take an example. Assume you have two types of computer mac and windows. Also, let’s say two types of printer epson and hp . Both computers and printers needs to work with each other in any combination.  The client will only access the computer without worrying about how print is happening. Instead of creating four structs for the 2*2 combination, we create two hierarchies

  • Abstraction Hierarchy
  • Implementation Hierarchy

See the below figure. These two hierarchies communicate with each other via a bridge where Abstraction (computer here) contains a reference to the Implementation(printer here). Both the abstraction and implementation can continue to develop independently without affecting each other.  Notice how win and mac embed the reference to printer. We can change the Abstraction’sImplementation (i.e., computer’s printer) at run time as abstraction refers to implementation via the interface. On calling mac.print() or windows.print() it dispatches the request to printer.printFile(). This acts as a bridge and provides a loose coupling between the two.

UML Diagram:

Mapping 

The below table represents the mapping from the UML diagram actors to actual implementation actors in “Practical Example” below

Abstractioncomputer.go
Refined Abstraction 1win.go
Refined Abstraction 2mac.go
Implementationprinter.go
Concrete Implementation 1epson.go
Concrete Implementation 2hp.go
Clientmain.go

Practical Example

computer.go

package main

type computer interface {
    print()
    setPrinter(printer)
}

mac.go

package main

import "fmt"

type mac struct {
    printer printer
}

func (m *mac) print() {
    fmt.Println("Print request for mac")
    m.printer.printFile()
}

func (m *mac) setPrinter(p printer) {
    m.printer = p
}

windows.go

package main

import "fmt"

type windows struct {
    printer printer
}

func (w *windows) print() {
    fmt.Println("Print request for windows")
    w.printer.printFile()
}

func (w *windows) setPrinter(p printer) {
    w.printer = p
}

printer.go

package main

type printer interface {
    printFile()
}

epson.go

package main

import "fmt"

type epson struct {
}

func (p *epson) printFile() {
    fmt.Println("Printing by a EPSON Printer")
}

hp.go

package main

import "fmt"

type hp struct {
}

func (p *hp) printFile() {
    fmt.Println("Printing by a HP Printer")
}

main.go

package main

import "fmt"

func main() {
    hpPrinter := &hp{}
    epsonPrinter := &epson{}
    macComputer := &mac{}
    macComputer.setPrinter(hpPrinter)
    macComputer.print()
    fmt.Println()
    macComputer.setPrinter(epsonPrinter)
    macComputer.print()
    fmt.Println()
    winComputer := &windows{}
    winComputer.setPrinter(hpPrinter)
    winComputer.print()
    fmt.Println()
    winComputer.setPrinter(epsonPrinter)
    winComputer.print()
    fmt.Println()
}

Output:

Print request for mac
Printing by a HP Printer

Print request for mac
Printing by a EPSON Printer

Print request for windows
Printing by a HP Printer

Print request for windows

Full Working Code:

package main

import "fmt"

type computer interface {
    print()
    setPrinter(printer)
}

type mac struct {
    printer printer
}

func (m *mac) print() {
    fmt.Println("Print request for mac")
    m.printer.printFile()
}

func (m *mac) setPrinter(p printer) {
    m.printer = p
}

type windows struct {
    printer printer
}

func (w *windows) print() {
    fmt.Println("Print request for windows")
    w.printer.printFile()
}

func (w *windows) setPrinter(p printer) {
    w.printer = p
}

type printer interface {
    printFile()
}

type epson struct {
}

func (p *epson) printFile() {
    fmt.Println("Printing by a EPSON Printer")
}

type hp struct {
}

func (p *hp) printFile() {
    fmt.Println("Printing by a HP Printer")
}

func main() {
    hpPrinter := &hp{}
    epsonPrinter := &epson{}
    macComputer := &mac{}
    macComputer.setPrinter(hpPrinter)
    macComputer.print()
    fmt.Println()
    macComputer.setPrinter(epsonPrinter)
    macComputer.print()
    fmt.Println()
    winComputer := &windows{}
    winComputer.setPrinter(hpPrinter)
    winComputer.print()
    fmt.Println()
    winComputer.setPrinter(epsonPrinter)
    winComputer.print()
    fmt.Println()
}

Output:

Print request for mac
Printing by a HP Printer

Print request for mac
Printing by a EPSON Printer

Print request for windows
Printing by a HP Printer

Print request for windows

The post Bridge Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/bridge-design-pattern-in-go/feed/ 2 603
Proxy Design Pattern in Go (Golang) https://vikasboss.github.io/proxy-design-pattern-in-golang/ https://vikasboss.github.io/proxy-design-pattern-in-golang/#comments Sat, 16 Nov 2019 17:29:05 +0000 https://vikasboss.github.io/?p=599 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Introduction:  Proxy Design Pattern is a...

The post Proxy Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Introduction: 

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.


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

  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.
  2. A web server such as Nginx can act as a proxy for your application server. It provides
    • Controlled access to your application server – For example, it can do rate limiting
    • Additional behavior – For example, it can do some caching

Let’ss see the UML Diagram

UML Diagram:

In below UML diagram

  • Subject: it represents the interface which proxy and the realSubject should follow
  • Proxy: The proxy class embeds the realSubject and passes on the request to the realSubject after it has finished its processing
  • RealSubject: This is the class that holds the main business logic and which is kept behind a proxy
  • Client: Can interact with both proxy and realSubject as they both follow the same interface.

Below is the corresponding mapping UML diagram with the practical example of nginx and application server which was described above.

Mapping 


The below table represents the mapping from the UML diagram actors to actual implementation actors in code

subjectserver.go
proxynginx.go
realSubjectapplication.go
clientmain.go

Practical Example:

server.go

package main

type server interface {
    handleRequest(string, string) (int, string)
}

nginx.go

package main

type nginx struct {
    application       *application
    maxAllowedRequest int
    rateLimiter       map[string]int
}

func newNginxServer() *nginx {
    return &nginx{
        application:       &application{},
        maxAllowedRequest: 2,
        rateLimiter:       make(map[string]int),
    }
}

func (n *nginx) handleRequest(url, method string) (int, string) {
    allowed := n.checkRateLimiting(url)
    if !allowed {
        return 403, "Not Allowed"
    }
    return n.application.handleRequest(url, method)
}

func (n *nginx) checkRateLimiting(url string) bool {
    if n.rateLimiter[url] == 0 {
        n.rateLimiter[url] = 1
    }
    if n.rateLimiter[url] > n.maxAllowedRequest {
        return false
    }
    n.rateLimiter[url] = n.rateLimiter[url] + 1
    return true
}

application.go

package main

type application struct {
}

func (a *application) handleRequest(url, method string) (int, string) {
    if url == "/app/status" && method == "GET" {
        return 200, "Ok"
    }
    if url == "/create/user" && method == "POST" {
        return 201, "User Created"
    }
    return 404, "Not Ok"
}

main.go

package main

import "fmt"

func main() {
    nginxServer := newNginxServer()
    appStatusURL := "/app/status"
    createuserURL := "/create/user"
    httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
}

Output:

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 403
Body: Not Allowed

Url: /app/status
HttpCode: 201
Body: User Created

Url: /app/status
HttpCode: 404
Body: Not Ok

Full Working Code:

package main

import "fmt"

type server interface {
    handleRequest(string, string) (int, string)
}

type nginx struct {
    application       *application
    maxAllowedRequest int
    rateLimiter       map[string]int
}

func newNginxServer() *nginx {
    return &nginx{
        application:       &application{},
        maxAllowedRequest: 2,
        rateLimiter:       make(map[string]int),
    }
}

func (n *nginx) handleRequest(url, method string) (int, string) {
    allowed := n.checkRateLimiting(url)
    if !allowed {
        return 403, "Not Allowed"
    }
    return n.application.handleRequest(url, method)
}

func (n *nginx) checkRateLimiting(url string) bool {
    if n.rateLimiter[url] == 0 {
        n.rateLimiter[url] = 1
    }
    if n.rateLimiter[url] > n.maxAllowedRequest {
        return false
    }
    n.rateLimiter[url] = n.rateLimiter[url] + 1
    return true
}

type application struct {
}

func (a *application) handleRequest(url, method string) (int, string) {
    if url == "/app/status" && method == "GET" {
        return 200, "Ok"
    }
    if url == "/create/user" && method == "POST" {
        return 201, "User Created"
    }
    return 404, "Not Ok"
}

func main() {
    nginxServer := newNginxServer()
    appStatusURL := "/app/status"
    createuserURL := "/create/user"
    httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
    httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
    fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
}

Output:

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 200
Body: Ok

Url: /app/status
HttpCode: 403
Body: Not Allowed

Url: /app/status
HttpCode: 201
Body: User Created

Url: /app/status
HttpCode: 404
Body: Not Ok

The post Proxy Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/proxy-design-pattern-in-golang/feed/ 2 599
Command Design Pattern in Go (Golang) https://vikasboss.github.io/command-design-pattern-in-golang/ https://vikasboss.github.io/command-design-pattern-in-golang/#comments Sat, 16 Nov 2019 17:20:56 +0000 https://vikasboss.github.io/?p=594 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – https://vikasboss.github.io/all-design-patterns-golang/ Introduction: Command Design Pattern is a behavioral design pattern. It suggests...

The post Command Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – https://vikasboss.github.io/all-design-patterns-golang/

Introduction:

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.

The basic components that are used in the command design pattern are:

  • Receiver – It is the class which contains the business logic. The command object only delays its requests to the receiver.
  • Command – embeds receiver and binds a particular action of the receiver.
  • Invoker – It embeds the command and envokes the command by calling the command’s execute method.
  • 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.

Let’s understand a situation after which it will be clear why the command pattern is useful.  Imagine the case of a TV. A TV can be turned ON by either

  1. Remote ON Button
  2. On Button on the tv.

Both these trigger points do the same thing i.e. turn the TV on. In order to ON the TV, we can implement the ON command object with the receiver as the TV. When execute() method is called on this ON command object, it in turn call TV.on() function. So in this case:

  • Receiver is the TV
  • Command is the ON command object which embeds TV
  • Invoker is the Remote ON Button or the ON Button on the TV. Both embed the ON command object

Notice here that we have wrapped the request of turning the TV on into an ON command object which can be invoked by multiple invokers. This ON command object embeds the receiver (TV here) and can be executed independently.

As another example, imagine the case of an Adobe Photoshop Application. In Photoshop a Save operation can be triggered from 3 places

  1. From the menu.
  2. From the button on the upper bar.
  3. Using shortcut Ctrl+S.

All three trigger points do the same thing, i.e save the current image in the application. This saves can be wrapped into a Save Command Object with a current image open in the application as the receiver.


What’s the benefit of creating a separate command object in the above examples.

  1. It decouples the UI logic from underlying business logic
  2. No need to create different handlers for each of the invokers.
  3. The command object contains all the information it needs to execute. Hence it can also be used for delayed execution.

Let’s look at the UML diagram now.

UML Diagram:

  • Notice how Invoker embeds the command. The request is sent to the Invoker and it passes the request to the encapsulated command object.
  • All the Concrete Command Object embed the receiver

Mapping 


The below table represents the mapping from the UML diagram actors to actual implementation actors in “Practical Example” below

Invokerbutton.go
Command Interfacecommand.go
Concrete Command 1onCommand.go
Concrete Command 2offCommand.go
Receiver Interfacedevice.go
Concrete Receivertv.go
Clientmain.go

Practical Example:

button.go

package main

type button struct {
    command command
}

func (b *button) press() {
    b.command.execute()
}

command.go

package main

type command interface {
    execute()
}

onCommand.go

package main

type onCommand struct {
    device device
}

func (c *onCommand) execute() {
    c.device.on()
}

offCommand.go

package main

type offCommand struct {
    device device
}

func (c *offCommand) execute() {
    c.device.off()
}

device.go

package main

type device interface {
    on()
    off()
}

tv.go

package main

import "fmt"

type tv struct {
    isRunning bool
}

func (t *tv) on() {
    t.isRunning = true
    fmt.Println("Turning tv on")
}

func (t *tv) off() {
    t.isRunning = false
    fmt.Println("Turning tv off")
}

main.go

package main

func main() {
    tv := &tv{}
    onCommand := &onCommand{
        device: tv,
    }
    offCommand := &offCommand{
        device: tv,
    }
    onButton := &button{
        command: onCommand,
    }
    onButton.press()
    offButton := &button{
        command: offCommand,
    }
    offButton.press()
}

Output:

Turning tv on
Turning tv off

Full Working Code:

package main

import "fmt"

type button struct {
    command command
}

func (b *button) press() {
    b.command.execute()
}

type command interface {
    execute()
}

type offCommand struct {
    device device
}

func (c *offCommand) execute() {
    c.device.off()
}

type onCommand struct {
    device device
}

func (c *onCommand) execute() {
    c.device.on()
}

type device interface {
    on()
    off()
}

type tv struct {
    isRunning bool
}

func (t *tv) on() {
    t.isRunning = true
    fmt.Println("Turning tv on")
}

func (t *tv) off() {
    t.isRunning = false
    fmt.Println("Turning tv off")
}

func main() {
    tv := &tv{}
    onCommand := &onCommand{
        device: tv,
    }
    offCommand := &offCommand{
        device: tv,
    }
    onButton := &button{
        command: onCommand,
    }
    onButton.press()
    offButton := &button{
        command: offCommand,
    }
    offButton.press()
}

Output:

Turning tv on
Turning tv off

The post Command Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/command-design-pattern-in-golang/feed/ 2 594
Strategy Design Pattern in Go (Golang) https://vikasboss.github.io/strategy-design-pattern-golang/ https://vikasboss.github.io/strategy-design-pattern-golang/#comments Wed, 13 Nov 2019 19:57:19 +0000 https://vikasboss.github.io/?p=562 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Definition:  Strategy design pattern is a...

The post Strategy Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Definition: 

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.

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

  1. LRU – Least Recently Used: Remove the entry which has been used least recently.
  2. FIFO – First In First Out: Remove the entry, which was created first.
  3. LFU – Least Frequently Used: Remove the entry which was least frequently used.

Now the problem is how to decouple our Cache class with the algorithm such that we should be able to change the algorithm at run time. Also Cache class should not change when a new algorithm is being added. This is were Strategy Pattern comes into the picture. The strategy pattern suggests creating a family of the algorithm with each algorithm having its own class. Each of these classes follows the same interface and this makes the algorithm interchangeable within the family. Let’s say the common interface name is evictionAlgo.

Now our main Cache class will embed evictionAlgo interface. Instead of implementing all types of eviction algorithms in itself, our Cache class will delegate all it to the evictionAlgo interface. Since evictionAlgo is an interface, we can run time change the algorithm to either be LRU, FIFO, LFU without any change in Cache class.

When to Use

  • When an object needs to support different behavior and you want to change the behavior at run time.
  • When you want to avoid a lot of conditionals of choosing the runtime behavior.
  • When you have different algorithms that are similar and they only differ in the way they execute some behavior.

UML Diagram

Notice below UML diagram, Context (Cache) embeds the strategy (evictionAlgo) interface.

Below is the corresponding mapping UML diagram with the example given above

Mapping

The below table represents the mapping from the UML diagram actors to actual implementation actors in code.

Contextcache.go
StrategyevictionAlgo.go
Concrete Strategy Object 1lfu.go
Concrete Strategy Object 2lru.go
Concrete Strategy Object 3fifo.go
Clientmain.go

Practical Example

evictionAlgo.go

package main

type evictionAlgo interface {
    evict(c *cache)
}

fifo.go

package main

import "fmt"

type fifo struct {
}

func (l *fifo) evict(c *cache) {
    fmt.Println("Evicting by fifo strtegy")
}

lru.go

package main

import "fmt"

type lru struct {
}

func (l *lru) evict(c *cache) {
    fmt.Println("Evicting by lru strtegy")
}

lfu.go

package main

import "fmt"

type lfu struct {
}

func (l *lfu) evict(c *cache) {
    fmt.Println("Evicting by lfu strtegy")
}

cache.go

package main

type cache struct {
    storage      map[string]string
    evictionAlgo evictionAlgo
    capacity     int
    maxCapacity  int
}

func initCache(e evictionAlgo) *cache {
    storage := make(map[string]string)
    return &cache{
        storage:      storage,
        evictionAlgo: e,
        capacity:     0,
        maxCapacity:  2,
    }
}

func (c *cache) setEvictionAlgo(e evictionAlgo) {
    c.evictionAlgo = e
}

func (c *cache) add(key, value string) {
    if c.capacity == c.maxCapacity {
        c.evict()
    }
    c.capacity++
    c.storage[key] = value
}

func (c *cache) get(key string) {
    delete(c.storage, key)
}

func (c *cache) evict() {
    c.evictionAlgo.evict(c)
    c.capacity--
}

main.go

package main

func main() {
    lfu := &lfu{}
    cache := initCache(lfu)
    cache.add("a", "1")
    cache.add("b", "2")
    cache.add("c", "3")
    lru := &lru{}
    cache.setEvictionAlgo(lru)
    cache.add("d", "4")
    fifo := &fifo{}
    cache.setEvictionAlgo(fifo)
    cache.add("e", "5")
}

Output:

Evicting by lfu strtegy
Evicting by lru strtegy
Evicting by fifo strtegy

The post Strategy Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/strategy-design-pattern-golang/feed/ 1 562
Flyweight Design Pattern in Go (Golang) https://vikasboss.github.io/flyweight-design-pattern-golang/ https://vikasboss.github.io/flyweight-design-pattern-golang/#comments Sat, 09 Nov 2019 08:58:12 +0000 https://vikasboss.github.io/?p=361 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Definition:  It is a structural design...

The post Flyweight Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Definition: 

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.

Let’s first see an example. Flyweight Pattern will be clear after this example.

In a game of Counter-Strike, Terrorist and Counter-Terrorist have a different type of dress. For simplicity, let’s assume that both Terrorist and Counter-Terrorists have one dress type each. The dress object is embedded in the player object as below

Below is the struct for a player, we can see the dress object is embedded in player struct

type player struct {
    dress      dress
    playerType string //Can be T or CT
    lat        int
    long       int
}

Let’s say there are 5 Terrorists and 5 Counter-Terrorist, so a total of 10 players. Now there are two options with respect to dress

  1. Each of the 10 player objects creates a different dress object and embed them. Total 10 dress objects will be created
  2. We create two dress object
    • Single Terrorist Dress Object: This will be shared across 5 Terrorist
    • Single Counter-Terrorist Dress Object: This will be shared across 5 Counter-Terrorist

As you can that in Approach 1, total of 10 dress objects are created while in approach 2 only 2 dress objects are created. The second approach is what we follow in the Flyweight design pattern. The two dress objects which we created are called the flyweight objects. Flyweight pattern takes out the common parts and creates flyweight objects. These flyweight objects (dress here)  can then be shared among multiple objects (player here). This drastically reduces the number of dress objects and the good part is that even if you create more players, still only two dress objects will be sufficient.

In the flyweight pattern, we store the flyweight objects in the map.  Whenever the other objects which share the flyweight objects are created, then flyweight objects are fetched from the map.

Intrinsic and Extrinsic States

  • Intrinsic State –Dress in the intrinsic state as it can be shared across multiple Terrorist and Counter-Terrorist Objects
  • Extrinsic State – Player location and the player weapon is an extrinsic state as it is different for every object.

When to Use:

  • When the objects have some intrinsic properties which can be shared.
    • As in the above example, dress is the intrinsic property that was taken out and shared.
  • 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.

UML Diagram:

Below is the corresponding mapping UML diagram with the example given above

Mapping:

The below table represents the mapping from the UML diagram actors to actual implementation actors in code.

Flyweight FactorydressFactory.go
Flyweight Interfacedress.go
Concrete Flyweight Object 1terroristDress.go
Concrete Flyweight Object 1counterTerroristDress.go
Contextplayer.go
Clientmain.go

Practical Example:

dressFactory.go

package main

import "fmt"

const (
    //TerroristDressType terrorist dress type
    TerroristDressType = "tDress"
    //CounterTerrroristDressType terrorist dress type
    CounterTerrroristDressType = "ctDress"
)

var (
    dressFactorySingleInstance = &dressFactory{
        dressMap: make(map[string]dress),
    }
)

type dressFactory struct {
    dressMap map[string]dress
}

func (d *dressFactory) getDressByType(dressType string) (dress, error) {
    if d.dressMap[dressType] != nil {
        return d.dressMap[dressType], nil
    }
    if dressType == TerroristDressType {
        d.dressMap[dressType] = newTerroristDress()
        return d.dressMap[dressType], nil
    }
    if dressType == CounterTerrroristDressType {
        d.dressMap[dressType] = newCounterTerroristDress()
        return d.dressMap[dressType], nil
    }
    return nil, fmt.Errorf("Wrong dress type passed")
}

func getDressFactorySingleInstance() *dressFactory {
    return dressFactorySingleInstance
}

dress.go

package main

type dress interface {
    getColor() string
}

terroristDress.go

package main

type terroristDress struct {
	color string
}

func (t *terroristDress) getColor() string {
	return t.color
}

func newTerroristDress() *terroristDress {
	return &terroristDress{color: "red"}
}

counterTerroristDress.go

package main

type counterTerroristDress struct {
    color string
}

func (c *counterTerroristDress) getColor() string {
    return c.color
}

func newCounterTerroristDress() *counterTerroristDress {
    return &counterTerroristDress{color: "green"}
}

player.go

package main

type player struct {
    dress      dress
    playerType string
    lat        int
    long       int
}

func newPlayer(playerType, dressType string) *player {
    dress, _ := getDressFactorySingleInstance().getDressByType(dressType)
    return &player{
        playerType: playerType,
        dress:      dress,
    }
}

func (p *player) newLocation(lat, long int) {
    p.lat = lat
    p.long = long
}

main.go

package main

import "fmt"

func main() {
    game := newGame()
    //Add Terrorist
    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)
    game.addTerrorist(TerroristDressType)
    //Add CounterTerrorist
    game.addCounterTerrorist(CounterTerrroristDressType)
    game.addCounterTerrorist(CounterTerrroristDressType)
    game.addCounterTerrorist(CounterTerrroristDressType)
    dressFactoryInstance := getDressFactorySingleInstance()
    for dressType, dress := range dressFactoryInstance.dressMap {
        fmt.Printf("DressColorType: %s\nDressColor: %s\n", dressType, dress.getColor())
    }
}

Output:

DressColorType: ctDress
DressColor: green
DressColorType: tDress
DressColor: red

The post Flyweight Design Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/flyweight-design-pattern-golang/feed/ 2 361
Composite Design Pattern in Go (GoLang) https://vikasboss.github.io/composite-design-pattern-golang/ https://vikasboss.github.io/composite-design-pattern-golang/#comments Mon, 04 Nov 2019 06:11:45 +0000 https://vikasboss.github.io/?p=344 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Definition:  This is a structural design...

The post Composite Design Pattern in Go (GoLang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Definition: 

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.
Let’s try to understand it with an example of a file system of OS. In the file system, there are two types of objects File and Folder. There are cases when File and Folder are treated to be the same way. It will be more clear as we go along.


When to Use

  • 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.

                – In our example above of the file system, let’s say search operation of a particular keyword needs to be executed. Now, this search operation applies to both File and Folder. For a File, it will just look into the contents of the file and for a Folder, it will go through all files in the hierarchy in that folder to find that keyword

  • Use this pattern when the composite and individual object form a tree-like structure

                –  In our example, File and Folder do form a tree structure

UML Diagram

  • Component – It is the interface which defines the common operations for both the Composite and Leaf objects
  • Composite – It implements Component interface and embeds an array of child Components
  • Leaf – it is the primitive object in the tree. It also implements the Component Interface

Below is the corresponding mapping UML diagram with the example given above

Mapping 

The below table represents the mapping from the UML diagram actors to actual implementation actors in code.

Component interfacecomponent.go
Compositefolder.go
Leaffile.go
clientmain.go

Practical Example

In the example below component is the interface and file and folder implement this interface.

component.go

package main

type component interface {
    search(string)
}

folder.go

package main

import "fmt"

type folder struct {
    components []component
    name       string
}

func (f *folder) search(keyword string) {
    fmt.Printf("Serching recursively for keyword %s in folder %s\n", keyword, f.name)
    for _, composite := range f.components {
        composite.search(keyword)
    }
}

func (f *folder) add(c component) {
    f.components = append(f.components, c)
}

file.go

package main

import "fmt"

type file struct {
    name string
}

func (f *file) search(keyword string) {
    fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
}

func (f *file) getName() string {
    return f.name
}

main.go

package main

func main() {
    file1 := &file{name: "File1"}
    file2 := &file{name: "File2"}
    file3 := &file{name: "File3"}
    folder1 := &folder{
        name: "Folder1",
    }
    folder1.add(file1)
    folder2 := &folder{
        name: "Folder2",
    }
    folder2.add(file2)
    folder2.add(file3)
    folder2.add(folder1)
    folder2.search("rose")
}

Output:

Serching recursively for keyword rose in folder Folder2
Searching for keyword rose in file File2
Searching for keyword rose in file File3
Serching recursively for keyword rose in folder Folder1
Searching for keyword rose in file File1

The post Composite Design Pattern in Go (GoLang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/composite-design-pattern-golang/feed/ 2 344
Adapter Design Pattern in Go (GoLang) https://vikasboss.github.io/adapter-design-pattern-go/ https://vikasboss.github.io/adapter-design-pattern-go/#comments Fri, 01 Nov 2019 13:50:07 +0000 https://vikasboss.github.io/?p=334 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Introduction: This design pattern is a...

The post Adapter Design Pattern in Go (GoLang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Introduction:

This design pattern is a Structural Design Pattern. The patter is best understood with an example. Let’s say you have two laptops

  1. MacBook Pro
  2. Windows Laptop

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

Problem:

  • 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)

This is where Adapter Pattern comes into the picture. We create a class known as Adapter that will

  • Adhere to the same interface which client expects ( Square USB port here)
  • 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.

When to Use

  • Use this design pattern when the objects implement a different interface as required by the client.

UML Diagram

Below is the corresponding mapping UML diagram with the example given above

Mapping 


The below table represents the mapping from the UML diagram actors to actual implementation actors in code.

Targetcomputer.go
Concrete Prototype 1mac.go
Concrete Prototype 2 (Adapter)windowsAdapter.go
adapteewindows.go
clientclient.go

Example:

computer.go

package main

type computer interface {
    insertInSquarePort()
}

mac.go

package main

import "fmt"

type mac struct {
}

func (m *mac) insertInSquarePort() {
    fmt.Println("Insert square port into mac machine")
}

windowsAdapter.go

package main

type windowsAdapter struct {
	windowMachine *windows
}

func (w *windowsAdapter) insertInSquarePort() {
	w.windowMachine.insertInCirclePort()
}

windows.go

package main

import "fmt"

type windows struct{}

func (w *windows) insertInCirclePort() {
    fmt.Println("Insert circle port into windows machine")
}

client.go

package main

type client struct {
}

func (c *client) insertSquareUsbInComputer(com computer) {
    com.insertInSquarePort()
}

main.go

package main

func main() {
    client := &client{}
    mac := &mac{}
    client.insertSquareUsbInComputer(mac)
    windowsMachine := &windows{}
    windowsMachineAdapter := &windowsAdapter{
        windowMachine: windowsMachine,
    }
    client.insertSquareUsbInComputer(windowsMachineAdapter)
}

Output:

Insert square port into mac machine
Insert circle port into windows machine

The post Adapter Design Pattern in Go (GoLang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/adapter-design-pattern-go/feed/ 2 334
Prototype Pattern in Go (Golang) https://vikasboss.github.io/prototype-pattern-go/ https://vikasboss.github.io/prototype-pattern-go/#comments Sun, 20 Oct 2019 04:45:36 +0000 https://vikasboss.github.io/?p=305 Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang) Definition: It is a creational design...

The post Prototype Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

Definition:

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.

The object to be cloned exposes a clone method which returns a clone copy of the object

When to Use

  • We use prototype pattern when the object to be cloned creation process is complex i.e the cloning may involve vases of handling deep copies, hierarchical copies, etc. Moreover, there may be some private members too which cannot be directly accessed.
  • A copy of the object is created instead of creating a new instance from scratch. This prevents costly operations involved while creating a new object such as database operation.
  • When you want to create a copy of a new object, but it is only available to you as an interface. Hence you cannot directly create copies of that object.

UML Diagram

Mapping 

The below table represents the mapping from the UML diagram actors to actual implementation actors in code.

prototype interfaceinode.go
Concrete Prototype 1file.go
Concrete Prototype 2folder.go
clientmain.go

Practical Example:

In the context of golang let’s try to understand it with an example of os file system. The os file system has files and folders and folders itself contain files and folders. Each file and folder can be represented by an inode interface. inode interface also has the clone() function.

inode.go

package main

type inode interface {
    print(string)
    clone() inode
}

file struct is represented as

file.go

package main

import "fmt"

type file struct {
	name string
}

func (f *file) print(indentation string) {
	fmt.Println(indentation + f.name)
}

func (f *file) clone() inode {
	return &file{name: f.name + "_clone"}
}

folder struct is represented as

folder.go

package main

import "fmt"

type folder struct {
	childrens []inode
	name      string
}

func (f *folder) print(indentation string) {
	fmt.Println(indentation + f.name)
	for _, i := range f.childrens {
		i.print(indentation + indentation)
	}
}

func (f *folder) clone() inode {
	cloneFolder := &folder{name: f.name + "_clone"}
	var tempChildrens []inode
	for _, i := range f.childrens {
		copy := i.clone()
		tempChildrens = append(tempChildrens, copy)
	}
	cloneFolder.childrens = tempChildrens
	return cloneFolder
}

Since both file and folder struct implements the print and clone functions, hence they are of type inode. Also, notice the clone function in both file and folder. The clone function in both of them returns a copy of the respective file or folder. While cloning we append the keyword “_clone” for the name field. Let’s write the main function to test things out.

main.go

package main

import "fmt"

func main() {
    file1 := &file{name: "File1"}
    file2 := &file{name: "File2"}
    file3 := &file{name: "File3"}
    folder1 := &folder{
        childrens: []inode{file1},
        name:      "Folder1",
    }
    folder2 := &folder{
        childrens: []inode{folder1, file2, file3},
        name:      "Folder2",
    }
    fmt.Println("\nPrinting hierarchy for Folder2")
    folder2.print("  ")
    cloneFolder := folder2.clone()
    fmt.Println("\nPrinting hierarchy for clone Folder")
    cloneFolder.print("  ")
}

Output:

Printing hierarchy for Folder2
  Folder2
    Folder1
        File1
    File2
    File3

Printing hierarchy for clone Folder
  Folder2_clone
    Folder1_clone
        File1_clone
    File2_clone
    File3_clone

The post Prototype Pattern in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/prototype-pattern-go/feed/ 2 305
Builder Pattern in GoLang https://vikasboss.github.io/builder-pattern-golang/ https://vikasboss.github.io/builder-pattern-golang/#comments Wed, 04 Sep 2019 17:58:52 +0000 https://vikasboss.github.io/?p=243 Definition: Builder Pattern is a creational design pattern used for constructing complex objects. Below is the UML diagram. Note: Interested in understanding how all other design patterns can be implemented in GO....

The post Builder Pattern in GoLang appeared first on Welcome To Golang By Example.

]]>
Definition:

Builder Pattern is a creational design pattern used for constructing complex objects. Below is the UML diagram.

Note: Interested in understanding how all other design patterns can be implemented in GO. Please see this full reference – All Design Patterns in Go (Golang)

UML Diagram

Mapping (Also Refer 5th point – Example)

Directordirector.go
Builder InterfaceiBuilder.go
Concrete Builder 1normalBuilder.go
Concrete Builder 2iglooBuilder.go
Producthouse.go

When To Use

  • 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
  • When a different version of the same product needs to be created. For example, in the below code we see a different version of house ie. igloo and the normal house being constructed by iglooBuilder and normalBuilder
  • When half constructed final object should not exist. Again referring to below code the house created will either be created fully or not created at all. The Concrete Builder struct holds the temporary state of house object being created

Example:

iBuilder.go

package main

type iBuilder interface {
    setWindowType()
    setDoorType()
    setNumFloor()
    getHouse() house
}

func getBuilder(builderType string) iBuilder {
    if builderType == "normal" {
        return &normalBuilder{}
    }
    if builderType == "igloo" {
        return &iglooBuilder{}
    }
    return nil
}

normalBuilder.go

package main

type normalBuilder struct {
    windowType string
    doorType   string
    floor      int
}

func newNormalBuilder() *normalBuilder {
    return &normalBuilder{}
}

func (b *normalBuilder) setWindowType() {
    b.windowType = "Wooden Window"
}

func (b *normalBuilder) setDoorType() {
    b.doorType = "Wooden Door"
}

func (b *normalBuilder) setNumFloor() {
    b.floor = 2
}

func (b *normalBuilder) getHouse() house {
    return house{
        doorType:   b.doorType,
        windowType: b.windowType,
        floor:      b.floor,
    }
}

iglooBuilder.go

package main

type iglooBuilder struct {
    windowType string
    doorType   string
    floor      int
}

func newIglooBuilder() *iglooBuilder {
    return &iglooBuilder{}
}

func (b *iglooBuilder) setWindowType() {
    b.windowType = "Snow Window"
}

func (b *iglooBuilder) setDoorType() {
    b.doorType = "Snow Door"
}

func (b *iglooBuilder) setNumFloor() {
    b.floor = 1
}

func (b *iglooBuilder) getHouse() house {
    return house{
        doorType:   b.doorType,
        windowType: b.windowType,
        floor:      b.floor,
    }
}

house.go

package main

type house struct {
    windowType string
    doorType   string
    floor      int
}

director.go

package main

type director struct {
    builder iBuilder
}

func newDirector(b iBuilder) *director {
    return &director{
        builder: b,
    }
}

func (d *director) setBuilder(b iBuilder) {
    d.builder = b
}

func (d *director) buildHouse() house {
    d.builder.setDoorType()
    d.builder.setWindowType()
    d.builder.setNumFloor()
    return d.builder.getHouse()
}

main.go

package main

import "fmt"

func main() {
    normalBuilder := getBuilder("normal")
    iglooBuilder := getBuilder("igloo")

    director := newDirector(normalBuilder)
    normalHouse := director.buildHouse()

    fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
    fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
    fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)

    director.setBuilder(iglooBuilder)
    iglooHouse := director.buildHouse()

    fmt.Printf("\nIgloo House Door Type: %s\n", iglooHouse.doorType)
    fmt.Printf("Igloo House Window Type: %s\n", iglooHouse.windowType)
    fmt.Printf("Igloo House Num Floor: %d\n", iglooHouse.floor)
}

Output:

Normal House Door Type: Wooden Door
Normal House Window Type: Wooden Window
Normal House Num Floor: 2

Igloo House Door Type: Snow Door
Igloo House Window Type: Snow Window
Igloo House Num Floor: 1

The post Builder Pattern in GoLang appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/builder-pattern-golang/feed/ 2 243