0%

GO_Learning_Fundamental_Dependency_Injection

Reference: DI Wiki, DIP,YouTuBe, YouTuBe, Blog, Blog

What

What is dependency injection, the literal meaning is Inject the dependencies.

  • What is Dependency?

    The Dependency is other object that your class need functional.

  • What is Injection?

    Injection means you pushed the dependency into your class.

    • Recommend: push dependency as parameter or use setter method;
    • Not Recommend: new dependency in your class;

In the wiki introduction, the Dependency injection involves four roles, which i will use it to explain the DI concept;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- the **service** objects to be used
Any object that may be used can be considered a service.
- the **client** object, whose behavior depends on the services it uses
Any object that uses other objects can be considered a client.
- the [**interfaces**](https://en.wikipedia.org/wiki/Interface_(object-oriented_programming)) that define how the client may use the services
The interfaces are the types the client expects its dependencies to be.
- the **injector**, which constructs the services and injects them into the client
The injector introduces services to the client. Often, it also constructs the client. An injector may connect a complex object graph by treating the same object as both a client at one point and as a service at another

As an analogy,
- service - an electric, gas, hybrid, or diesel car
- client - a driver who uses the car the same way regardless of the engine
- interface - [automatic transmission](https://en.wikipedia.org/wiki/Automatic_transmission), which ensures the driver does not have to understand details of shifting gears
- injector - the parent who bought the car for the driver and decided which kind

Why

The reason why DI is important? DIP dependency inversion principle;

Example

The above description is too abstract to understand; In one word, DI is a kind of design mode:

1
2
The intent behind dependency injection is to achieve separation of concerns of construction and use of objects. This can increase readability and code reuse.
- wiki

Here is a scenario, a server API would use DB model to get some information;

  • Bad programming style

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    type Service struct {
    // some fields definition
    }

    func (s *Service)GetData(id string) string{
    db := new(mysql)
    return db.queryData(id)
    }

    func NewService() *Service{
    return &Service{}
    }

    service := NewService()
    service.Run()
  • After Dependency Injection

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    type Service struct {
    // some fields definition
    db *MYSQLClient
    }

    func (s *Service)GetData(id string) string{
    return s.db.queryData(id)
    }

    func NewService() *Service{
    return &Service{db: NewMySQLClient(mySqlUrl)}
    }

    service := NewService("mysql://url")
    service.Run()
  • After Dependency Inversion Principle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    type DataSource interface {
    GetDataById(id string) string
    }

    type mySql struct {
    m *MySqlClient
    }

    func (m *mysql) GetDataById(id string) string {
    return m.m.Exec("select data from table where id = ?", id)
    }

    func NewMySQL(addr string) *mySql {
    return &mySql{m: NewMySQLClient(addr)}
    }

    type redis struct {
    r *RedisClient
    }

    func (r *RedisClient) GetDataById(id string) string {
    return r.r.Do("GET", "table_"+id)
    }

    func NewRedis(addr string) *redis {
    return &redis{r: NewRedisClient(addr)}
    }

    type Service struct {
    // some fields definition
    db DataSource
    }

    func (s *Service)GetData(id string) string{
    return s.db.GetDataById(id)
    }

    func NewService(addr string) *Service{
    // Redis
    return &Service{db: NewRedis(myRedisUrl)}
    // Mysql
    return &Service{db: NewMySQL(myRedisUrl)}
    }

    service := NewService()
    service.Run()