Разбор лайвкода в Т-Банк на 450к / Android собеседование — Transcript
Full transcript
- 0:00Всем привет я Артур мобильный
- 0:01разработчик в этом видео Я разберу
- 0:03решение реальной задачи собеседования на
- 0:06позицию Android разработчика в т банке
- 0:09осенью того года на меня вышел Чар из т
- 0:11банка предлагали вилку в районе 400.000
- 0:14руб и я решил пройти собеседование после
- 0:17первого общения с HR и обсуждения моего
- 0:19опыта меня решили прогнать по упрощённой
- 0:22системе всего лишь с одним техническим
- 0:24собеседованием другие разработчики
- 0:26проходят больше технических этапов Я
- 0:28хочу показать не только решение но и
- 0:31постараюсь раскрыть подход к подобным
- 0:33задачам и разберу темы которые в ней
- 0:35затрагиваются Лайф кода на
- 0:37собеседованиях становится всё больше
- 0:40потому что компании хотят понять Можете
- 0:42ли вы бегло ориентироваться в коде хотят
- 0:45проверить знания конкретных тем умеете
- 0:48ли вы выяснять требования объяснять ход
- 0:50своих мыслей и непосредственно решать
- 0:53поставленные перед вами задачи в этом
- 0:55видео разбор только одной из реальных
- 0:57задач но у моих учеников есть
- 0:59эксклюзивный доступ к разбору более чем
- 1:01пти задач из таких компаний как Яндекс
- 1:04Авито azon Абан wiber и других крупных
- 1:07техов перейдём к задаче которую мне
- 1:10давали что мы здесь видим здесь много
- 1:13плохо написанного кода для начала
- 1:16хотелось бы понять его главную идею что
- 1:20здесь вообще происходит Давайте
- 1:22попробуем посмотреть на него Ну вот так
- 1:24сверху вниз есть какой-то
- 1:28object с нй индексом есть г есть класс
- 1:35User Data с дишни ком именем и номером
- 1:38телефона это всё выглядит как лирика
- 1:41которую надо будет исправлять видимо
- 1:43Центральный класс здесь и Судя по
- 1:46строчкам кода он самый большой это users
- 1:49Holder singl Tone давайте взглянем что
- 1:51здесь происходит у нас есть Companion
- 1:54object с инсо этого класса есть метод
- 2:00Get instance который непосредственно
- 2:03этот класс возвращает есть какие-то поля
- 2:07users execut и метод update который
- 2:11принимает айдини пользователя его номер
- 2:14телефона его форматирует здесь какая-то
- 2:17Лирика и затем делает поиск этого айди
- 2:21Ника в массиве если нашёл то заменяет
- 2:24значение новым номером телефона Окей то
- 2:27есть здесь главный идея этого класса в
- 2:31том что у него есть метод update для
- 2:33изменения номера телефона и он хранит
- 2:36юзеров То есть sone он потому что
- 2:40реализует паттерн Гоф sone ЛР потому что
- 2:46хранит список пользователей и на
- 2:48самом-то деле главное за что он отвечает
- 2:51- это изменение номера телефона поэтому
- 2:53я бы Ещё назвал его апдейтер Или
- 2:57апдейтер давайте это сделаем
- 3:01US Up
- 3:03Hold где-то плачет Роберт марсон Ну
- 3:05кажется что здесь нам придётся
- 3:07исправлять всё подряд как будто бы можно
- 3:10это делать Прямо сверху вниз так смотрим
- 3:12на ко где у нас это используется тут нам
- 3:15в помощь Ctrl F Ага в переборе списка
- 3:21пользователей проходим по индексам Ну мы
- 3:24знаем что у нас вве в кот
- 3:27индексация поэтому
- 3:30Нам нужно поменять на ноль и как будто
- 3:33бы не нужно выносить это в отдельную
- 3:36константу и можно написать прямо вот так
- 3:38и это убрать дальше Раз уж мы тут начали
- 3:42смотреть этот for то у нас здесь выход
- 3:44за пределы массива потому что индексация
- 3:47у нас от нуля до Сай -1 поэтому можно
- 3:51написать Вот таким образом либо есть
- 3:54функция until которая за нас делает
- 3:58работу и э будет проходиться от нуля до
- 4:01Сай -1 либо можно сделать это ещё проще
- 4:05и написать for I until users indices
- 4:11простите за мой английский дальше у нас
- 4:13есть тег где он
- 4:16используется а в логе и тогда непонятно
- 4:20А зачем этот тег вынесли на Top level
- 4:26уровень как будто бы можно это перенести
- 4:28в Companion object непосредственно в
- 4:32класс и заменить
- 4:35на компилятор ную константу const W А
- 4:40зачем это нужно const - это компилятор
- 4:43ная Константа то есть значение
- 4:45вычисляется на этапе компиляции и
- 4:50заменяется в месте использования что
- 4:52ускоряет выполнение кода в ранта О'Кей
- 4:55Давайте пойдём дальше у нас есть класс
- 4:59User
- 5:00сразу видим что здесь изменяемые
- 5:03свойства этого класса Ну кто хоть
- 5:06немного работал с котле знает что Бест
- 5:08практи сом является использование дата
- 5:10классов Ну почему как минимум мы получим
- 5:13equals и шкод для использования в
- 5:15коллекциях которые базируются на
- 5:17вычислении хэш-функции от ключей и
- 5:19сравнении объектов дополнительно мы
- 5:22получим метод копи для создание нового
- 5:24инстанса с изменёнными полями класса и
- 5:28вот это ко которая принимает в себя
- 5:33старый р ID и номер телефона для
- 5:36изменения и затем создаёт новый инстанс
- 5:39нам на это намекает А если сделать все
- 5:41поля неизменяемыми то класс станет
- 5:43иммутабельность
- 6:00Давайте же
- 6:07[музыка]
- 6:09поменяем идём дальше к нашему главному
- 6:13классу он Open то есть открыт для
- 6:17наследования но внутри этого класса все
- 6:21методы и поля Они то есть мы насле этот
- 6:26класс ничего не сможем переде
- 6:30уме не делать его открытым для
- 6:33наследования сделать его смотрим дальше
- 6:36у нас конструктор Ну это для того чтобы
- 6:39мы создавали экземпляр класса в методе
- 6:43тут всё окей смотрим Что передаётся в
- 6:47Конструктор передаётся контекст и
- 6:50репозиторий про передачу контекста Я
- 6:53думаю поговорим отно миним э сде при
- 7:00было доступа и соблюсти
- 7:06инкапсуляция Давайте перейдём Дальше он
- 7:09наследуется от р
- 7:12провайдер Видимо для того чтобы получить
- 7:16Вот это значение для Гера уместно ли
- 7:21наследовать sone
- 7:24от Я думаю
- 7:28нече вопрос про разницу композиции и
- 7:32наследования В общем случае предпочтения
- 7:34стоит отдавать композиции а не
- 7:36наследованию наследование стоит
- 7:38использовать когда у классов есть чёткая
- 7:40связь из A при наследовании мы берём всё
- 7:43из суперкласса это может привести к тому
- 7:45что у нас будет функциональность которая
- 7:47нам не нужна А это уже нарушение
- 7:49принципа Interface segregation при этом
- 7:51при композиции мы зависим только той
- 7:53функциональности которую сами вызываем
- 7:57также наследование ломает капсуляции
- 8:00хороший пример приведён в книгах
- 8:01effective Java и effective cin в котором
- 8:04был создан Наследник Хеш сеете в котором
- 8:07просто был подсчёт количества Всех
- 8:09добавляемых элементов За всё время мы
- 8:11там
- 8:14переопределить на один а в методе Add
- 8:17All суммировать на размер добавляемое
- 8:19коллекции Казалось бы всё Логично но
- 8:22если у этого наследника Хеш Сета вызвать
- 8:24метод Add All то результат будет в два
- 8:27раза больше ожидаемого потому что под
- 8:29капотом шс в методе Add All вызывается
- 8:32метод Add этой проблемы можно было бы
- 8:34избежать Когда наш новый сет содержит в
- 8:37себя Хеш сеет а не является его
- 8:39наследником Ну и композиция более гибкая
- 8:42потому что наследовать можно один класс
- 8:45а содержать в себе можно сколько угодно
- 8:47и при наследовании мы берём всё из
- 8:49суперкласса и у нас меняется поведение
- 8:51всех дочерних классов а при композиции
- 8:54Конечно можно за
- 8:59из зависимого класса используется и у
- 9:01нас конечно же написаны на этот тесты
- 9:03при надобности мы вообще можем замакима
- 9:06класс поэтому здесь я с большим
- 9:09удовольствием не буду наследоваться от
- 9:11loger провайдер
- 9:13а
- 9:15сделаю буду передавать его в
- 9:20Конструктор и буду получать лоер из лоер
- 9:25провайдер Давайте разбираться что
- 9:26происходит вот здесь с методом Get
- 9:30instance с получением экземпляра этого
- 9:32класса во-первых у нас метод Get
- 9:35instance содержится не в comp обк а в
- 9:38самом классе и поэтому чтобы его вызвать
- 9:41нам придётся как-то создать экземпляр
- 9:44этого класса и только потом мы сможем
- 9:46вызвать метод Get это не статичный метод
- 9:49поэтому его нужно перенести в comp
- 9:53object Окей давайте
- 9:56форматируем код сразу же
- 10:00дальше instance у нас латай Что здесь
- 10:04происходит получается в нём мы проверяем
- 10:06что если инс пустой точнее лабильный мы
- 10:10его создаём и только после этого
- 10:13возвращаем на самом деле здесь может
- 10:16произойти Race Con Если два потока
- 10:19одновременно зайдут в этот метод и
- 10:22первый прочитает значение instance оно
- 10:25будет нула бельм второй тоже зайдёт в
- 10:28этот метод прочитает значение instance
- 10:30оно будет нула бельм оба выполнят
- 10:32проверку и оба создадут
- 10:35экземпляр этого класса и у нас будут
- 10:39невалидные данные А мы хотим создать
- 10:42экземпляр только строго один раз кстати
- 10:45здесь нужно поменять название класса и
- 10:48здесь ещё такой момент что в параметр
- 10:52метода передаётся л в этом нет никакого
- 10:56смысла этот код не
- 10:59в параметры метода мы передаём ссылки на
- 11:04экземпляры каких-то классов и Вал у нас
- 11:06используются только в первичном
- 11:09конструкторе чтобы сделать передаваемый
- 11:11параметр свойством класса здесь этого
- 11:14делать Нет смысла Да по поводу проблемы
- 11:17с рейс кондишен как же нам сделать так
- 11:19чтобы при использовании этого метода в
- 11:22многопоточное среде у нас не создавалось
- 11:26несколько экземпляров этого класса ну
- 11:29первое что мне приходит в голову можно
- 11:31просто пометить этот метод Синай Что
- 11:34будет в таком случае здесь у нас будет
- 11:36страдать перф потому что мы хотим
- 11:38создать экземпляр всего лишь один раз а
- 11:41все остальные разы уже читать значение
- 11:44Как по мне чтобы сделать этот код более
- 11:47эффективным нужно использовать паттерн
- 11:50многопоточного программирования под
- 11:52названием Double Checking для чего он
- 11:54нужен для того чтобы ограничить доступ к
- 11:56критической секции доступ этот должен
- 11:59быть на запись По какому-то условию и мы
- 12:02хотим при выполнении вот этого условия
- 12:05делать эту операцию строго один раз а
- 12:08все остальные разы когда эта операция
- 12:11когда эта проверка не выполняется мы не
- 12:13хотим выполнять никаких синхронизации
- 12:15что здесь нам нужно сделать нам нужно
- 12:17убрать synchronized потому что это
- 12:18страдает потому что это affect
- 12:20performance что нам нужно сделать после
- 12:22проверки что instance равен Ну следите
- 12:25за руками я выставляю блок сирона
- 12:30здесь ещё раз делаю вот эту
- 12:34проверку и уже после этого
- 12:38Я заполняю экземпляр инстанса это на
- 12:43самом деле реализация паттерна dou
- 12:45Checking Что здесь происходит Мы первый
- 12:48раз делаем проверку без синхро Найда
- 12:51если эта проверка проходит успешно мы
- 12:53как раз-таки
- 12:55начинаем критический блок и в этом
- 12:58критическом блоке мы ещё раз проверяем
- 13:00наши условия и делаем нужную нам
- 13:03операцию выглядит немножко странно но
- 13:06это паттерн Double Check loing как
- 13:09раз-таки для того чтобы мы делали запись
- 13:12только один раз а все остальные разы
- 13:14читали без блока синхро найд на самом
- 13:17деле этот паттерн может использоваться
- 13:19не только в Гоф синглтон это нужно в
- 13:24целом для ограничения доступа к
- 13:27критической секции по какому условию для
- 13:30предотвращения Рей конди это может быть
- 13:32полезно Например у вас есть какой-то
- 13:36интерсек для вашего Ок http вы хотите
- 13:39обновлять там ваши Access токены если
- 13:42они протух и если вы повесить
- 13:45synchronized на весь метод intercept у
- 13:47вас не будет параллельных запросов у вас
- 13:50каждый запрос будет идти последовательно
- 13:52что affected performance и вы внутри
- 13:54этого интерсек можете проверять что если
- 13:56Вам пришёл ответ о том что ваш токен
- 13:59протух то только тогда Вы заходите в
- 14:03блок сирона делаете проверку Ещё раз
- 14:05чтобы только один из запросов обновил
- 14:09токен И после этого обновляет токен и
- 14:12ваши проверки Вот это эта проверка не
- 14:14будет пройдено и вы будете выполнять ваш
- 14:17оставшийся код ваш оставшийся запрос
- 14:18Окей кажется что мы с дабл чек Кингом
- 14:22разобрались Давайте пойдём дальше у нас
- 14:24есть поля users и exec Давайте посмотрим
- 14:27используется ли где-то
- 14:30Вообще нигде не используется поэтому его
- 14:32можно удалить что дальше users Здесь мы
- 14:36получаем значение из репозитория и при
- 14:39этом здесь есть важный момент что на
- 14:41самом деле это работает вот так и мы
- 14:43Каждый раз при обращении users будем
- 14:46получать значение актуальное из
- 14:48репозитория и у нас эта переменная
- 14:51mutable во-первых этот код не с
- 14:53компилируется потому что такой мы можем
- 14:58сделать для иммутабельность
- 15:02у нас это поле используется для поиска
- 15:08нужного нам айдини мы получаем значение
- 15:11и затем меняем это значение в листе но
- 15:14при этом это значение меняется
- 15:18локально и никак не прокиды ется в
- 15:22репозитории чтобы записать на диск или
- 15:25отправить куда-то запрос в сеть и как
- 15:29будто бы здесь допущена ошибка в том
- 15:32плане что если мы изменим локально наших
- 15:36юзеров то при перезапуске приложения эти
- 15:39эти данные потеряются Я предлагаю
- 15:42добавить метод в репозиторий метод
- 15:45апдейт который будет обновлять по айдини
- 15:47номер телефона чтобы
- 15:49данные были актуальными при перезапуске
- 15:53приложения Давайте добавим сюда
- 15:56айдини и добавим сюда новый номер
- 15:59телефона что здесь я предлагаю делать мы
- 16:03будем получать в методе актуальный
- 16:06список юзеров
- 16:08[музыка]
- 16:14Ага Так здесь я получаю список
- 16:17пользователей и когда я нахожу нужного
- 16:21пользователя получается
- 16:27что и затем нам нужно прокинуть
- 16:31обновлённого пользователя в репозиторий
- 16:34То бишь здесь у меня
- 16:36будет
- 16:37[музыка]
- 16:40будет новый номер телефона это фор всё
- 16:46здесь мы тогда это убираем локально мы
- 16:49не изменяем и это конда нам боль нуж
- 16:55какие здесь есть
- 16:59фона Обращаемся к ресурсам видимо где
- 17:03задан какой-то паттерн и передаём
- 17:05параметр собственно этот номер телефона
- 17:08вообще использование контекста Здесь нам
- 17:11может создать проблем Почему как минимум
- 17:14важно гарантировать чтобы этот контекст
- 17:16был Application контекстом если это
- 17:19будет активити контекст то при смене
- 17:21конфигурации у Activity или при
- 17:24уничтожении её у нас будет утечка памяти
- 17:27потому что этот класс держит ссылку на
- 17:30активити как это можно решить Ну самый
- 17:32простой вариант - это при при создании
- 17:35этого экземпляра мы можем сделать вот
- 17:37так вот контекст Application котек и
- 17:40проблему с утечками мы решим Но даже
- 17:42если мы решили проблему с утечками
- 17:44контекст нас жёстко привязывает к
- 17:46Андроиду и не даёт уйти в котлин
- 17:50платформ или у нас будет проблема что
- 17:53при тестировании Нам нужно будет мокать
- 17:55контекст кто мока контекст ПС чат знает
- 17:59какая это проблема поэтому я бы создал
- 18:01отдельный интерфейс для форматирования
- 18:04номера телефона То бишь у нас был бы
- 18:07интерфейс
- 18:09НФ где был бы метод
- 18:13Format который вы принимал номер
- 18:15телефона и возвращал бы форматированный
- 18:20номер телефона и вместо контекста
- 18:23Давайте использовать нфор
- 18:29соответственно нам нужно передавать этот
- 18:32фон форматер в
- 18:34instance и передавать его при создании
- 18:37экземпляра класса И что мы будем здесь
- 18:39делать формат фон -
- 18:41[музыка]
- 18:49это какое преимущество нам это даст мы
- 18:52получим больше гибкости мы сможем нфор
- 18:55переопределить
- 18:57зайка этот интерфейс при тестировании мы
- 19:00не привязываем к контексту и если будем
- 19:03уходить в н платформ то сможем при
- 19:07помощи expected
- 19:09actual сделать это для Android iOS и
- 19:12других таргетом поэтому предлагаю
- 19:14ставить таким образом Давайте дальше что
- 19:16ещё здесь смущает Давайте посмотрим есть
- 19:18ли ещё какие-то проблемы в Козе и можно
- 19:20ли его как-то улучшить или упростить
- 19:24получается что здесь происходит Мы рым
- 19:26по списку пользователей смотрим Есть ли
- 19:31пользователи с нужным нам айтишником
- 19:33чтобы изменить номер телефона и передать
- 19:35эти изменённые данные в репозитории как
- 19:38будто бы
- 19:39хочется немножко упростить и сделать
- 19:43что-то вроде
- 19:45такого найти пользователя можно просто
- 19:48через
- 19:50ляду ID ра ID
- 19:59то мы можем выйти из этой функции в
- 20:03таком случае можно форматировать номер
- 20:05телефона позже чтобы не делать лишних
- 20:09операций Окей Допустим мы нашли
- 20:11пользователя отформатировано телефона и
- 20:14дальше мы вызываем наш
- 20:16метод вызываем
- 20:20логирование здесь меняем
- 20:27на е есть проблемы нам репозиторий с
- 20:30пользователями возвращает linked list В
- 20:34чём здесь проблема то есть репозиторий
- 20:37предоставляет наружу конкретную
- 20:39реализацию класса list и для меня как
- 20:42для пользователя этого репозитория это
- 20:44не должно иметь значение Мне важно
- 20:46просто получить этот список Если вдруг
- 20:49мы захотим поменять под капотом linked
- 20:52list на array list или Copy onr list то
- 20:55в местах использования сломается
- 20:57компиляция если мы я указываем тип при
- 21:00вызове если мы начали зависеть от
- 21:02конкретных методов linkit листа например
- 21:04методы Push Pop пик А я напомню linked
- 21:07list ещё и реализует интерфейс theq
- 21:12двустороннюю очередь то клиенты могут
- 21:14должны будут избавляться от
- 21:15использования этих методов Итого это не
- 21:18гибко и тоже ломает инкапсуляции давайте
- 21:21мы поменяем просто на лист здесь у нас
- 21:24при вызове это не имеет значения какой
- 21:27конкретно лист и как будто бы для
- 21:30эффективного поиска э хочется не
- 21:34итерироваться каждый раз по этому списку
- 21:37потому что у этого Линейная сложность А
- 21:39хочется За константное время проверять
- 21:41Есть ли этот пользователей в нашем
- 21:43репозитории и Для этого нам лучше
- 21:46подойдёт не лист а Map Тем более что
- 21:50если мы хотим сделать репозиторий гибки
- 21:52если внутри есть какой-то in Memory cash
- 21:54то мапы здесь как нельзя Кстати если мы
- 21:57часто из
- 21:59конкретно адини поэтому Предлагаю
- 22:02поменять
- 22:04на То бишь у нас будет ключом это
- 22:08наш дишни а значением User таким образом
- 22:13что мы получаем мы получаем маку со
- 22:15списком
- 22:16[музыка]
- 22:16[аплодисменты]
- 22:18пользователей in
- 22:21User Как найти юзера мы можем
- 22:25сделать от ID И если мы его не нашли то
- 22:31прерывать выполнение этого метода дальше
- 22:33мы форматируем и репозиторий уже сам
- 22:38решит Как нам обновить внутри хш маку то
- 22:41есть обновить in Memory cash сходить
- 22:43куда-то в диск или в сеть и мы
- 22:47собственно дальше логи смотрите какие
- 22:50здесь ещё есть проблемы после того как
- 22:53мы улучшили метод Аде нам как будто бы
- 22:57не нужно делать из этого класса силтон
- 23:00потому что у нас внутри этого класса
- 23:04ничего не хранится в сингл Тоне просто
- 23:08нет смысла мы просто Будем держать
- 23:10Постоянно в памяти инстанс этого класса
- 23:12при этом в нём даже не содержится
- 23:15Никаких данных которые нужно ну
- 23:17постоянно держать актуальными поэтому от
- 23:20сингл тона Я предлагаю
- 23:23избавляться в нём нет смысла Ага У нас г
- 23:28нужно ставить соответственно это больше
- 23:31не sone и он не холдер потому что он не
- 23:34хранит список пользователей То есть
- 23:36сейчас это просто н апдейтер по айдини
- 23:40как будто бы это уже напоминает нам
- 23:42некий кейс Да на нам ещё брать Private
- 23:46конструктор чтобы мы могли создавать как
- 23:48будто бы это уже просто кейс который мы
- 23:51сможем использовать нашем entation слою
- 23:53для обновления номера телефона Давайте
- 23:55поменяем это просто use Update
- 24:01вроде бы всё на самом деле этот код
- 24:04можно улучшать и дальше в
- 24:06частности Мы в кейсе То бишь в бизнес
- 24:10логике в домейн слое самостоятельно
- 24:13выполняем поиск пользователя делаем
- 24:16проверку Есть ли этот пользователь в
- 24:19репозитории и как будто бы это не
- 24:22ответственность бизнес и нам бы в кейсе
- 24:27хотелось бы просто послать запрос в
- 24:29репозиторий А репозиторий уже сам решит
- 24:32Нужно ли обновлять Не нужно искать
- 24:34пользователя или создавать нового под
- 24:36него поэтому я бы предложил отсюда это
- 24:40убрать и мы бы просто форматировать при
- 24:43визовы метода update форматировать бы
- 24:45номер телефона и отправляли его в
- 24:47репозиторий репозиторий уже сам решит
- 24:49что ему нужно делать также у нас здесь
- 24:52есть лоер Я считаю что не
- 24:56ответственность кейса
- 24:59делать какие-то логирования это нужно
- 25:01делать либо на стороне дата слоя внутри
- 25:05репозитория либо на стороне entation
- 25:08слоя например во VI модели ну здесь Всё
- 25:11зависит от вашей бизнес логики поэтому
- 25:14логирование я также предлагаю
- 25:18убрать убираем убираем соответственно
- 25:20comp object и убираем из
- 25:22конструктора Также хотелось бы
- 25:25переименовать репозитории
- 25:32и код стал уже Чуть более чистым мы
- 25:36убрали Ну типа процентов 80 кода
- 25:40предлагаю на этом завершать
About this transcript
This page contains the full transcript of Разбор лайвкода в Т-Банк на 450к / Android собеседование by Фабрика Офферов / Артур Илькаев, generated from the public captions YouTube serves with the video. The transcript has 2,935 words across 510 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.