Как упоминалось в первой статье, чтобы сделать код пригодным для тестирования, нам нужно использовать внедрение зависимостей для создания наших объектов, и обычно мыmain.go
сделать внедрение зависимостей, что приводит кmain.go
будет становиться все более и более раздутым. Чтобы модульное тестирование проходило гладко,main.go
За счет своего якобы стройного и стройного тела. слишком толстыйmain.go
Плохой сигнал, в этой статье будет представлена структура внедрения зависимостей (wire), предназначенная для помощиmain.go
Вернуться в форму.
раздутый основной
существуетmain.go
Выполнение внедрения зависимостей в , означает, что в коде инициализации мы должны управлять:
- Зависимый порядок инициализации
- отношения между зависимостями
Для небольших проектов количество зависимостей относительно невелико, кода инициализации не так много, и нет необходимости вводить фреймворк внедрения зависимостей. Однако для средних и крупных проектов с большим количеством зависимостей код инициализации вонючий и длинный, а читабельность и ремонтопригодность становятся очень плохими.
func main() {
config := NewConfig()
// db依赖配置
db, err := ConnectDatabase(config)
if err != nil {
panic(err)
}
// PersonRepository 依赖db
personRepository := NewPersonRepository(db)
// PersonService 依赖配置 和 PersonRepository
personService := NewPersonService(config, personRepository)
// NewServer 依赖配置和PersonService
server := NewServer(config, personService)
server.Run()
}
Практика показала, что изменение кода инициализации с большим количеством зависимостей — утомительное и трудоемкое занятие. В настоящее время нам нужна структура внедрения зависимостей, чтобы помочь и упростить код инициализации.
Приведенный выше код взят из:blog.Drew Olson.org/dependency-…
Использовать инфраструктуру внедрения зависимостей — провод
What is wire?
Wire — это платформа внедрения зависимостей с открытым исходным кодом от Google. Или, цитируя официальные слова: «Wire — это инструмент генерации кода, которыйautomates connecting components using dependency injection".
Why wire?
В дополнение к проводу, инфраструктура внедрения зависимостей Go также имеет Uber.digи Facebookinject, все они используют механизм отражения для реализации внедрения зависимостей во время выполнения (внедрение зависимостей во время выполнения), а wire использует генерацию кода для внедрения внедрения зависимостей во время компиляции (внедрение зависимостей во время компиляции). Во-вторых, снижение производительности при использовании отражения, и, что более важно, отражение затрудняет отслеживание и отладку кода (отражение сделает недействительным сочетание клавиш Ctrl+left...). Код, сгенерированный проводом, соответствует повседневным привычкам программистов, и его очень легко понять и отладить.
Что касается преимуществ проволоки, более подробное описание есть в официальном блоге:blog.golang.org/wire
How does it work?
Эта часть контента относится к официальному сообщению в блоге:blog.golang.org/wire
wire имеет два основных понятия: провайдер и инжектор.
provider
Провайдер — это обычная функция Go, которую можно рассматривать как конструктор объекта, через провайдер мы сообщаем проводу зависимость объекта:
// NewUserStore是*UserStore的provider,表明*UserStore依赖于*Config和 *mysql.DB.
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
// NewDefaultConfig是*Config的provider,没有依赖
func NewDefaultConfig() *Config {...}
// NewDB是*mysql.DB的provider,依赖于ConnectionInfo
func NewDB(info ConnectionInfo) (*mysql.DB, error) {...}
// UserStoreSet 可选项,可以使用wire.NewSet将通常会一起使用的依赖组合起来。
var UserStoreSet = wire.NewSet(NewUserStore, NewDefaultConfig)
injector
Инжектор — это функция, генерируемая wire.Мы получаем нужный нам объект или значение, вызывая инжектор.Инжектор будет вызывать функцию провайдера по порядку согласно зависимостям:
// File: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
// initUserStore是由wire生成的injector
func initUserStore(info ConnectionInfo) (*UserStore, error) {
// *Config的provider函数
defaultConfig := NewDefaultConfig()
// *mysql.DB的provider函数
db, err := NewDB(info)
if err != nil {
return nil, err
}
// *UserStore的provider函数
userStore, err := NewUserStore(defaultConfig, db)
if err != nil {
return nil, err
}
return userStore, nil
}
Инжектор помогает нам делать шаги инициализации зависимостей по порядку, мы вmain.go
нужно только позвонитьinitUserStore
метод, чтобы получить объект, который мы хотим.
Так откуда wire знает, как генерировать инжекторы? Нам нужно написать функцию, чтобы сказать это:
- Определите сигнатуру функции инжектора
- использовать по назначению
wire.Build
Метод перечисляет поставщиков, необходимых для создания инжектора.
Например:
// initUserStore用于声明injector的函数签名
func initUserStore(info ConnectionInfo) (*UserStore, error) {
// wire.Build声明要获取一个UserStore需要调用到哪些provider函数
wire.Build(UserStoreSet, NewDB)
return nil, nil // 这些返回值wire并不关心。
}
С помощью вышеуказанной функции провод может знать, как создать инжектор. Шаги инжектора генерации проволоки описаны следующим образом:
- Определите сигнатуру сгенерированной функции инжектора:
func initUserStore(info ConnectionInfo) (*UserStore, error)
- Первый параметр возвращаемого значения восприятия:
*UserStore
- экзамен
wire.Build
список, найти*UserStore
провайдер:NewUserStore
- подписан функцией
func NewUserStore(cfg *Config, db *mysql.DB)
учитьсяNewUserStore
зависит от*Config
, и*mysql.DB
- экзамен
wire.Build
список, найти*Config
и*mysql.DB
провайдер:NewDefaultConfig
иNewDB
- подписан функцией
func NewDefaultConfig() *Config
учиться*Config
Других зависимостей нет. - подписан функцией
func NewDB(info *ConnectionInfo) (*mysql.DB, error)
учиться*mysql.DB
зависит отConnectionInfo
. - экзамен
wire.Build
список, не нашелConnectionInfo
поставщика, но соответствующий тип входного параметра находится в сигнатуре функции инжектора, и этот параметр используется непосредственно какNewDB
ввод. - Возвращаемое значение восприятия Второй параметр
error
- ....
- По зависимостям последовательно вызываются функции-провайдеры и собираются функции-инжекторы.
взять каштан
Каштановый портал:wire-examples
Уведомление
На момент публикации этой статьи официальные лица указали, что статус проекта wire — альфа, что еще не подходит для производственных сред, и API может измениться.
Несмотря на то, что это альфа-версия, ее основная функция заключается в создании для нас кода внедрения зависимостей.Сгенерированный код очень прост для понимания.При условии хорошего контроля версий, даже если API изменится, это не нанесет большого ущерба среде генерации. , Влияние. Я думаю, что это все еще безопасно для использования.
в заключении
Эта статья является последней в этой серии.Оглядываясь назад на предыдущие статьи, основываясь на принципах и основных идеях модульного тестирования, мы представили метод тестирования на основе таблиц, gomock, testify, wire и другие практические инструменты. непрерывная оптимизация от «написания модульных тестов» до «написания хороших модульных тестов». Я надеюсь, что эта серия статей может быть полезной для вас.