This repository contains list of design pattern implemented in Golang Reference:
- Abstract Factory
- Use this when we want to handle with a lot kind of objects but also we want to omit dependency to concrete class when trying to create the objects
- Benefit:
- We can be sure that objects created by the factory will be compatible with each other
- We can avoid tight coupling between client and concrete class
- Open Closed Principle: we can easily add objects without changing the existing code
- Single Responsibility Principle: creation of objects only handled in one class
- Factory
- Use this when:
- We don't know exactly which object we are going to be depend on:
- Factory design pattern separates construction of objects and the code that uses the object, so any addition of objects can be done easily
- We provide a library: we wanted the client to be able to put their own object plugin with they provide their own behaviour of the object
- We can reuse objects
- It's better to use factory's createObjects() method instead of constructor, since constructor should always return new object.
- for example if we already created database connections and we want to reuse the connections
- We don't know exactly which object we are going to be depend on:
- Benefit:
- Avoid tight coupling between concrete object and business logic code
- Single Responsibility Principle
- We separate objects creation into separate place, so that the object creator only have 1 reason to change
- Open Closed Principle
- We can easily add objects with its behaviour without changing the business logic code
- Use this when:
- Builder
- Use this when:
- We need flexibility in creating objects
- There are many parameters for creating an object and sometimes not all attribute is used.
- We want to create multiple objects with the similar steps of creation.
- Builder interface provides steps of creating object, and director class guides the process of building it
- Constructing Composite object (we can create recursive objects)
- We need flexibility in creating objects
- Benefit:
- Reuse builder interface to build several objects with similar steps
- Build recursive objects
- Single Responsibility Principle: we can isolate building objects and business logic
- Use this when:
- Prototype
- Use this when:
- We need to have a copy for an object, and copying a complex object will be such a hassle
- We need a copy for the object but it only exposes method for copying it through an interface. And also, the object doesn't expose all the attributes
- Benefit:
- We can clone objects without depending to the concrete object class (copying through an interface)
- We can copy complex objects easily
- Use this when:
- Singleton
- Use this when:
- We want an object to be used in many part of the code.
- We want to have stricter control over global variable
- The only one who can create / access the variable is the singleton class itself
- Disadvantage:
- Violate single responsibility principle.
- The code now depends on singleton class and if the singleton class and the business logic change, we have more than 1 reason to change the class.
- Hard to unit test since we depend on the singleton
- solution: Maybe we can find way to mock it or just don't use this pattern
- Requires extra attention to multithreading programming such that the object is guaranteed only created once
- The part of code know to much about the other code. Bad design in general
- Violate single responsibility principle.
- Use this when:
- Chain of Responsibility
- Use this when:
- We want to have several actions and it's too big to be handled in one handler
- In this process we can do it step by step, asking each handler whether they are responsible to do it, or the other handler's job
- We want to execute several handler in a certain order
- We want to dynamically set the handler order
- We want to have several actions and it's too big to be handled in one handler
- Benefit:
- We can control the order of the handler
- Single Responsibility Principle
- Previously, the request is handled in a big handler, and if we separate the handler into separate handlers, then each handler will only change if there is only 1 reason to change.
- Open Closed Principle
- We can introduce new handler into the code without breaking the application / changing the business logic.
- Use this when:
- Command
- We should use this:
- We should separate concern of handling request and show something in frontend
- Showing something is the job of frontend code, and handling request must be processed in backend, and any request must go through 1 router, and it will route to specific interface implementation to handle the request.
- We should separate concern of handling request and show something in frontend
- Benefit:
- Single Responsibility Principle
- We can create each request handler and each request handler only change on 1 reason only
- Open Closed Principle
- We can easily add command implementation without changing main business logic code.
- Single Responsibility Principle
- We should use this:
- Iterator
- Use this when:
- We want to traverse a data structure and the data structure is unknown beforehand
- We want to traverse data structure easily and we want to hide the data structure and its complexity from client
- Benefit:
- Open Closed Principle
- We can add another data structure and its iterator without changing the business logic code
- Single Responsibility Principle
- For each data structure, the way to traverse the data structure is different. Even the same data structure can be iterated differently (tree, DFS & BFS).
- Therefore, it's better to create iteration logic in a separate class and it will only change on 1 reason.
- We can iterate many data structures in parallel since each iterator has it's own state
- Open Closed Principle
- Use this when:
- Mediator
- Use this when:
- There are a lot of components that are dependant to each other. This condition makes the component hard to reuse
- Example: checkbox in form if checked must tell text field to fill dogs name. Checkbox is dependant to text field.
- It's hard to change a class because the class is tightly coupled to the other classes.
- Change to this class might also means that we need to change the other classes that we are depending.
- There are a lot of components that are dependant to each other. This condition makes the component hard to reuse
- Benefit:
- Open Closed Principle
- When we add another component and it needs different handler for action into the component, we can just add another mediator class.
- Single Responsibility Principle
- We can move the communication between components into single place in mediator class
- We can reduce coupling between components.
- More component reuse
- Open Closed Principle
- Disadvantage:
- It can become a God Class, controlling everything
- Use this when:
- Memento
- Use this when:
- You want to add undo / redo operation in your application
- Benefit:
- We can produce snapshot without breaking encapsulation
- Disadvantage:
- Big RAM usage when we need to store all the history
- Extra care for garbage collection when we want to discard old memento
- Use this when:
- Observer
- Use this when:
- We want to have pub-sub look alike application. The subscriber list is dynamic, some subscribers sometimes only last for a particular time.
- Benefit:
- Open Closed Principle
- We can add another subscribers without changing the business logic code.
- Open Closed Principle
- Use this when:
- State
- Use this when:
- We want to implement objects that has behaviour depending on its state.
- Benefit:
- Single Responsibility Principle
- Since we have each state in a different class, then it will only have 1 reason to change
- Open Closed Principle
- We can add another state class without changing the business logic code.
- We can eliminate many state conditionals (many if(s))
- Single Responsibility Principle
- Disadvantage:
- It can be overkill if the state of an object is just a few
- Use this when:
- Strategy
- Use this when:
- We encounter some similar classes with the difference is just the way the class behave.
- We are relying to much to conditional statements of almost similar code of different behaviours.
- Benefit:
- Open Closed Principle
- We can easily add more strategies without changing the business logic code.
- We can separate business logic code from algorithm implementation details
- Open Closed Principle
- Use this when: