Skip to content

Latest commit

 

History

History
100 lines (76 loc) · 8.88 KB

File metadata and controls

100 lines (76 loc) · 8.88 KB

Модификаторы

Как было написано в других главах - сервисы позволяют получать объект не только по типу, но и по дополнительным критериям. В принципе список всех этих критериев, по которым можно получить экземпляр объекта и является сервисом. Но выделяется два вида критериев: основной тип и модификатор. Модификатор - дополнительная информация к типу объекта для получения экземпляра объекта. Бывают трех видов:

  • Теги
  • Имена
  • Множественная

Теги

Тег - это типизированный модификатор. То есть любой тип в программе является тегом. Я рекомендую использовать protocol для декларирования тегов, но никто не запрещает использовать структуры или классы. Единственное что не используйте typealias - Да имя вроде другое, но, к сожалению, для swift распознать это имя очень тяжеловесная операция и поэтому от нее отказались в пользу более быстрой. Тег является наиболее предпочтительным модификатором по сравнению с именем, так как:

  • Может быть легко изменён во всей программе с помощью средств рефакторинга, которые в XCode9 начали работать.
  • Имеет область видимости - Если получить объект нельзя в стороннем модуле, но базовый тип при этом известен, тэги могут спасти.
  • Поддержаны библиотекой - то есть их синтаксис короткий, и работает во всех случаях.

Дальше будет приведен пример кода, как объявлять теги и зарегистрировать/получить несколько классов/объектов с разными тегами:

protocol YourTag1 {}
protocol YourTag2 {}

container.register(YourClass.init)
.as(YourProtocol.self, tag: YourTag1.self)
.as(YourProtocol.self, tag: YourTag2.self)

let obj1: YourClass = container.resolve(tag: YourTag1.self)
let obj2: YourClass = by(tag: YourTag2.self, on: *container)

Мы зарегистрировали свой класс так, что он доступен по двум тегам. В реальной программе это нужно достаточно редко, но хочу обратить внимание - если объект имеет время жизни singleton, то поведение старой версии и новой будет отличаться - в старой версии для каждого тега создавался свой экземпляр класса, что слегка не соответствуем тому, что это синглетон. В новой версии это был убрано, и если надо получить один и тот же класс, но в нескольких экземплярах по разным тегам, то можно написать несколько почти одинаковых регистраций.

Выше я писал, что теги поддержаны библиотекой - посмотрите на то, как получается второй объект - такой синтаксис можно использовать в любом месте:

// в методе инициализации
container.register{ YourClass(p: by(tag: YourTag.self, on: $0) }
// во внедрении зависимостей
.injection{ $0.param = by(tag: YourTag.self, on: $1) }

По этой причине я настоятельно рекомендую не использовать старый способ получения объекта по имени, а использовать новый - он более универсальный.

Начиная с версии 3.1.3 появилась возможность использовать несколько тегов одновременно. Это будет означать что выберется тот объект который соответствует сразу всем тегам. Для этого можно использовать следующий вариант синтаксиса:

// этот вариант может быть расширен на любое количество тегов
let obj: YourClass = by(tag: YourTag1.self, by(tag: YourTag2.self, on: *container))

// этот вариант может максиум содержать 3 тега - впринципе зачем больше?
let obj: YourClass = by(tags: YourTag1.self, YourTag2.self, on: *container)

Имена

Имя - это строковый модификатор. То есть любая строка может быть именем для модификатора. Имя и теги очень сильно похожи, но имена имеют меньше возможностей, и все варианты будут представлены ниже:

container.register(YourClass.init)
.as(YourProtocol.self, name: "your_name")
.injection(name: "other_name") { $0.paramByName = $1 }

let obj: YourClass = container.resolve(name: "your_name")

Да именно так, по имени нельзя:

  • Получить объект внутри метода инициализации
  • Получить объект внутри внедрения через метод

При этом можно:

  • Получить объект по имени на прямую из контейнера
  • Внедрить объект по имени с помощью внедрения через свойства

Множественная

Одна из самых интересных возможностей - так как никто более ее не поддерживает. Это такой же модификатор, который позволяет получить все экземпляры класса по некоторому типу. В прошлой главе был пример логгера, в этой я приведу пример с животными:

container.register(Cat.init)
.as(Animal.self)

container.register(Dog.init)
.as(Animal.self)

container.register(Hamster.init)
.as(Animal.self)

let animals1: [Animal] = container.resolveMany()
let animals2: [Animal] = many(*container)

container.register{ Desert(speciesOfAnimal: many($0)) }

container.register(Desert.init)
.injection{ $0.speciesOfAnimals = many($1) }

Видно, что этот модификатор поддерживается также хорошо, как и Теги. Стоит только напомнить про одну его особенность: в случае если класс имеет получение самого себя в методе инициализации через множественную регистрацию, то он просто не будет учтен в массиве объектов - его там не будет. То есть рекурсивной ссылки не будет.

Начиная с версии 3.1.3 этот модификатор стал сочетаться с тегами, то есть можно сказать: "Дай мне все объекты которые соответствуют некоторому тегу или тегам". Синтаксис для этой операции не отличается от уже существующего и выглядит следующих образом:

let animals: [Animal] = many(by(tag: YourTag1.self, on: *container))

При сочетании множественного модификатора и тегов надо учитывать одно правило - many должен быть завершающим модификатором и не может находится в середине условия. В принципе компилятор не даст скомпилировать по другому.