Хорошо, я попробую.
Вы сказали "нет синглтона", поэтому я исключаю это в следующем, но, пожалуйста, также посмотрите на нижнюю часть этого ответа.
Комментарий Джоша Хоманна уже является хорошим указателем для одного решения, но лично у меня есть проблемы с шаблоном координатора.
Как правильно сказал Джош, контроллеры представления не должны (много) знать друг о друге [1], но тогда как, например, координатор или какая-либо зависимость передается / доступна?Есть несколько шаблонов, которые предлагают, как, но у большинства есть проблема, которая в основном идет вразрез с вашим требованием: они более или менее делают координатора синглтоном (либо сами по себе, либо как свойство другого синглтона, такого как AppDelegate
).Координатор также часто де-факторирует и синглтон (но не всегда, и это не обязательно).
Я склонен полагаться на простые инициализированные свойства или (чаще всего) ленивые свойства и протоколно-ориентированное программирование.Давайте построим пример: UserService
должен быть протоколом, определяющим все функциональные возможности, необходимые для вашей службы, MyUserService
его реализующую структуру.Давайте предположим, что UserService
является конструктивной конструкцией, которая в основном функционирует как система получения / установки для некоторых пользовательских данных: токены доступа (например, сохраненные в цепочке для ключей), некоторые предпочтения (URL-адрес изображения аватара) и тому подобное.При инициализации MyUserService
также подготавливает данные (например, загружает с пульта).Это должно использоваться на нескольких независимых экранах / контроллерах представления и не является одиночным.
Теперь у каждого контроллера представления, который заинтересован в доступе к этим данным, есть простое свойство:
lazy var userService: UserService = MyUserService()
Я держу это публично, потому что это позволяет мне легко смоделировать / заглушить его в модульных тестах (если мне нужно это сделать, я могу создать пустышку TestUserService
, которая высмеивает / заглушает поведение).Инстанцирование также может быть закрытием, которое я могу легко отключить во время теста, если init требует параметры.Очевидно, что свойства не обязательно должны быть lazy
в зависимости от того, что на самом деле делают объекты.Если создание экземпляра объекта раньше времени не причиняет вреда (помните о модульных тестах, а также об исходящих соединениях), просто пропустите lazy
.
Трюк явно предназначен для дизайнаUserService
и / или MyUserService
таким образом, чтобы не создавать проблем при создании нескольких его экземпляров .Однако я обнаружил, что в действительности это не проблема в 90% случаев, поскольку фактические данные, на которые должен полагаться экземпляр, сохраняются где-то еще, в единой точке истины, например, в цепочке для ключей, стеке основных данных,пользовательские настройки по умолчанию, или удаленный бэкэнд.
Я знаю, что это своего рода ответ на отказ, так как я просто говорю, описывая подход, который (по крайней мере, часть) многих общих шаблонов там.,Однако я обнаружил, что это самая общая и простая форма для подхода к внедрению зависимостей в Swift.Шаблон координатора можно использовать ортогонально к нему, но я обнаружил, что он менее «похож на Apple» при повседневном использовании.Это решает проблему, но в основном ту, которую вы получаете, вы неправильно используете раскадровки, как они предназначены (особенно: просто используйте их как «репозитории VC», создайте их экземпляры оттуда и перенесите себя в код).
[1] За исключением некоторых базовых и / или второстепенных вещей, которые вы можете передать в обработчике завершения или prepareForSegue
.Это спорно и зависит от того, насколько строги вы будете следовать координатор или другой шаблон.Лично я иногда использую ярлык, если он не раздувается и становится грязным.Некоторые всплывающие окна проще сделать таким образом.
В качестве заключительного замечания фраза «Заметьте, что мы хотим избежать Синглтона», а также ваш комментарий относительно этого вопроса дают мне впечатление, что вы просто следовали этому совету, не задумываясь об обосновании.Я знаю, что «Синглтон» часто считается антипаттерном, но так же часто это суждение неверно информировано.Синглтон может быть допустимой архитектурной концепцией (что видно по тому, что он широко используется в фреймворках и библиотеках).Плохая вещь в том, что это слишком часто побуждает разработчиков использовать ярлыки в дизайне и использовать его как «хранилище объектов», чтобы им не приходилось думать о том, когда и где создавать объекты.Это приводит к беспорядку и плохой репутации шаблона.
A UserService
, в зависимости от того, что на самом деле делает в вашем приложении, может быть хорошим кандидатом на синглтон.Мое личное эмпирическое правило таково: «Если оно управляет состоянием чего-то особенного и уникального, например, определенного пользователя, который может быть только в одном состоянии в данный момент времени», я мог бы пойти на одиночный.
Особенно, если вы не можете спроектировать его так, как я обрисовал выше, то есть , если вам нужно иметь в памяти данные единственного состояния , синглтон в основном простой и правильный способ реализовать это.(Даже тогда, когда использование (ленивых) свойств выгодно, вашим контроллерам представления даже не нужно знать, является ли это синглтоном или нет, и вы все равно можете по отдельности заглушить / смоделировать его (т.е. не только глобальный экземпляр).)