Pull to refresh
17
0
Кирилл @ws233

Руководитель мобильной разработки

Send message

Но ведь NSNotificationCenter – это ничто иное, как шаблон "Шина данных" ("Event Bus").

Разница в отсутствии типизации обусловлена лишь нюансами реализации. NSNotificationCenter реализован на языке без строгой типизации. Но это абсолютно не значит, что используя этот класс в Swift, Вы не можете получить строгую типизацию в шине данных.

Шаблон изучен на троечку. Применение строгой статической типизации к нестрого типизированным данным тоже. Пересдавать будете?ч

5 лет назад код на C++ без танцев с бубном собирался просто включением соответствующих файлов на C++ в ObjC-проект с изменением их расширения на .mm. Или даже и переименовывать не надо было, не помню уже -- старый стал. Полагаю, что и сейчас этот способ работает.

Этот же совет можно использовать и для работы со Swift ниже версии 5.9.

Ох, уж эти зуммеры, раз и навсегда похоронившие ObjC ^.^

И почему модульность нельзя реализовать исключительно на SPM?

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

Добрый день. У нас есть автоматическое формирование списка изменений, да. И даже автоматическое определение новой версии по анализу изменений в интерфейсе (Semver). Надеюсь, мы сможем обо всем этом рассказать в ближайшее время либо на Хабре, либо на Мобиусе.

Совет действенный. Подтверждаю. Как и то, что он часто используется молодыми техлидами. Но больно уж он дорогой для заказчика. Статья все же дешевле. :)

модульные тесты = юнит-тесты

Забавное следствие из OCP, о котором Вы не упомянули.

Если сохраняется стабильность протокола, то не повышается мажорная версия (расширение протокола повышает лишь минорную версию, определение Symver). Отсутствие изменения мажорной версии позволяет в многомодульных системах не менять зависимые модули. Таким образом мы добиваемся исполнения принципа инверсии зависимости за счет исполнения принципа OCP. Стоит нарушить принцип OCP в многомодульных системах (да и не только, просто в многомодульных это нагляднее, чем в связном монолите, но и в последнем эффект тот же) и вы теряете DIP и приобретаете зависимость верхних модулей от нижних. Что в свою очередь ведет к тому, что при изменении нижнего модуля вы вынуждены переписать полприложения.

Таким образом, следование OCP упрощает следование DIP. В статье вы указали на обратное утверждение: "Следование DIP упрощает следование LSP и OCP". Оно тоже верное. А отсюда следует, что начиная правильно использовать любой из этих принципов, вы, желая того или нет, вынуждены будете следовать всем трем. И на самом деле всем 5, ибо они все аналогично упрощают работу друг с другом. В итоге Вы получите синергию всех 5 методов, значительно ускоряющих вашу работу. Эти 5 методов взаимно не позволят вам нарушить ни один из них.

Такие забавные пирожки у тёти Клавы :)

За статью – респект. Примеры хорошие.

Поэтому не нужно защиты в общий доступ выкладывать. Одно дело про них прочитать человеческим языком, другое – сидеть и ковырять защищенный в несколько слоев бинарь.

Что будет с тестом, если функция loader.loadMovies() подвиснет?

Интересно, что "двунаправленная архитектура" легко преобразуется в "однонаправленную". Достаточно в схеме "двунаправленной архитектуры" тот блок, что между двумя оставшимися находится, поделить на 2 части: одна часть пропускает сигналы в одну сторону, вторая – в противоположную. да, получится 4 блока, а не 3, но смысл тот же. И описанное преобразование гораздо проще, чем переписывать какой-нить MVVM на MVI. Ну, и плюшки все получаете от однонаправленности в том же MVVM.

Только тсссс!

IMHO, как раз вот не все из функций контроллера SwiftUI умеет делать хорошо. Есть вещи, которые как раз существенно лучше реализуются через механизмы уже имеющиеся в контроллерах, но про которые разработчики почему-то забыли и пытаются их костылить в обертке SwiftUI. Но навязывать свое мнение не стану. В остальном согласен. Обёрточка вышла годной. Просто прошу не забывать (а если не знали, то узнать) о том, что под ней и что и так работает прекрасно.

Красиво пишите. Еще и ссылок много – видно, что стараетесь базой подтверждать каждую свою мысль. Продолжайте, таких авторов в иосе немного.

Разрешите добавлю по теме статьи.

Паттерны в SwiftUI не упоминаются, кмк, по одной простой причине – изменений нет. Это все тот же MVC. Но теперь это MVC в стандартной реализации, а не в модифицированной версии Cocoa от Apple.

Поясню.

Все знают MVC в виде с рисунка 7.2 (как раз модификация от Apple), но мало кто знает оригинальную модификацию с рисунка 7.1. Так вот, если Вы возьмете вашу картинку Архитектуру State-Model-View из статьи и дорисуете к ней сверху контроллер, как медиатор между View и Model, то Вы получите полностью канонический MVC с картинки 7.1. А в описании увидите и про биндинги, и про состояние.

"Но в SwiftUI нет контроллеров", – возразите Вы.

На что я отвечу, что они есть, просто Apple сделала их полностью абстрактными и скрытыми от программиста, переместив все, что раньше размещали в контроллере, на вью SwiftUI. Поэтому какой бы экран вы не верстали на SwiftUI, контроллер, рисующий экран, у вас все равно будет. Полностью абстрактный.

Отсюда, кстати, и очень важные следствия:

  1. А все ли из того, что традиционно размещают в контроллере возможно перенести на вью?

  2. Подсказка. Обратите внимание, какие взаимодействия идут напрямую от View к Model в MVC, а какие через Controller.

  3. А если что-то переносить не стоит, то как использовать SwiftUI с кастомизированным, а не абстрактным контроллером? А это возможно.

  4. А стоило ли Apple делать полностью абстрактный контроллер и прятать его от разработчиков? Не приведет ли это к путанице и новым связностям? – Подсказка: уже приводит.

  5. В итоге, глубокое понимание того, что должно быть в контроллере, а что во вью, кажется, не даст испортить подход на SwiftUI и повторить уже классические ошибки с MassiveSwiftUI. Вы правильно указали, что проблема MassiveViewController легко решается. Так вот сейчас сообществу необходимо будет понять эту разницу, чтобы не испортить всю идею MassiveSwiftUI подходом. Что-то из этого Вы уже тоже предложили.

Ваше мнение?

ПС: Apple еще и Coordinating Controller описывает, который очень перекликается с комбинациями визуальных элементов ("Combine" pattern) и управлением переходами, которые вы описываете.

Зависимости в коде бывают ооооочень разными.

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

Конкретно в обоих этих примерах не устранена скрытая контекстная зависимость. ViewModel вынуждена знать и говорить на языке View: все эти методы "didSearch", "didSelect", "viewDidLoad" и замыкания (названные в статье "кложурами") для вызова View кода из ViewModel прям кричат о том, что они опустились туда, куда не должны были.

Это приводит к нарушению единой ответственности - при изменении View, приходится менять и вьюмодель вот из-за таких неявных контекстных зависимостей.

Также теряется возможность переиспользовать вьюмодель на других платформах, где логика отображения будет другой и не будет иметь метода viewDidLoad (т.е. мы в нашу вьюмодель внесли зависимость от платформы, под которую написан UI). Например, будет тяжело переиспользовать эту ViewModel в консольном приложении.

Исправляется довольно просто. В обоих случаях ViewModel становится свободной о знании деталей реализации View.

  1. Из ViewModel удаляются замыкания. ViewModel просто возвращает управление вьюхе из той функции, где вызывалось замыкание, а уже View коммуницирует с координатором и осуществляет нужный переход.

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

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

В остальном – статья глубокая. Автор молодец. Переводчик тоже – добро пожаловать в расширенный аккаунт на Хабре :)

Не соглашусь с тем, что он приведен "чисто для примера". Но соглашусь с тем, что он "формирует запросы в интерактор".

Контроллер у дядюшки Боба (да и не только у него, это часто встречающаяся практика) осуществляет ту же функцию, что и презентер, только в обратную сторону. Контроллер и презентер у дяди Боба – это прослойка между двумя независимыми слоями, разделенная по направлению преобразования на независимые сущности. Просто гляньте где у него в кольце контроллер, а где презентер. Контроллер обычно работает с вводом пользователя, а презентер – наоборот – с выводом ему информации. Это реализация принципа инверсии зависимостей, порт-адаптер (преобразование) и единой ответственности (направление). Не зря на картинке и то, и другое отображено одинаково, только с разным направлением управляющего сигнала.

Без такого адаптера "Вид" в VIP будет все знать о бизнес-логике, а значит, и на идее независимости слоев, и на идее повторного переиспользования (на которых построена вся книга), можно ставить крест. В направлении от "Вида" к бизнес логике VIP потерял промежуточное звено, потерял адаптер, приобрел жесткую зависимость "Вида" от бизнес-логики.

Не утверждаю, что это неверно и неправильно.

Просто, похоже, что VIP – это не "чистая архитектура" от дяди Боба. В чистой архитектуре вот к той сноске в правом нижнем углу нужно просто слева подрисовать представление ("Вид") и замкнуть граф в кольцо.

Вы не считаете это ошибкой реализации VIP относительно описанных у дяди Боба колец с адаптерами?

У дядюшки Боба в его чистой архитектуре присутствует контроллер. Поглядите хотя бы на ту картинку, которую Вы привели в своей статье. В ее правом нижнем углу имеется контроллер. А вот куда контроллер делся в VIP, который якобы является реализацией чистой архитектуры с этой картинки? И почему Вы продолжаете с этой же ошибкой в Вашей архитектуре?

Потому что, как выше написали, абстракции нужны НЕ для переиспользования кода. Протокол прекрасно выполняет роль абстракции. Точка. Для этого создавать еще и абстрактные классы совсем нет необходимости.

Для переиспользования кода нужно использовать другие инструменты.

Автору:

Не пытайтесь забивать гвозди отверткой.

За системный подход к деталям Вам – огромный плюс. Статья получилась глубокая.

А вот за то, что утонули в этих деталях, забыли про взгляд с высоты птичьего полета и начали применять инструмент не по назначению, хотя рядом лежит тот инструмент, что Вам нужен – минус. Попробуйте прикинуть, какими еще инструментами надо получать переиспользование кода и сравните, что получится – вот это будет интересная и полезная для сообщества статья.

Спасибо за Ваши усилия.

Очень опасная обертка.

  1. 2 объекта одного класса не будут синхронизированы между собой.

  2. Нет потокобезопасности.

Вывод: Использовать обертку можно только с синглтонами.

Но об этом ни в статье, ни в описании кода обертки – ни слова.

Исключение, лишь подтверждающее правило?

Значит, им там и место :)

А больше нигде и не нужно. И все попытки натянуть сову на глобус – неправильны в самой постановке реализации.

Либо пользуемся имеющимися контроллерами, либо создаем свой универсальный... Но мне сложно придумать такой универсальный контроллер, в котором мне бы пришлось добавлять поддержку работы с клавиатурой. Имеющихся двух вполне достаточно для решения проблемы, описанной в статье. Да и любой другой проблемы с клавиатурой.

1
23 ...

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Works in
Date of birth
Registered
Activity