Skip to content

Latest commit

 

History

History
170 lines (115 loc) · 7.52 KB

README-ru.md

File metadata and controls

170 lines (115 loc) · 7.52 KB

ozzo-di

GoDoc Build Status Coverage

ozzo-di — это контейнер внедрения зависимостей (DI) на языке Go. Он имеет следующие возможности:

  • Внедрение зависимостей через конкретные типы, интерфейсы, и функции-провайдеры.
  • Внедрение зависимостей для значений параметров функций и полей структур.
  • Создание и внедрение новых объектов.
  • Иерархические контейнеры DI.

Требования

Go 1.2 или выше.

Установка

Выполните следующие команды для установки:

go get github.com/go-ozzo/ozzo-di

С чего начать

Следующий фрагмент кода показывает, как можно использовать DI контейнер.

package main

import (
	"fmt"
	"reflect"
	"github.com/go-ozzo/ozzo-di"
)

type Bar interface {
    String() string
}

func test(bar Bar) {
    fmt.Println(bar.String())
}

type Foo struct {
    s string
}

func (f *Foo) String() string {
    return f.s
}

type MyBar struct {
    Bar `inject`
}

func main() {
    // создаем DI контейнер
	c := di.NewContainer()

    // регистрируем экземпляр Foo как интерфейс типа Bar
    c.RegisterAs(&Foo{"hello"}, di.InterfaceOf((*Bar)(nil)))

    // &Foo{"hello"} будет внедрено как параметр Bar для test()
    c.Call(test)
    // Выведет:
    // hello

    // создаем объект MyBar и внедряем его в поле Bar
    bar := c.Make(reflect.TypeOf(&MyBar{})).(Bar)
    fmt.Println(bar.String())
    // Выведет:
    // hello
}

Регистрация типа

di.Container — это DI контейнер, полгающийся для определения значений для внедрения на типы. Для того, чтобы это работало, как правило, типы надо предварительно зарегистрировать. di.Container поддерживает три вида регистрации типа:

c := di.NewContainer()

// 1. регистрация конкретного типа:

// &Foo{"hello"} зарегистрирован в качестве соответствующего конкретного типа (*Foo)
c.Register(&Foo{"hello"})


// 2. регистрация интерфейса:

// &Foo{"hello"} зарегистрирован как интерфейс Bar
c.RegisterAs(&Foo{"hello"}, di.InterfaceOf((*Bar)(nil)))
// конкретный тип (*Foo) зарегистрирован как интерфейс Bar
c.RegisterAs(reflect.TypeOf(&Foo{}), di.InterfaceOf((*Bar)(nil)))


// 3. регистрация провайдера:

// Функция-провайдер зарегистрирована как Bar интерфейс.
// Она будет вызвана при внедрении Bar.
c.RegisterProvider(func(di.Container) interface{} {
    return &Foo{"hello"}
}, di.InterfaceOf((*Bar)(nil)), true)

Совет: Чтобы указать тип интерфейса при регистрации, используйте функцию-помощник di.InterfaceOf((*InterfaceName)(nil)). Для конкретных типов используйте рефлексию reflect.TypeOf(TypeName{}).

Внедрение значений

di.Container поддерживает три типа внедрения значений:

// ...продолжение предыдущего примера регистрации...

type Composite struct {
    Bar `inject`
}

// 1. внедрение в поле структуры:

// Значения будут внедрены в экспортированное поле структуры с тегом `inject` и анонимные поля.
// Значение &Foo{"hello"} будет внедрено в поле Composite.Bar
composite := &Composite{}
c.Inject(composite)


// 2. внедрение в параметр функции:

// Значениями в соответствии с их типами будут внедрены в параметры функции.
// Здесь &Foo{"hello"} будет внедрено в bar.
func test(bar Bar) {
    fmt.Println(bar.String())
}
c.Call(test)


// 3. создание новых экземпляров:
// Новые экземпляры структуры могут создаваться с внедрением их полей.
// Или может быть возвращён экземпляр-синглтон.

foo := c.Make(reflect.TypeOf(&Foo{})).(*Foo)          // возвращает синглтон &Foo{"hello"}
bar := c.Make(di.InterfaceOf((*Bar)(nil))).(*Bar)     // возвращает синглтон &Foo{"hello"}

// Возвращает новый экземпляр Composite с внедреным в Bar синглтоном &Foo{"hello"}
composite := c.Make(reflect.TypeOf(&Composite{})).(*Composite)

В том случае, если при внедрении зарегистрированного ранее типа значение уже зарегистрировано как этот тип, для внедрения будет использоваться само значение.

Если в качестве типа зарегистрирован провайдер, для внедрения будет использован результат вызова провайдера. Вы можете использовать третий параметр для Container.RegisterProvider() чтобы указать, будет провайдер вызываться каждый раз, когда необходимо внедрение, или только в первый раз. Во втором случае провайдер будет вызван только один раз и результат вызова будет использван для внедрения соответствующего зарегистрированного типа.

При внедрении значения не зарегистрированного типа T будет использована следующая стратегия:

  • Если *T был зарегистрирован, то соответствующее значение будет разыменовано и возвращено;
  • Если T является указателем на P, будет возващен указатель на внедрёное для P значение;
  • Если тип T — это структура, будет создан новый экземпляр и её поля будут внедрены;
  • Если T — это Slice, Map, или Chan, будет создан и инициализирован новый экземпляр;
  • Во всех остальных случаях будет возвращено нулевое значение.

Признательность

ozzo-di ссылается на реализацию codegansta/inject.