{
  "slug": "Factory-Design-Patterns-and-SOLID-Principles-in-Golang-6aa6a60cc543",
  "title": "Factory Design Patterns and SOLID Principles in Golang",
  "subtitle": "Design patterns and SOLID principles are fundamental concepts in software engineering that can greatly enhance the quality…",
  "excerpt": "Design patterns and SOLID principles are fundamental concepts in software engineering that can greatly enhance the quality…",
  "date": "2024-08-06",
  "tags": [
    "Go",
    "Career"
  ],
  "readingTime": "8 min",
  "url": "https://medium.com/@mobinshaterian/factory-design-patterns-and-solid-principles-in-golang-6aa6a60cc543",
  "hero": "https://cdn-images-1.medium.com/max/800/1*zBhWdEQ_hNWVJ0b-9NhhuA.jpeg",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Factory Design Patterns and SOLID Principles in Golang"
    },
    {
      "type": "paragraph",
      "html": "Design patterns and SOLID principles are fundamental concepts in software engineering that can greatly enhance the quality, maintainability, and scalability of code. This essay explores their application in Golang, focusing on the benefits of design patterns, the key SOLID principles, and practical implementations of the Factory and Abstract Factory patterns."
    },
    {
      "type": "paragraph",
      "html": "Design patterns in Golang offer numerous advantages. They promote code reusability by providing proven solutions to common problems, enhance scalability through loose coupling and high cohesion, improve readability by using recognizable structures, and offer flexibility for future modifications. Additionally, they encourage best practices, potentially improve performance through optimized solutions, and foster consistency across projects and teams."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*zBhWdEQ_hNWVJ0b-9NhhuA.jpeg",
      "alt": "Factory Design Patterns and SOLID Principles in Golang",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "paragraph",
      "html": "The SOLID principles, a cornerstone of object-oriented design, are equally applicable in Golang:"
    },
    {
      "type": "paragraph",
      "html": "<strong>1. Single Responsibility Principle (SRP)</strong>: Each module should have only one reason to change, focusing on a single actor or stakeholder.<br><strong>2. Open-Closed Principle (OCP):</strong> Software entities should be open for extension but closed for modification.<br><strong>3. Liskov Substitution Principle (LSP):</strong> Objects of a superclass should be replaceable with objects of its subclasses without affecting program correctness.<br><strong>4. Interface Segregation Principle (ISP):</strong> Clients should not be forced to depend on interfaces they don’t use.<br><strong>5. Dependency Inversion Principle (DIP):</strong> High-level modules should not depend on low-level modules; both should depend on abstractions."
    },
    {
      "type": "paragraph",
      "html": "The Factory Method pattern, a creational design pattern, provides an interface for creating objects in a superclass while allowing subclasses to alter the type of objects created. This pattern is particularly useful when dealing with families of related objects. The provided example demonstrates a “Mark Factory” that randomly creates different types of guns, showcasing how the Factory Method can be implemented in Golang."
    },
    {
      "type": "paragraph",
      "html": "The Abstract Factory pattern takes this concept further by allowing the production of families of related objects without specifying their concrete classes. This pattern is illustrated through a sports equipment example, where different brands (Nike and Adidas) produce their own lines of shoes and shirts."
    },
    {
      "type": "paragraph",
      "html": "Both patterns demonstrate the power of abstraction and polymorphism in Golang, allowing for flexible and extensible code structures. They encapsulate object creation logic, promoting loose coupling between client code and concrete implementations."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Why use design patterns in Golang?"
    },
    {
      "type": "paragraph",
      "html": "1. Code reusability: Design patterns provide proven solutions to common problems, allowing developers to reuse efficient code structures."
    },
    {
      "type": "paragraph",
      "html": "2. Scalability: They help create more scalable and maintainable code by promoting loose coupling and high cohesion."
    },
    {
      "type": "paragraph",
      "html": "3. Readability: Using well-known patterns makes code more recognizable and easier to understand for other developers."
    },
    {
      "type": "paragraph",
      "html": "4. Flexibility: Design patterns often simplify modifying or extending code without major refactoring."
    },
    {
      "type": "paragraph",
      "html": "5. Best practices: They encourage following established best practices in software design."
    },
    {
      "type": "paragraph",
      "html": "6. Efficiency: Many patterns are optimized solutions, potentially improving performance."
    },
    {
      "type": "paragraph",
      "html": "7. Consistency: Using patterns promotes a consistent approach across a project or team."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "SOLID Principles"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Single Responsibility"
    },
    {
      "type": "paragraph",
      "html": "The <strong>single-responsibility principle</strong> (<strong>SRP</strong>) is a computer programming principle that states that “A module should be responsible to one, and only one, actor.”<a href=\"https://en.wikipedia.org/wiki/Single-responsibility_principle#cite_note-1\" target=\"_blank\" rel=\"noreferrer noopener\">[1]</a> The term actor refers to a group (consisting of one or more stakeholders or users) requiring a module change."
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Open Closed Principle"
    },
    {
      "type": "paragraph",
      "html": "In object-oriented programming, the open-closed principle (OCP) states, “software entities (classes, modules, functions, etc.) should be open for extension but closed for modification”;[1] that is, such an entity can allow its behavior to be extended without modifying its source code."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*Kb6PYl9sfODwSq9Fqj7IYA.png",
      "alt": "Factory Design Patterns and SOLID Principles in Golang",
      "caption": "",
      "width": 594,
      "height": 274
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Liskov substitution principle"
    },
    {
      "type": "paragraph",
      "html": "The Liskov Substitution Principle (LSP) is one of the five SOLID principles of object-oriented programming. It states that objects of a superclass should be replaceable with objects of a subclass without affecting the program's correctness. In other words, a subclass should be able to be used interchangeably with its parent class without causing unexpected behavior."
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Interface segregation principle"
    },
    {
      "type": "paragraph",
      "html": "<strong>A client should never be forced to implement an interface it doesn’t use or depend on methods it doesn't</strong> use."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*zHLeSecHen8pqgo8Pw2kLA.png",
      "alt": "Factory Design Patterns and SOLID Principles in Golang",
      "caption": "",
      "width": 676,
      "height": 197
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Dependency Inversion"
    },
    {
      "type": "paragraph",
      "html": "The Dependency Inversion Principle (DIP) <strong>emphasizes decoupling and abstraction</strong>. The principle consists of two core concepts: high-level modules should not depend on low-level modules, and both should depend on abstractions. This inverted dependency relationship promotes flexibility, testability, and maintainability."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "What is the difference between pointer receivers and receivers?"
    },
    {
      "type": "paragraph",
      "html": "Receivers allow you to associate a method with a type, and they come in two flavors: value receivers and pointer receivers. Understanding the differences between these two types of receivers is crucial for designing efficient and correct Go programs."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "What are Receivers?"
    },
    {
      "type": "paragraph",
      "html": "In Go, a receiver is a parameter of a method that binds the method to a specific type. This is similar to what is commonly known as “this” or “self” in other programming languages. By using a receiver, you can define methods that operate on instances of the associated type. Receivers can be applied to both value and pointer types."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Choosing Between Value and Pointer Receivers"
    },
    {
      "type": "paragraph",
      "html": "The choice between value and pointer receivers depends on the use case and the behavior you want to achieve. Here are some considerations:"
    },
    {
      "type": "paragraph",
      "html": "<strong>Value Receivers:</strong>"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Use value receivers when the method doesn’t need to modify the instance’s state and operates purely on a copy of the instance.",
        "Value receivers are ideal for methods that are read-only and don’t mutate the internal state of the type."
      ]
    },
    {
      "type": "paragraph",
      "html": "<strong>Pointer Receivers:</strong>"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Use pointer receivers when the method needs to modify the instance’s state directly.",
        "Pointer receivers are essential when you want to modify the underlying state of a struct or any other type."
      ]
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Initial Project"
    },
    {
      "type": "code",
      "lang": "bash",
      "code": "go mod init"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Simple Impeliment of Factory design Pattern"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Dependency Injection"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*3ZVSq7lzeSt_RiBP_92IaQ.png",
      "alt": "https://medium.com/@ankitpal181/service-repository-pattern-802540254019",
      "caption": "https://medium.com/@ankitpal181/service-repository-pattern-802540254019",
      "width": 773,
      "height": 416
    },
    {
      "type": "paragraph",
      "html": "In dependency Injection, we inject the repository into the service layer instead of directly using the database in the service layer."
    },
    {
      "type": "paragraph",
      "html": "main‍.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package main\nimport (\n\"go-example/repository\"\n\"go-example/service\"\n)\nfunc main() {\n    repository := repository.New(\"mysql\") service := service.New(\"user\", repository) service.Run()\n}"
    },
    {
      "type": "paragraph",
      "html": "service.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package serviceimport \"go-example/repository\"type Service struct {\n    ServiceName string\n    Model *repository.DB\n}\nfunc New(serviceName string, model *repository.DB) *Service {\n    return &Service{\n        ServiceName: serviceName, Model: model,\n    }\n}\nfunc (s *Service) Run() {\n    println(\"Service is running\")\n}"
    },
    {
      "type": "paragraph",
      "html": "repository.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package repositorytype DB struct {\n    RepositoryName string\n}\nfunc New(repositoryName string) *DB {\n    return &DB{\n        RepositoryName: repositoryName,\n    }\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Factory Pattern"
    },
    {
      "type": "embed",
      "provider": "youtube",
      "url": "https://www.youtube.com/embed/EcFVTgRHJLM"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Factory Method"
    },
    {
      "type": "paragraph",
      "html": "<strong>Factory Method</strong> is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created."
    },
    {
      "type": "paragraph",
      "html": "The Factory Method pattern suggests replacing direct object construction calls (using the <code>new</code> operator) with calls to a special <em>factory</em> method. Don’t worry: the objects are still created via the <code>new</code> operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as <em>products.</em>"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*vfAsBBjc8DVs-pWsCW2fOw.png",
      "alt": "https://refactoring.guru/design-patterns/factory-method",
      "caption": "https://refactoring.guru/design-patterns/factory-method",
      "width": 667,
      "height": 348
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*S2tATsFbd9fpH3tKHyNd6g.png",
      "alt": "https://refactoring.guru/design-patterns/factory-method",
      "caption": "https://refactoring.guru/design-patterns/factory-method",
      "width": 490,
      "height": 250
    },
    {
      "type": "paragraph",
      "html": "Suppose that we are going to design a game. In this game, we have a factory named Mark Factory. In this factory, two different types of guns are randomly created. The factory makes them with special distribution. We can only order guns. The factory will give us a list of guns."
    },
    {
      "type": "paragraph",
      "html": "main.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package main\nimport (\n\"go-example/manufacture\"\n)\nfunc main() {\n    markCompany := manufacture.NewMarkCompany() for i := 0;\n    i <= 10;\n    i++ {\n        markCompany.AddGun()\n    }\n    markCompany.PrintGunList()\n}"
    },
    {
      "type": "paragraph",
      "html": "manufacture.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package manufacture\nimport (\n\"fmt\"\n\"go-example/gun\"\n\"math/rand\"\n)\ntype MarkCompany struct {\n    gunList []gun.IGun\n}\nfunc NewMarkCompany() *MarkCompany {\n    mk := MarkCompany{\n    }\n    mk.gunList = make([]gun.IGun, 0) return &mk\n}\nfunc (m *MarkCompany) AddGun() {\n    myRand := rand.Intn(2) //Random distribution if myRand == 0 {\n        g := gun.NewAK47() m.gunList = append(m.gunList, g)\n    }\n    else {\n        g := gun.NewShotgun() m.gunList = append(m.gunList, g)\n    }\n}\nfunc (m *MarkCompany) PrintGunList() {\n    for _, g := range m.gunList {\n        fmt.Println(g.GetName(), g.GetPower())\n    }\n}"
    },
    {
      "type": "paragraph",
      "html": "shotgun.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package guntype Shotgun struct {\n    Gun\n}\nfunc NewShotgun() IGun {\n    return &Shotgun{\n        Gun: Gun{\n            name: \"Shotgun\", power: 10,\n        },\n    }\n}"
    },
    {
      "type": "paragraph",
      "html": "AK47.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package guntype AK47 struct {\n    Gun\n}\nfunc NewAK47() IGun {\n    return &AK47{\n        Gun: Gun{\n            name: \"AK47\", power: 5,\n        },\n    }\n}"
    },
    {
      "type": "paragraph",
      "html": "gun.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package guntype IGun interface {\n    setName(name string) GetName() string setPower(power int) GetPower() int\n}\ntype Gun struct {\n    name string\n    power int\n}\nfunc (g *Gun) setName(name string) {\n    g.name = name\n}\nfunc (g *Gun) GetName() string {\n    return g.name\n}\nfunc (g *Gun) setPower(power int) {\n    g.power = power\n}\nfunc (g *Gun) GetPower() int {\n    return g.power\n}"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*68xFQDFxd98TtPQX-d9UtQ.png",
      "alt": "Factory Design Patterns and SOLID Principles in Golang",
      "caption": "",
      "width": 221,
      "height": 219
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Abstract Factory"
    },
    {
      "type": "paragraph",
      "html": "<strong>Abstract Factory</strong> is a creational design pattern that lets you produce families of related objects without specifying their concrete classes."
    },
    {
      "type": "paragraph",
      "html": "The abstract factory pattern in software engineering is a design pattern that provides a way to create families of related objects without imposing their concrete classes by encapsulating a group of individual factories that have a common theme without specifying their concrete classes."
    },
    {
      "type": "embed",
      "provider": "youtube",
      "url": "https://www.youtube.com/embed/v-GiuMmsXj4?feature=oembed"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*c7LYVUhOSGFLAZcZAt6EqQ.png",
      "alt": "Factory method pattern",
      "caption": "Factory method pattern",
      "width": 765,
      "height": 545
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*nTXVzbbqxCi4hm1l08LsLw.png",
      "alt": "Factory Abstract Pattern",
      "caption": "Factory Abstract Pattern",
      "width": 626,
      "height": 526
    },
    {
      "type": "paragraph",
      "html": "Suppose that we have 2 different products. product Sauce and product Chips. product Sauce has been made in 3 different ways and products Chips has been made in 4 differents way. On the other hand, we have Chips Factories look like pringles, they only make a chips with special combinations. Not all combination has accepted in Chips factory."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*d_MiNHkVsHUyHEioW8M1lg.png",
      "alt": "https://refactoring.guru/design-patterns/factory-method",
      "caption": "https://refactoring.guru/design-patterns/factory-method",
      "width": 700,
      "height": 512
    },
    {
      "type": "paragraph",
      "html": "factory.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package factory\nimport (\n\"go-example/abstract/shirt\"\n\"go-example/abstract/shoe\"\n)\ntype ISportFactory interface {\n    MakeShoe() shoe.IShoe MakeShirt() shirt.IShirt GetShoe() shoe.IShoe GetShirt() shirt.IShirt\n}"
    },
    {
      "type": "paragraph",
      "html": "shirt.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package shirttype IShirt interface {\n    SetLogo(logo string) GetLogo() string\n}\ntype Shirt struct {\n    logo string\n}\nfunc NewShirt(logo string) Shirt {\n    return Shirt{\n        logo: logo,\n    }\n}\nfunc (s *Shirt) SetLogo(logo string) {\n    s.logo = logo\n}\nfunc (s *Shirt) GetLogo() string {\n    return s.logo\n}"
    },
    {
      "type": "paragraph",
      "html": "shoe.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package shoetype IShoe interface {\n    SetLogo(logo string) SetSize(size int) GetLogo() string GetSize() int\n}\ntype Shoe struct {\n    logo string\n    size int\n}\nfunc NewShoe(logo string, size int) Shoe {\n    return Shoe{\n        logo: logo, size: size,\n    }\n}\nfunc (s *Shoe) SetLogo(logo string) {\n    s.logo = logo\n}\nfunc (s *Shoe) GetLogo() string {\n    return s.logo\n}\nfunc (s *Shoe) SetSize(size int) {\n    s.size = size\n}\nfunc (s *Shoe) GetSize() int {\n    return s.size\n}"
    },
    {
      "type": "paragraph",
      "html": "nike.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package factory\nimport (\n\"go-example/abstract/shirt\"\n\"go-example/abstract/shoe\"\n)\ntype Nike struct {\n    shoe shoe.IShoe\n    shirt shirt.IShirt\n}\ntype NikeShoe struct {\n    shoe.Shoe\n}\ntype NikeShirt struct {\n    shirt.Shirt\n}\nfunc NewNike() *Nike {\n    nike := Nike{\n    }\n    nike.shirt = nike.MakeShirt() nike.shoe = nike.MakeShoe() return &nike\n}\nfunc (n *Nike) MakeShoe() shoe.IShoe {\n    return &NikeShoe{\n        shoe.NewShoe(\"nike\", 20),\n    }\n}\nfunc (n *Nike) MakeShirt() shirt.IShirt {\n    return &NikeShirt{\n        shirt.NewShirt(\"nike\"),\n    }\n}\nfunc (n *Nike) GetShoe() shoe.IShoe {\n    return n.shoe\n}\nfunc (n *Nike) GetShirt() shirt.IShirt {\n    return n.shirt\n}"
    },
    {
      "type": "paragraph",
      "html": "adidas.go"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "package factory\nimport (\n\"go-example/abstract/shirt\"\n\"go-example/abstract/shoe\"\n)\ntype Adidas struct {\n    shoe shoe.IShoe\n    shirt shirt.IShirt\n}\ntype AdidasShoe struct {\n    shoe.Shoe\n}\ntype AdidasShirt struct {\n    shirt.Shirt\n}\nfunc NewAdidas() *Adidas {\n    adidas := Adidas{\n    }\n    adidas.shoe = adidas.MakeShoe() adidas.shirt = adidas.MakeShirt() return &adidas\n}\nfunc (a *Adidas) MakeShoe() shoe.IShoe {\n    return &AdidasShoe{\n        shoe.NewShoe(\"adidas\", 20),\n    }\n}\nfunc (a *Adidas) MakeShirt() shirt.IShirt {\n    return &AdidasShirt{\n        shirt.NewShirt(\"adidas\"),\n    }\n}\nfunc (a *Adidas) GetShoe() shoe.IShoe {\n    return a.shoe\n}\nfunc (a *Adidas) GetShirt() shirt.IShirt {\n    return a.shirt\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "All codes are exists in this repository"
    }
  ]
}
