Результати пошуку за запитом: mvc4 5
500+ питань на співбесіді щодо Ruby
Автор: Влад Сверчков
Дорогие друзья! Предлагаем вашему вниманию перевод статьи, опубликованной на DOU.ua 14 июня 2021 года. Оригинальная версия на украинском языке доступна по ссылке.
Junior Ruby Developer
Middle Ruby Developer
Senior Ruby Developer
Что нужно знать, когда проходишь техническое собеседование по Ruby? Конечно, ко всем вопросам готовым не будешь, но мы попросили интервьюеров-практиков прислать свои списки вопросов, а затем обобщили их в одном материале.
Junior
Общие вопросы
1. Какие принципы программирования вы знаете и какие из них вы пытаетесь соблюдать чаще всего?
2. Что такое парадигма ООП? Какие основные составляющие парадигмы?
3. Зачем нужна инкапсуляция? Что может произойти и не произойти, если все методы будут иметь модификатор доступа public?
4. Что такое наследование? Назовите пример(ы) плохого наследования (такое наследование, которое не стоило бы реализовывать).
5. Что такое полиморфизм? Приведите пример использования полиморфизма.
6. Что такое паттерн/шаблон проектирования? Какие шаблоны знаете?
7. Что такое клиент и сервер? Какая механика их взаимодействия?
8. Опишите путь, который проходит запрос после того, как в браузер ввести URL любого ресурса.
9. Что такое протокол HTTP? Какая разница между HTTP и HTTPS?
10. Что такое API? Может ли веб-приложение использовать более одного API?
11. Что такое REST API? Какие действия чаще всего выполняются над ресурсами?
12. Что такое сессия? Что такое cookies? Какая разница между ними?
13. Какие веб-уязвимости знаете? Как от них защититься? Какие есть инструменты, чтобы автоматически контролировать защиту от веб-уязвимостей?
14. Какими критериям пользуетесь для выбора библиотеки?
15. Что такое рефакторинг? Какие должны быть условия для выполнения рефакторинга?
16. Знаете ли вы семейство UNIX-подобных операционных систем (Linux, MacOS)? Назовите команды, которые чаще всего используете в терминале.
17. Что такое аутентификация?
18. Что такое авторизация?
19. Из каких частей состоит HTTP Request и HTTP Response?
20. Какие HTTP-статусы (HTTP-коды) знаете? На какие группы вы могли бы их разделить?
21. Какие HTTP-методы знаете? Зачем используется каждый из них?
Git
22. Знакомы ли вы с системой Git?
23. Для чего нужны ветки в Git? Как сделать ветку?
24. Как скопировать с Git-хостинга (GitHub / GitLab / Bitbucket / другие) проект?
25. Что такое флаг -f? Для чего он и какие минусы использования?
26. Что такое Pull Request? Зачем ветви соединяют через Pull Request?
27. Какие еще команды чаще всего применяете, когда пользуетесь Git?
Ruby
28. Как вы контролируете версии Ruby на своей локальной машине?
29. Что такое метапрограммирование? Какие плюсы и минусы при использовании метапрограммирования?
30. Какой менеджер библиотек для Ruby знаете? Как добавлять библиотеки? Как контролировать версии библиотек?
Style guide
31. Что такое style guide и зачем его используют?
32. Какой популярный линтер контролирует выполнение большей части пунктов с Ruby Style Guide?
Основы Ruby
33. Какие типы переменных есть в Ruby?
34. Что вернётся в результате сложения 10.5 и 10?
35. Чем отличаются переменные, начинающиеся с @, @@ и $?
36. Что такое attr_reader, attr_writer, attr_accessor и зачем они?
37. Что такое метод?
38. Что значит "?" в конце названия методов? Что означает "!" в конце названия методов?
39. Чем отличается энумератор map от each?
40. Что такое модуль? Какие есть варианты подключения модулей в класс?
41. Что такое класс и зачем он нужен?
42. Что такое модуль и зачем его используют? Чем модуль отличается от класса?
43. Что такое константа?
44. Что такое блок? Что такое yield? Можно ли передавать блоки в методы? Каким образом?
45. Что такое proc?
46. Что такое lambda?
47. В чем разница между proc и lambda?
48. Что такое eigenclass?
49. Какие виды условных операторов есть в Ruby? (if, unless, ternary, case)
50. Есть ли разница между операторами && и and?
51. Какие виды циклов есть в Ruby? (loop, while, until, for, each(), times(), upto())
Типы данных
52. Какие типы данных есть в Ruby?
53. Что такое интерполяция?
54. Что такое конкатенация?
55. Что такое массив в Ruby?
56. Что такое Hash в Ruby? Чем Hash отличается от массива?
57. Что такое символ и зачем он нужен?
58. Что такое Range и зачем его используют?
59. Что такое Time и Date?
60. Что такое Struct?
61. Что такое File?
Структура классов и объектов
62. Что такое BasicObject?
63. Что такое Numeric?
64. Что такое Enumerable?
65. Что такое Struct?
66. Что такое OpenStruct?
67. Что такое Set?
68. Что такое Queue?
69. Что такое Object?
70. Что такое Kernel?
ООП на Ruby
71. Есть ли интерфейсы в Ruby?
72. Что такое getter и setter в Ruby? Как их создавать?
73. Что такое инстансная переменная?
74. Что такое константа класса?
75. Какая разница между методом класса и методом объекта?
76. Какие есть области видимости?
77. Какие есть модификаторы доступа? Как private отличается от protected?
78. Поддерживает ли Ruby множественное наследование?
79. Как наследуются методы? Можно ли переопределить метод?
80. Зачем метод super?
81. Что такое Singleton-метод?
82. Что такое миксины? Зачем они и чем отличаются от декоратора?
83. Что такое include и extend и в чем разница между ними?
84. Какой путь поиска метода в иерархии классов?
85. Какая разница между последовательными и именуемыми аргументами метода?
86. Что такое exception? Когда они применяются? Как вызвать exception? Как отловить exception?
Метапрограммирование
87. Что означает open class?
88. Что такое monkey patching?
89. Возможно ли создать класс динамически? Если да, то как именно?
90. Какие есть способы динамического управления методами, переменными и классами (define_method(), class_eval(), module_eval())?
91. Как происходит поиск метода в иерархии? Как можно обработать отсутствие метода (method_missing())?
92. Что такое алиас метода? Для чего нужны алиасы?
93. Как получить значение инстансной переменной? Как задать значение? Как проверить существование инстансной переменной (instance_variable_get(), instance_variable_set(), instance_variable_defined())?
94. Какие методы интроспекции есть в Ruby (class(), ancestors(), methods(), private_methods(), instance_variables(), singleton_methods(), local_variables())?
95. Что такое хуки и зачем их используют (inherited(), included(), method_added(), singleton_method_added(), method_missing())?
RubyGems
96. Что такое gem? Что такое RubyGems?
97. С какой целью используют RVM? Что такое gemset?
98. Что такое Bundler и зачем он?
99. Что такое Gemfile и для чего его используют?
100. За что отвечает каждый из gem`ов? Если знаете нюансы в работе какого-либо из них — обязательно озвучьте.
active_admin
active_model_serializers
bundle audit
bcrypt
devise
jwt
cancancan / pundit
draper
kaminari
sidekiq
rspec
shoulda-matchers
faker
factory_bot
simplecov
rubocop (with extensions)
pry
Ruby on Rails
101. Что такое MVC? Является ли Rails MVC-фреймворком?
102. Что такое миграция? Для чего она нужна? Какие правила создания миграций?
103. Что такое роуты? Что такое resource и resources в роутах?
104. Чем отличается has_many through от has_and_belongs_to_many?
105. Зачем используют concerns?
106. Как работать с sensitive-данными в Rails-приложениях (токены, ключи и т. д.)? Как работает механизм credentials в Rails?
107. С какой целью используют локалы (locals)?
Style guide
108. Знакомы ли вы с RoR style guide?
Структура файлов в Rails
109. Из чего состоит Rails-приложение? Какая архитектура директорий? Зачем нужна каждая из них?
110. Чем отличается lib от vendor?
111. Как создать background job в Rails?
112. Какие способы загрузки ассоциаций в Rails?
Модели и ActiveRecord
113. Зачем нужны модели? Что представляет собою модель?
114. Что такое ORM? Что такое ActiveRecord?
115. Какие есть правила наименования моделей и таблиц в базе данных?
116. Что такое миграции и зачем нужен этот механизм? Как создавать миграции?
117. Какие виды валидаций есть в AR?
118. Как можно вызвать валидацию? Когда валидация вызывается автоматически? Как можно отключить валидацию при сохранении?
119. Что такое callback в Rails?
120. Какие есть виды колбеков в AR?
121. Какие виды ассоциаций есть в AR?
122. Что такое has_one и belongs_to?
123. Что такое has_many?
124. Что такое has_many: through?
125. Что такое has_one: through?
126. Что такое has_and_belongs_to_many?
127. Что такое полиморфная ассоциация?
128. Что такое STI?
129. Каким образом можно получить данные из базы данных? Как получить один объект и коллекцию? Как задать условия выборки?
130. Что такое скоупы? Какова их функция?
131. Як выполнить произвольный SQL-запрос?
132. Как посмотреть информацию о том, как выполнялся запрос, какие использовались ключи и т. п.?
Контроллеры
133. Что такое контроллер? Какую задачу выполняет?
134. Что такое action? Что такое CRUD action?
135. Что такое permit params?
136. Что такое ActiveRecord? Как он реализован в Rails-фреймворке?
137. Что такое параметры? Как они попадают в контроллер? Что такое дефолтные параметры (в роутах)? Что такое стронг-параметры?
138. Что такое cookies и зачем они? Где хранятся cookies? Как получить доступ к cookies?
139. Что такое сессии и в чем их задача? Где хранятся сессии? Как получить доступ к сессии?
140. Какие виды фильтров есть в Rails? (Before / after / around actions)
141. Зачем нужен объект Request? Какую информацию он содержит?
142. Для чего нужен объект Response? Какую информацию он содержит?
143. Что такое стриминг? Каким образом можно отдать файл на загрузку (Send_data, send_file)?
144. Что такое Rescue в контроллерах и для чего применяется?
145. Как принудительно включить использование HTTPS-протокола?
Маршрутизация
146. Что такое роуты и в чем их функция?
147. Что означает аббревиатура CRUD?
148. Если нужно сделать новый CRUD для определенного ресурса, какие классы вы создадите и какие файлы измените?
149. Что такое ресурсы?
150. Что такое Singular ресурсы и что отличает их от обычных ресурсов?
151. Что делают неймспейсы в роутах?
152. Что такое вложенные ресурсы?
153. Что такое shallow nesting?
154. Что означает RESTful роуты?
155. Что такое нересурсные роуты?
156. Что такое actions в роутах?
View
157. Что такое view и для чего он нужен?
158. Что такое layout? Чем отличается от view?
159. Что такое шаблонизатор и с какими шаблонизаторами приходилось работать?
160. Зачем нужен метод render? Что рендерить с его помощью?
161. Как передаются переменные во view?
162. Что такое partial? Когда стоит его применять?
163. Что такое хелпер и зачем его используют?
164. Какие есть хелперы для генерации роутов?
165. Какие есть хелперы для генерации форм?
166. Как создавать собственные хелперы?
Интернационализация Rails
167. Что такое i18n? Зачем нужна i18n? Как она устроена в RoR? В каком виде можно хранить переводы?
ActionMailer
168. Что такое ActionMailer и какие задачи он решает?
AssetsPipeline
169. Что такое AssetsPipeline и какие задачи он выполняет?
Инструменты консоли и rake-таски
170. Какие есть консольные команды для работы с RoR? Что такое rake-таски?
Безопасность
171. Знаете ли вы какие-то уязвимые места, защиту от которых Rails предоставляет out of the box?
172. Какие атаки могут быть при неправильном использовании сессий? Почему нельзя хранить важные данные в сессиях?
173. Что такое CSRF? Как правильно защититься от CSRF?
174. В чем уязвимость Open Redirect? Как защититься от возможных атак, связанных с Open Redirect?
175. Какие уязвимости связаны с загрузкой файлов? Почему важно фильтровать имена и расширения файлов? Как защититься от опасности?
176. Почему нельзя загружать файлы в каталог, доступный снаружи? Что будет, если злоумышленник загрузит файл с кодом?
177. Какие уязвимости связаны с управлением пользователями (ссылка активации, пароли в чистом виде)? Как защититься?
178. Что такое инъекции и как происходит такая атака? Как их избежать?
179. Как организовать безопасную генерацию приглашений? Что такое deep_munge?
Конфигурация
180. Где располагается основная конфигурация программы?
181. Что такое initializers? Зачем они?
182. Что такое окружение? Каково его назначение?
183. Как и где настраивается взаимодействие с базой данных?
184. Как и где настраивается Assets, генераторы, мидлвары, i18n?
185. Можно ли указывать произвольные настройки? Если да, то где и каким образом?
Кэширование
186. Что такое кэш и зачем нужен этот механизм?
187. Какие виды кэширования есть в Rails?
188. Где может храниться кэш?
189. Зачем использовать кэширование страниц?
190. Зачем использовать кэширование экшенов?
191. Зачем использовать кэширование фрагментов?
192. Что такое Memcached?
193. Зачем и как реализуется низкоуровневое кэширование?
194. Как и когда кэшируется SQL?
195. Как тестировать кэширования в режиме разработки?
Реляционные базы данных
Теория реляционных баз данных
196. Что такое СУБД?
197. Что такое реляционная база данных?
198. Что такое нормализация базы данных?
199. Что такое нормальная форма?
200. Сколько есть нормальных форм баз данных?
201. Опишите первые три нормальные формы баз данных.
202. Что такое денормализация баз данных?
203. В чем разница между Primary Key, Foreign Key? Зачем они?
204. Какая разница между LEFT JOIN, INNER JOIN, RIGHT JOIN?
205. Как добавить запись в таблицу без ORM?
206. Как обновить запись в таблице без ORM?
207. Как удалить запись из таблицы без ORM?
208. Как прочитать записи в таблице без ORM?
209. Какие правила группировки данных в таблице вы знаете?
SQL
210. Что такое DML (Data Manipulation Language)?
211. Что такое DDL (Data Definition Language)?
212. Что такое DCL (Data Control Language)?
213. Что такое TCL (Transaction Control Language)?
214. Как выбрать данные из нескольких таблиц? Какие виды JOIN вы знаете?
215. Какие агрегированные показатели можно использовать во время выборки из таблицы?
216. Что такое таблица в базе данных?
217. Как создать таблицу?
218. Как выбрать записи из таблицы?
219. Как вставить запись в таблицу?
220. Как изменить запись в таблице?
221. Как удалить запись из таблицы?
222. Как объединить выборку из разных таблиц?
223. Что такое группировка или агрегирование?
224. Каким образом можно задавать условия агрегирования?
Индексы баз данных
225. Что такое индексы в базе данных?
226. Для чего нужны индексы?
227. Какие виды индексов существуют?
228. Что такое первичный ключ?
229. Что такое внешний ключ?
230. Что такое составной ключ?
Полнотекстовый индекс в базах данных
231. Что такое полнотекстовый поиск и в чем его задача?
232. Что такое полнотекстовый индекс?
233. Что такое ts_vector?
234. Что такое ts_query?
PostgreSQL
235. Что такое select distinct и какова его функция?
236. Каким образом можно сортировать результаты выборки?
237. Как ограничить количество результатов выборки?
238. Как проводить поиск по подстрокам?
239. Какие виды объединений (JOIN) есть в PostgreSQL?
240. Что такое подзапросы и в чем их задача?
241. Какие типы данных есть в PostgreSQL?
242. Какие есть виды символьных типов данных?
243. Какие есть виды типов данных для работы с датой и временем?
244. Что такое тип enum? Когда он используется?
245. Как можно создавать таблицы в базе данных?
246. Как изменять структуру таблицы?
247. Как удалять таблицы?
248. Как очистить таблицу?
249. Возможно ли задавать значения по умолчанию для полей? Если да, то как именно?
250. Возможно ли контролировать уникальность значений при вставке? Если да, то каким образом?
251. Что такое функции в PostgreSQL?
252. Какие операторы есть в PostgreSQL?
Базы данных NoSQL
Redis
253. Что такое Redis и зачем используют этот инструмент?
Elasticsearch
254. Что такое Elasticsearch и зачем его используют?
Статический анализ кода
255. Что такое Overcommit и зачем нужен?
256. Что такое Danger и для чего используется?
257. Что такое статические анализаторы кода? Зачем их используют?
258. Какие анализаторы для Ruby / Ruby on Rails знаете?
259. Какие статические анализаторы кода для JavaScript знаете?
Тестирование
260. Какие виды тестирования знаете? Какие из них вы практиковали при использовании Rails?
261. Какой фреймворк для тестирования используете?
262. Какие показатели качества для своих тестов можете назвать?
263. С помощью какой библиотеки вы контролируете процент покрытия тестами?
264. Расскажите об основах тестирования в Ruby. Что такое RSpec? Какая разница между describe, context и it?
265. Какая разница между юнит-тестами и интеграционными?
266. Что проверяют приёмным тестированием?
267. Что проверяют интеграционным тестированием?
268. Что тестируют в моделях?
269. Что тестируют в контроллерах?
270. Что тестируют в сериалайзерах?
271. Как тестировать API?
272. Как тестировать различные Service Objects?
273. Что такое RSpec? Какие задачи выполняет этот инструмент?
274. В чем разница между let и let! в RSpec?
275. Какие хуки в RSpec знаете?
276. Что выполняется раньше: let! или хук before (:all) ?
277. Что такое Capybara? Какие задачи решает Capybara?
278. Что такое стабы?
279. Что такое моки?
280. Что такое матчер?
281. Какую задачу выполняет гем Shoulda Matchers?
282. Какую задачу выполняет Faker?
283. Зачем нужны Webmock и VCR? Чем они отличаются?
284. Что такое WebKit и PhantomJS? В чем разница между ними? Какие еще есть драйверы?
285. Как работать с базой данных в тестах? Как работает gem database_cleaner?
Дополнительные RubyGems и Rails плагины
286. Какую задачу выполняет Devise?
287. Какую задачу выполняет Rake?
288. Какую задачу выполняет Cancancan и Pundit?
289. Какую задачу выполняет Kaminari и Will_paginate?
290. Какую задачу выполняет Carrierwave?
291. Какую задачу выполняет MiniMagick?
292. Какую задачу выполняет Aasm?
293. Какую задачу выполняет ActiveAdmin?
294. Какую задачу выполняет Nokogiri?
295. Какую задачу выполняет Formtastic и SimpleForm?
296. Какую задачу выполняет Omniauth?
297. Какую задачу выполняет Draper?
298. Какую задачу выполняет Faye?
299. Какую задачу выполняет Pusher?
300. Какую задачу выполняет Sidekiq, Resque?
301. Какую задачу выполняет Mongoid?
Управление, сервисы и DevOps
302. Что такое SSH? Как и для чего используется?
303. Каким образом можно настроить аутентификацию на хосте без пароля?
304. Какие задачи выполняет Heroku? В чем его особенность?
305. Какие задачи выполняет Digitalocean?
306. Какие задачи выполняет Docker?
Мониторинг
307. Какие задачи выполняют Airbrake, DataDog, NewRelic?
308. С какой целью используют Monit?
Ruby Web Tools
309. Какие есть веб-серверы, написанные на Ruby?
310. Что такое Sinatra?
311. Что такое API? Для чего используется? Что такое сериалайзеры и зачем они нужны?
312. Что такое клиент-серверная модель?
313. Какая разница между GET- и POST-запросами?
314. Что такое HTTParty и какие задачи он выполняет?
315. Что такое Faraday и какие задачи он выполняет?
316. Что такое Rack? Что такое Rack-application? Что такое Rack middleware?
317. Что такое push-технология?
Интеграции
318. Есть ли опыт интеграции с Facebook? Если да, то на каком проекте?
319. Есть ли опыт интеграции с Twitter? Если да, то на каком проекте?
320. Есть ли опыт интеграции с LinkedIn? Если да, то на каком проекте?
321. Есть ли опыт интеграции с Google? Если да, то на каком проекте?
Практические задания
322. Описать в ООП-стиле: один из предметов интерьера в контексте конкретного бизнеса. Например, кресло, на котором сидит кандидат, теперь надо продавать в магазине.
323. Написать консольную версию игры «крестики-нолики», где входные данные передаются через gets, а выводят через puts. Два пользователя на одной машине должны иметь возможность сыграть партию.
324. Отсортировать массив.
325. Написать функцию, которая вернет уникальные элементы массива, не используя функцию uniq или set, to_set со стандартной библиотеки.
Middle
Общее
1. Что такое SOLID? Приведите примеры на каждую из букв.
2. Что такое микросервисная архитектура? Какие плюсы и минусы вы видите во время её использования?
3. Знаете ли вы шаблоны проектирования Singleton / Factory / Facade / Decorator / Template Method / Strategy?
4. Можно ли измерить скорость выполнения алгоритма? Какие факторы влияют на скорость? Какие виды зависимостей вы знаете?
5. Что такое методология Scrum? На каких активностях / встречах может присутствовать / быть полезным Software Developer?
6. Какие инструменты для логирования используете в работе?
7. Какие инструменты для мониторинга веб-приложений используете?
8. С помощью какого/каких инструментов вы деплоили приложения на staging / production? Опишите процесс.
9. Если один из ендпоинтов в приложении отрабатывает очень долго, что делать, чтобы улучшить скорость?
10. Чем отличается библиотека от фреймворка?
11. Что такое трехзвенная архитектура? Зачем и когда ее используют? Приведите примеры.
12. Что такое кэширование? Зачем и когда его используют? Приведите примеры.
13. Что такое функциональное разделение? Зачем и когда его используют? Приведите примеры.
14. Что такое Comet-сервер? Зачем и когда его используют? Приведите примеры.
15. Что такое денормализация? Зачем и когда ее используют? Приведите примеры.
16. Что такое деградация функциональности? Зачем она и в каких случаях используется? Приведите примеры.
17. Что такое сервис-ориентированная архитектура? Зачем и когда ее используют? Приведите примеры.
18. Что такое монолитное приложение? Зачем и когда его используют? Приведите примеры.
19. Что такое асинхронная обработка? Зачем и когда ее применяют? Приведите примеры.
20. Что такое ленивые вычисления? Зачем и когда их используют? Приведите примеры.
Предметно-ориентированное проектирование
21. Что такое DDD? В чем преимущества перед классическим MVC?
22. Что такое домен?
23. Что такое модель?
24. Что такое контекст?
25. Что такое сущность?
26. Что такое value object?
27. Что такое агрегирование?
Git и code review
28. Что делают команды cherry-pick, log, stash, reset, squash?
29. Что такое Gitflow? Какие правила использования Git на вашем прошлом проекте?
30. Каких правил именования комитов / ветвей / Pull Request`ов вы придерживаетесь?
31. На что обращаете внимание при code review?
32. Что бы вы могли назвать плохо проведенным code review?
Базы данных
33. Для чего нужны индексы?
34. Какие есть виды индексов?
35. Ускоряют ли индексы все операции с базами?
36. Что такое DB view?
37. Чем отличаются materialized db view от non-materialized db view?
38. Как можно сохранить данные в различные таблицы и гарантировать, что все они или запишутся, или нет?
39. Можно ли строить индексы по нескольким полям? Важен ли порядок этих полей в индексе?
40. Какие вы знаете constraints при создании столбцов?
41. В чем разница между SQL и NoSQL базами данных?
42. Как бы вы импортировали большие массивы данных в БД (1-2 миллиона строк в CSV-файл)?
43. Что такое N+1 и как избегать?
Реляционные базы данных
44. Какие различия между джоинами FULL OUTER JOIN, CROSS JOIN, NATURAL JOIN, INTERSECT и EXPECT?
45. Какие специфические типы данных есть в PostgreSQL?
46. Что такое view? С какой целью используется?
47. Что такое materialized view?
48. Что такое recursive view?
49. Что такое хранимая процедура и зачем она нужна?
50. Что такое партиционирование и какую проблему оно решает?
51. Умеете ли вы работать с чистыми SQL-запросами?
52. Каким образом можно работать с геолокацией в PostgreSQL?
53. Какие способы резервного копирования данных? Что такое pg_dump? В каком виде можно создавать резервные копии?
NoSQL базы данных
Redis
54. Что такое Redis? Какие задачи он выполняет?
55. Какие типы данных есть в Redis и для чего нужен каждый из них?
56. Что такое pub / sub?
57. Что такое транзакции?
58. Что такое mass insertion?
59. Что такое партиционирование?
60. Умеете ли работать с Redis в консоли?
61. Умеете ли настраивать Redis?
Elasticsearch
62. Что такое Elasticsearch и какие задачи он выполняет?
63. Что такое кластер?
64. Что такое нода?
65. Что такое индекс?
66. Что такое тип?
67. Что такое документ?
68. Что такое шарды и реплики?
69. Что такое Query DSL?
70. Что такое мапинг?
Статический анализ кода
71. Что такое overcommit и зачем нужна эта утилита?
72. Что такое danger и для чего используют эту утилиту?
73. Какую задачу выполняет каждый из нижеперечисленных гемов?
Rubocop;
Reek;
Rails Best Practices;
Brakeman;
RubyCritic;
SimpleCov;
Bundle Audit;
Bundle Leak;
Traceroute.
74. Какие задачи выполняет каждый из нижеперечисленных модулей?
Eslint;
Stylelint;
Prettier.
Ruby
75. Какие проблемы в Ruby (как в языке программирования) вы видите? Какие, по вашему мнению, нужно решать как можно быстрее?
76. Какими библиотеками, кроме фреймворка Rails, пользовались для создания структуры проекта?
77. Как можно в Ruby запросить сторонний API? Какой встроенный класс позволяет это сделать?
78. Что такое Thread? Чем он отличается от Process?
79. Есть ли в Ruby возможность создать абстрактный класс? Есть ли способ искусственно создать абстрактный класс?
80. Есть ли в Ruby возможность множественного наследования? Есть ли способ искусственно заменить множественное наследование?
81. Назвать максимально точный путь поиска методов в Ruby
82. Какие минусы использования method_missing знаете?
83. Можно ли узнать идентификатор объекта в памяти, если да, то каким образом?
84. Какие виды enumerator для массива вы знаете?
85. Что такое мемоизация методов? Зачем ее используют?
86. С помощью какого хука можно посчитать количество потомков определенного класса?
87. В чем разница запуска rspec и bundle exec rspec?
88. Создавали ли вы гем в Ruby? Какая механика / последовательность действий для создания гема?
89. Что такое Rack middleware?
90. Есть такой код:
users = User.where (id: [1,3,4])
users.where (name: "Alex")
users.inspect
После какой строчки кода ActiveRecord выполняет запрос в базу?
Ruby on Rails
91. Что такое ActiveSupport? Зачем нужен? Какие задачи выполняет? Приведите пример методов, которые добавляет ActiveSupport.
92. Что такое Action Cable? Зачем используют этот фреймворк?
93. Как создать функциональность для отправки имейла в Rails-приложении?
94. Какие библиотеки для background jobs использовали? Какая механика работы таких библиотек?
95. Какие и виды кэша в Rails знаете? Где можно хранить кэш?
96. Какие библиотеки / подходы для создания API документации знаете / используете?
97. Что такое Swagger? Какие преимущества создания API документации в Swagger-формате?
98. Что такое Query Objects? Когда и как их можно использовать?
99. Что такое Decorators? Когда и как их можно использовать?
100. Что такое Form Objects / Contract? Когда и как их можно использовать?
101. Где и как вы рекомендуете держать бизнес-логику в Rails-приложении?
102. Что такое JSON API формат? Использовали ли вы библиотеки для сериализации объектов в JSON API формате?
Тестирование
103. Работали ли вы с feature-тестами? Что такое capybara?
104. Что такое TDD? Какие преимущества / недостатки использования TDD-подхода видите?
105. Что такое BDD? В чем отличие между TDD и BDD?
106. Что может быть причиной того, что тесты на проекте выполняются очень долго?
107. Что такое принцип тестирования FIRST?
108. Как порекомендуете писать тесты на код, в котором посылается запрос на сторонний сервис (API)?
109. Какие инструменты помогают контролировать качество тестов?
110. Какие составляющие response`a вы тестируете, когда пишете тест на определенный эндпоинт?
111. Является ли 100% code coverage на проекте залогом низкой вероятности что-то «сломать» во время багфиксинга / имплементации новой функциональности? Объясните свой ответ.
Управления, сервисы и DevOps
112. Опишите CI / CD-подход на своем последнем проекте? Как бы вы его изменили?
113. Какие в целом Continuous strategies знаете? Чем отличается Continuous Deployment от Continuous Delivery?
114. Что такое Docker? Чем отличается Docker от виртуальной машины?
115. Для чего используют docker-compose?
116. Умеете ли вы работать с Docker?
117. Что такое image в Docker? Как работать с ним в Docker?
118. Что такое container в Docker? Как работать с контейнерами? Как запускать и останавливать контейнеры? Как узнать список рабочих контейнеров?
119. Что такое Docker Daemon?
120. Что такое Docker Client?
121. Что такое Docker Hub?
122. Что такое Docker Compose?
Практические задания
123. Написать клиент для открытого API ресурса с использованием только низкоуровневых библиотек: Net::HTTP, HTTP.rb, Faraday, etc.
124. Как бы вы посчитали количество пассажиров, которые заходят или выходят на определенной станции метро?
125. Посчитать количество автомобилей в пробке.
126. Организовать автомобильную стоянку.
127. Написать функцию, которая вернет уникальные элементы массива, не используя uniq или set, to_set со стандартной библиотеки. Ожидается вариант с применением хеш-таблицы, где ключами будут уникальные элементы из массива, а значениями true.
Senior
Общие вопросы
1. Опишите основные способы аутентификации в API.
2. Назовите основные принципы построения REST API.
3. Вам нужно спроектировать API, какие основные вопросы будете задавать клиенту?
4. Какие основные преимущества и недостатки монолитной архитектуры?
5. Какие основные преимущества и недостатки микросервисной архитектуры?
6. Что такое SOLID? Какие из принципов, по вашему мнению, чаще всего нарушаются, какие являются наиболее критичными?
7. Опишите Singleton-паттерн. Где и когда вы его использовали?
8. Опишите принцип работы DNS?
9. Что такое code smells? Каковы основные признаки плохого кода?
10. Что такое reverse proxy, для чего он нужен?
11. Что такое load balancer, основные разновидности?
12. Что такое MVC и как не попасть с ним в беду?
13. Опишите процесс обработки HTTP-запроса на примере любого веб-фреймворка в Ruby (Rails, Hanami, Sinatra, Roda, etc).
14. Чем отличается refactoring от оптимизации? Как и когда нужно выполнять каждый из вышеназванных процессов?
15. Знаете ли вы GraphQL? Какие плюсы и минусы вы видите по сравнению с REST?
16. Что такое multi-tenant architecture? Когда рекомендуете ее использовать?
17. Чем отличается понятие Dependency Inversion от Dependency Injection и от Inversion of Control?
18. Что такое GDPR? Назовите правила, которые использовали, чтобы быть GDPR compliant?
Метапрограммирование и DSL
19. Параллельное выполнения кода через Thread, Fiber, Ractor. Объясните на примерах.
20. Дизайн-паттерны: Service, Form, Value, Policy, Guard, etc. Объясните на примерах.
21. Расскажите об архитектурных паттернах: Monolith, Microservices, Distributed app, etc.
22. Как организовать бизнес-логику, когда в проект уже 1000+ бизнес-операций?
23. Что такое Sidekiq? Как правильно организовывать очереди?
24. Расскажите об оптимизации быстродействия через benchmark, lineprof и flamegraph.
25. Работали ли вы с Garbage collector? Расскажите.
26. Расскажите о собственных примерах решений, которыми гордитесь и почему.
Ruby / Rails
27. Что такое Thread? Какие особенности использования тредов в разработке?
28. Что такое Process? Какие особенности использования процессов в разработке?
29. Что такое Mutex? Приведите примеры использования мьютексов в разработке?
30. Если нужно определить, сколько выполняется тот или иной код, как порекомендуете это сделать?
31. Какие недостатки видите в Ruby? Когда и как они могут себя проявить во время разработки?
32. Можно ли передать контекст выполнения одного метода в другой метод? Если да, то как это сделать?
33. Какие callback-s знаете в Ruby?
34. Как реализовать Singlethon-класс Ruby? Опишите базовые концепты.
35. Как работает Garbage collection в Ruby? Опишите базовые концепты.
36. Что такое Ractor? Какая разница между классической моделью работы с тред и работой с помощью Ractor?
37. Как работают Lazy Enumerators? Чем они отличаются от классических Enumerators? Как и когда они могут помочь?
38. Назвать примеры использования Fiber`ов
39. Как include добавляет метод модуля в класс? Что происходит на уровне MRI? В чем разница по сравнению с extend и prepend?
40. У вас есть задача импортировать пользователей в базу Rails-приложения. Пользователи записаны в CSV-файле, где каждая строка - это данные. Опишите ваш подход к решению проблемы. Как изменится решение, если файл станет очень большим (> 300000 строк)?
41. Если в модели User у вас есть callback, то какие могут возникнуть проблемы при решении предыдущей задачи?
42. Как мы можем отслеживать прогресс Background Job?
43. Опишите жизненный цикл Request - Response в Rails. Через какие основные компоненты фреймворка проходит запрос и для чего они нужны?
44. Какие основные типы тестов используете и с какой целью?
45. Как вы относитесь к интеграционным тестам?
46. Что такое полиморфные ассоциации в ActiveRecord?
47. Как бы вы порекомендовали построить semantic versioning для Rails API, работающего с мобильными приложениями (iOS, Android, Web)? Поверхностно опишите стратегию.
48. Если необходимо, чтобы каждый запрос, пришедший к Rails app независимо от роута, возвращал 403 статус как можно быстрее, как бы вы порекомендовали реализовать код для этой задачи?
49. Какие, по вашему мнению, пять основных минусов Rails? Что бы вы порекомендовали изменить, чтобы нейтрализовать эти минусы?
50. Можно ли в Rails работать с несколькими базами, если да, то как это сделать?
Базы данных
51. Что такое транзакция? Какие основные свойства транзакций?
52. Что такое database lock и чем он отличается от транзакций?
53. Что такое Pessimistic и Optimistic locking?
54. У нас есть таблица Users. Мы добавили к ней индекс в трех полях: first_name, last_name, email. Именно в таком порядке. Теперь делаем запрос SELECT * FROM users WHERE first_name = "Alex" AND email = "<a href="mailto:test@mail.com"> test@mail.com </a>". Поможет ли нам индекс ускорить такой запрос, и почему?
55. Что такое Database View? Какие виды есть?
56. Что такое Database trigger?
57. Что такое партицирование? Опишите кейсы, когда партицирование было бы полезным?
58. Поверхностно опишите шаги для реализации партицирования на примере любой СУБД.
59. Что такое триггеры? Как и когда их можно использовать? Какие плюсы и минусы вы видите в сравнении с тем, чтобы использовать триггеры / колбеки на уровне бэкенд?
60. Что такое WAL? Какую роль он играет?
61. Была ли у вас возможность / необходимость использовать шардинг? Чем отличается шардинг от партицирования?
62. Опишите принципы, преимущества и недостатки работы с primary / secondary (master / slave) репликацией?
63. Знакомы ли вы с CAP-теоремой? Если да, объясните основную идею.
64. Знаете ли вы ACID? Как вы понимаете букву "I" в этой аббревиатуре?
65. Представьте ситуацию, когда ваш сервер, на котором размещен redis, по неизвестным причинам перезапустился. Что рекомендуете сделать заранее, чтобы такие ситуации не нанесли большой вред?
Тестирование
66. Знаете ли вы, что такое Load Testing? Чем может помочь разработчик при Load Testing?
67. Какие инструменты используют для того, чтобы собрать как можно больше метрик при Load Testing?
68. Какие подходы вы бы порекомендовали использовать, чтобы ускорить тесты на CI?
69. Как внедрить процесс написания тестов, если проект (Rails API) их совсем не имеет?
Практические задания
70. Написать реализацию структуры данных Set. Объяснить алгоритмическую сложность добавления элементов к сету.
71. Спроектировать реферальную систему (то есть, пользователь может пригласить других пользователей, которые становятся его рефералами, они приглашают своих рефералов, и так формируется древовидная структура). Есть модель:
class Users <ActiveRecord :: Base
end
Как бы вы связали ее с рефералами?
72. Добавьте метод:
class Users <ActiveRecord :: Base
end
который возвращает рефералы пользователя поуровнево. Например, user имеет рефералы user1, user2. user1 имеет реферала user3, user2 имеет рефералов user4 и user5. Должны получить результат:
{0 => [user]
1 => [user1, user2],
2 => [user3, user4, user5}
Спасибо за вопросы Денису Земляному, Николаю Воронину, Сергею Гнилицкому, Владимиру Свередюку, Михаилу Одинюку, Виктору Нову, Александру Шестопалу, Андрею Зеленцу, Кириллу Шевченко, Виктору Новицкому.
Міфи про програмування та програмістів
Автор: Влад Сверчков
Миф 1. Без знания математики дверь в программирование закрыта
Миф 2. “Прочту книгу — стану программистом”
Миф 3. Чтобы освоить программирование, необходимо быть очень умным
Миф 4. Необходимо обладать талантом к написанию кода
Миф 5. Программисты — замкнутые и необщительные люди
Миф 6. Программирование — скучное занятие
Миф 7. Программисты всё пишут с нуля
Миф 8. Чтобы устроиться на работу в качестве программиста, необходимо очень долго учиться
Миф 9. После курсов у вас сразу высокая ЗП
Миф 10. “Выучи язык программирования… за 1 час!”
Миф 11. Нельзя освоить программирование самостоятельно
Миф 12. Программисты разбираются во всём, что связано с техникой
Миф 13. Программирование — мужское занятие
Миф 14. Существует самый-самый лучший языка программирования
Миф 15. Разработчики компьютерных игр — самые богатые и счастливые люди в IT
Миф 16. Начинающим айтишникам устроиться на работу невозможно
Миф 17. Пойду в ВУЗ, там меня научат программированию
Миф 18. Программирование имеет возрастные ограничения
Миф 19. Программист — вымирающая профессия: роботы заменят этих специалистов
Итоги
Приветствуем вас, друзья!
При своей относительной молодости программирование успело обзавестись приличным количеством мифов. Время, когда писателей кода считали дикими отшельниками, потихоньку уходит. Однако, и по сегодняшний день многие имеют ложные представления о программировании.
Некоторые считают, что программист - это человек, который взламывает компьютерные системы, банкоматы и совершает прочие несогласованные с буквой закона действия. Другие уверены: программист и компьютер тебе починит, и Windows переустановит, и ТВ-каналы настроит, и новый Фейсбук за ночь напишет, и покажет, как генерировать содержание документа в Ворде.
Более того, некоторые стереотипы настолько прижились, что стали отпугивать новичков в IT и мешать их профессиональному дебюту в данной сфере.
Мы подготовили для вас рейтинг самых главных мифов, которые необходимо развеять в первую очередь. Давайте же приступим к их разрушению.
Миф 1. Без знания математики дверь в программирование закрыта
Одно из самых распространенных заблуждений. Как мы уже упоминали в статьях “Нужно ли программисту высшее образование?” и “FAQ начинающего программиста”, важно не столько знание математики напрямую, сколько само математическое мышление. Сейчас мы все объясним.
Для того, чтобы начать изучение любого популярного языка программирования (ЯП), с головой хватит знаний школьной алгебры. Согласно опросу Stack Overflow Developer Survey 2020, более 50% разработчиков-респондентов написало свою первую строку кода до 16 лет, а в этом возрасте никто еще не изучает высшую математику. Значит, сделать старт в программировании может любой, кто учился/учится в обыкновенной среднестатистической школе.
Это первая ступенька на пути к качественному овладению ЯП. Когда вы начнете более-менее ориентироваться в языке и поднабьете руку на практических задачках, вам необходимо будет изучить смежные с математикой дисциплины: теорию множеств, графов, автоматов, алгоритмов, базовую логику. Это программа первых курсов технических вузов по IT-направлению, однако и самостоятельное ее освоение — вполне подъемная задача. При этом наименее зависимыми от математики являются такие специальности, как верстальщик и FrontEnd разработчик.
Хорошие знания в области высшей математики необходимы тем, кто хочет реализовать себя в таких направлениях: научная область, шифрование, машинное и глубокое обучение, Data Science, разработка искусственного интеллекта и все, что связано с большими данными. Именно там находит широкое применение тот мат. аппарат, которым славятся технические вузы.
Математика в программировании — это, прежде всего, о математическом и аналитическом мышлениях, которые тесно связаны с критическим мышлением и позволяют абстрагироваться, развязывать задачи с умелым применением логики. Именно правильный взгляд и рациональный подход к решению задач является главным оружием программиста, поскольку программирование — это динамика, в то время, как формулы, теоремы и аксиомы статичны. С развитием мат. мышления вам помогут различные книги, а также практика — кодинг, решение математических задачек и прочие упражнения, которые можно найти на просторах интернета.
Кстати, критическое мышление отлично развивают шоу с фокусниками. Посмотрите их, подумайте над тем, как маэстро смог провернуть тот или иной трюк (затем посмотрите соответствующие разоблачения). Также будут полезны детективные игры в стиле “Шерлока Холмса”, где, казалось бы, мистическим событиям находяться вполне логические объяснения. Умение не принимать всю информацию как чистую правду, а смотреть на нее со всех возможных углов — очень полезный навык не только в кодинге, но и в повседневной жизни.
В любом случае, данный миф о математике разрушен, а это означает, что начать программировать вы можете прямо сейчас.
Миф 2. Можно стать программистом, просто прочтя одну или несколько книг (“Прочту книгу — стану программистом”)
Программирование — это в большей степени практика. Теория здесь обязательно должна подкрепляться добротным кодингом. Это обеспечит закрепление полученной информации, а также будет гарантировать понимание вами материала и способствовать развитию ваших навыков написания кода. Поэтому чтение книг начинающими программистами должно обязательно сопровождаться соответствующими отработками (практикой), иначе получите ноль пользы от литературы и только зря потратите свое время, не достигнув желаемого.
Миф 3. Чтобы освоить программирование, необходимо быть очень умным
Миф, который отпугивает множество потенциальных программистов. Появился он из-за ложного убеждения, мол, программисты — это сверхразумы, которые видят мир, как в “Матрице” — в форме бесконечно бегущих зеленых символов. На самом деле они обыкновенные люди. Просто они горят кодом.
Программистам нравится создавать компьютерные программы, веб-сервисы, игры, мобильные приложения. Точно так же экстремалам-байкерам нравится выделывать различные трюки на железном коне, знатокам поварского дела — готовить вкусные и красивые блюда, летчикам — поднимать в воздух многотонные крылатые гиганты, водителям — колесить по бескрайним просторам, психологам — помогать людям понимать себя.
Если посмотреть на каждую профессию со стороны, в каждой можно найти свои сложности. И каждое препятствие преодолевается прежде всего большим трудом и упорством. А мозги — это часть организма, которая поддается прокачке, как и мышцы тела. Поэтому если вы чувствуете, что “недостаточно умны” для программирования, начинайте ломать этот барьер, работайте над собой и ни в коем случае не позволяйте каким-либо предубеждениям вставать у вас на пути. Никто этот шаг за вас не предпримет, так что все в ваших руках.
Миф 4. Необходимо обладать талантом к написанию кода
Успех программиста как такового обусловлен его заинтересованностью выполняемой задачи, количеством специализированных знаний, степенью владения ЯП и математическим мышлением, а также прилагаемыми усилиями. Таланта или какого-то дара в перечне нет. Так что программирование — это 95% усердной работы. Не ожидайте манны небесной — работайте, трудитесь и тогда вы сможете преуспеть в создании кода.
Миф 5. Программисты — замкнутые и необщительные люди
Возможно, в далеком прошлом это и было правдой, однако сейчас все совершенно по-другому. Более того, одними из главных требований к личным качествам программистов сегодня являются коммуникабельность, открытость и умение работать в команде. Время компьютерных гиков-одиночек кануло в Лету.
Различные конференции, хакатоны, совместный отдых и развлечения — эти вещи как-то мало совместимы с замкнутостью и необщительностью, вы не находите?
Такой спектр коммуникабельности не всегда можно встретить в фирмах, которые специализируются на коммуникациях с клиентами, а тут целые мероприятия, куда разработчики приходят пообщаться, заиметь новые связи, обменяться опытом и просто отдохнуть.
Миф 6. Программирование — скучное занятие
На первый взгляд это правдоподобно: человек сидит перед монитором (или несколькими), набирает строчки кода и так целый день напролет. Ну что тут может быть интересного? Даже как-то страшно становится... Однако, это очень урезанный взгляд на то, чем занимается программист.
Прежде всего, в рассматриваемой ситуации он может:
разрабатывать одну из механик компьютерной/мобильной игры;
создавать мобильное приложение;
реализовывать привлекательный внешний вид веб-сайта;
разрабатывать программу для какого-то “умного” устройства;
работать над автоматизацией каких-то рутинных процессов, которые обыватели проделывают чуть ли не каждый день;
писать программное обеспечение для космического аппарата, самолета, машины;
и многое другое.
Поверьте, кто-кто, а программисты не скучают — для них всегда есть работа, которая требует и знаний, и навыков, и творческого подхода к решению. К тому же, у каждого человека свой спектр интересов. Кто-то программирование считает скучным вследствие своего гиперактивного способа жизни, а кто-то просто поддается влиянию когнитивного искажения, прислушиваясь к собственному ложному суждению, обусловленному субъективными предубеждениями и стереотипами, социальными, моральными и эмоциональными причинами, то есть, вырабатывает свое отношение к программированию на основе искаженной информации.
Миф 7. Программисты всё пишут с нуля
Современные сложные программы состоят из сотен тысяч строк кода. Если бы программисты писали всё с нуля, то на разработку одной такой программы уходило бы очень много времени, особенно, если говорить про игры — там и вовсе приходилось бы для каждого нового экземпляра и движок свой создавать, и физику свою писать и делать много других лишних движений. А это и время, и деньги, и лишние нервы.
Поэтому в среде программистов принято не заниматься разработкой “велосипедов”, а использовать проверенные наработки. Разработчики часто применяют сторонние библиотеки, а также код, который был написан ими самими либо другими кодерами для других проектов. Это существенно упрощает и ускоряет создание проектов любой сложности и любого объема.
Миф 8. Чтобы устроиться на работу в качестве программиста, необходимо очень долго учиться
В каждом человеке это утверждение находит различное отображение. Кто-то может освоить ЯП и необходимые технологии за месяц. Кому-то на это потребуется пол года. Некоторые и за год не управятся.
Все зависит от вашего желания и стремления изучать ЯП, а также от времени, выделяемого вами на теорию и практику. Поставьте перед собой четкую цель и не сворачивайте с выбранного пути. Максимально быстрого обучения можно достичь, выбрав хорошие курсы, разбавляя их большим количеством самостоятельной практики.
Миф 9. После курсов у вас сразу высокая ЗП
Один из самых распространенных мифов, который делает программистов в глазах других, незнакомых с данной сферой занятости людей, буквально миллионерами. Мол, “вот мой знакомый недавно листовки раздавал, потом походил на курсы 2 месяца и теперь деньги лопатой гребет”. На самом деле все не так.
Те, кто только окончил курсы по освоению той или иной IT-специальности, по своему уровню знаний и умений могут претендовать на должность разработчика-джуниора (младший разработчик, Junior Developer). Конечно, все зависит от выбранного направления и конкретного места работы, однако джуниоры не срывают куш и первое время имеют довольно невысокую зарплату, которая не всегда доходит до пятизначной отметки (если говорить об украинских гривнах). Приблизительно на третий год работы можно говорить о действительно хорошей заработной плате.
Так что запомните: высокая ЗП — даже в IT — результат не курсов, а усердной и ответственной работы.
Миф 10. “Выучи язык программирования… за 1 час!”
Миф касается различных видео уроков на YouTube, которые пестрят подобным названием и тем самым обманывают вас. Ни один язык программирования не учится за час. Большое количество опытных программистов утверждают: сколько лет они программируют, столько они изучают ЯП. Языки взаимодействия с электронно-вычислительными устройствами настолько же компактны и многогранны, как и те, которыми мы пользуемся в повседневности.
Таким образом, во время изучения ЯП вы осваиваете основной синтаксис языка и то, как с ним работать. Затем во время профессиональной деятельности вы углубляетесь в язык и открываете для себя новые техники кодинга и решения различных задач. Но даже процесс овладения языком (синтаксис + основы работы с ним) — небыстрый процесс. У опытного программиста изучение нового ЯП может занять несколько дней. У новичка могут уйти месяцы — все зависит только от вас. Но на всевозможные “Выучи язык… за 1 час!” и подобные вещи не ведитесь; такие видео ролики создают иллюзию того, что вы знаете ЯП, в то время, как на самом деле вы толком ничего и не умеете.
Миф 11. Нельзя освоить программирование самостоятельно
Можно. Просто на это уйдет больше времени, чем на обучение при помощи специализированных курсов. Причины тому очень просты:
отсутствие ментора, который бы мог направлять вас в нужное русло, давать советы и отвечать на вопросы;
отсутствие предельно четкого понимания о знаниях и умениях, которыми необходимо обладать, чтобы в будущем занять соответствующую должность;
отсутствие четкой программы обучения, которая покроет весь необходимый профильный материал;
отсутствие стимула и достаточной мотивации, которые обычно присутствуют в коллективной среде (тренер, другие учащиеся, домашние задания и т. д.).
Главная проблема самостоятельного обучения в силе воли, которая зачастую быстро испаряется и в итоге изучение ЯП сводится к нулю. А программирование вещь серьезная — на недельку-другую все забросил и вот ты уже ничего не помнишь. Так что самостоятельно обучаться программированию можно, главное — запастись упорством, терпением, силой воли и мощной мотивацией, которая должна постоянно подпитываться.
Миф 12. Программисты разбираются во всём, что связано с техникой
Как вы поняли из вступления статьи, это тоже миф, причем один из самых распространенных.
Разработчик мобильных приложений специализируется на создании программ под мобильные устройства. Он знает соответствующие ЯП и смежные технологии, которые позволяют ему выполнять свою работу качественно и без лишних затрат времени, однако, с какой стати этот специалист должен уметь чинить телевизоры и устанавливать Windows?
Или, например, человек увлекается программированием микроконтроллеров. Почему он должен уметь создавать веб-сайты, если это абсолютно другая отрасль в IT? Вы же не требуете от педиатра вылечить вам зуб, а от стоматолога — избавить вас от кашля? Хотя и тот и тот специалист — врач. Каждый является специалистом в своей области и не следует это забывать.
Сюда же относится и миф о хакерах, согласно которому программисты приравниваются к людям этого рода деятельности. Опять-таки, сторонники данной теории слишком плохо знают IT-сферу, поэтому все равняют под одну гребенку. То, что человек разбирается в определенном ЯП и смежных технологиях не делает его хакером. Хакерство — это специфический род деятельности, который предусматривает достаточно глубокие знания компьютерных сетей, операционных систем, социальной инженерии, криптографии и множества других IT-ответвлений.
Если хотите узнать больше подробностей, не пожалейте своего времени — совершите поиск по специализированным ресурсам и тогда сможете расставить все точки над “i“ — кто кем является и какой спектр знаний-умений какому IT-специалисту свойственен.
Миф 13. Программирование — мужское занятие
Безусловно, мужчин в программировании больше, чем женщин. По данным исследования Stack Overflow Developer Survey 2020, женщин среди разработчиков 8%. В Украине процент женщин в IT в 2020 году достиг уровня 25%, согласно исследованиям DOU.ua. Однако, это связано, скорее, с определенными социальными и психологическими явлениями. Дело в том, что женщины по своей природе более “социальны”, чем представители мужского пола. Соответственно, они чаще выбирают те сферы занятости, которые предусматривают общение и социум.
В то же время парни и мужчины увлечены преимущественно техническими науками, поскольку достаточно распространенная среди них интровертивность позволяет посвятить необходимое количество времени цифрам, формулам и вычислениям. Плюс детская любовь к конструкторам, машинам, компьютерным играм и всему, что связано с техникой, с экспериментами. Ну и стереотипы, привитые обществом — куда же без них.
Однако, это ни в коем случае не означает, что женщинам путь в программирование заказан. С каждым годом все больше и больше представительниц прекрасного пола покоряют IT в различных его секторах. Не ведитесь на предубеждения — программирование абсолютно открыто для всех полов, народов и возрастов.
Миф 14. Существует самый-самый лучший языка программирования
Очень часто в интернете можно наткнутся на неутихающие дискуссии касательно того, какой ЯП лучше. Однако “лучшего” не существует. ЯП подбирается под задачу, а не задачи под ЯП. Если вы хотите максимально быстро развернуть свой сайт, лучше обратить внимание на Python и фреймворк Django. Хотите самостоятельно запрограммировать, допустим, сигнализацию с инфракрасным датчиком? Выбирайте C/C++ либо низкоуровневый Assembler. Те же С/С++ подойдут под разработку тяжеловесных игр, а для более простых идеальным выбором будет среда разработки Unity вместе с языком C#. FrontEnd разработка немыслима без языков верстки HTML & CSS, а также языка программирования JavaScript. Определитесь с тем, какая сфера разработки вам интересна и тогда выбирайте тот язык, который вам по душе.
Миф 15. Разработчики компьютерных игр — самые богатые и счастливые люди в IT
Казалось бы: ты посвящаешь себя тому, о чем мечтал, наверное, каждый ребенок девяностых и нулевых — компьютерным играм. Ну что может быть прекраснее этого? Воплощаешь в жизнь все свои детские задумки: создаешь героев, работаешь над их характерами, занимаешься реализацией собственного геймплея, придумываешь уникальные квесты, сюжет не хуже “Игры престолов”, открытый и насыщенный игровой мир… Да вот только одна проблема — это так не работает; на деле все получается совсем иначе.
Чтобы стать разработчиком игр, необходимо ими “гореть”, причем “гореть” так, чтобы ни время, ни вода, ни песок, ни отсутствие кислорода не смогли потушить ваше пламя.
Во-первых, богатство гейм девелоперов преувеличено. Если игра “выстреливает”, либо вы работаете на плюс-минус солидную студию, то тогда можно говорить о деньгах. Однако, приличное количество разработчиков занимаются инди-играми, то есть, разрабатывают игры без финансирования крупных компаний (в одиночку, либо небольшими группами энтузиастов). Естественно, пока вы работаете над своим продуктом, единственным источником внешних доходов могут быть лишь пожертвования (донаты) от потенциальной аудитории, которая заинтересована в вашем творении.
Также, важно знать, что разрабатывать игры и играть в них — две абсолютно разные вещи. Игростроение — сам по себе трудоемкий и комплексный процесс, который сильно отличается от того, что мы себе воображали в детстве.
Подробнее о пути гейм-мейкеров вы можете прочесть в нашей статье “Как стать разработчиком игр”. В ней мы постарались собрать максимальное количество информации с отечественных и зарубежных информационных ресурсов, чтобы подать вам все самое вкусное в одном компактном виде.
Миф 16. Начинающим айтишникам устроится на работу невозможно
Действительно, если брать на рассмотрение популярные направления в IT, то конкуренция достаточно большая. И это на фоне растущих с каждым годом требований от работодателей. Однако, это не означает, что IT-отрасль закрыла свои двери перед новичками. Как раз таки наоборот.
Сегодня функционирует множество программ стажировок от известных компаний, занимающихся созданием программного обеспечения. Например, EPAM, GlobalLogic, SoftServe и другие открывают вакантные места для тех, кто хорошо знает предметную область, но не имеет опыта работы. Конечно, необходимо будет пройти тестирование и/или собеседование, однако, это уже упрощает процесс внедрения в рабочую среду желанной IT-секции.
Миф 17. Пойду в ВУЗ, там меня научат программированию
В ВУЗе не обучают программированию в соответствии с теми требованиями и ожиданиями, которые предъявляют к соискателям IT компании. Хоть вам и будут преподавать алгоритмы и различные ЯП, но нагрузка в вузе будет настолько объемной и пёстрой, что вы физически не сможете нормально научиться программировать. Все равно вы будете вынуждены самостоятельно учить/доучивать тот или иной язык.
Если бы вы поступали на факультет, допустим, прикладной математики, вы бы туда шли с сильными знаниями по математике, правда? Так же само и с айтишными факультетами: если вы туда идете, у вас УЖЕ должен быть опыт программирования на любом ЯП. Иначе вам будет очень тяжело и мучительно больно.
Миф 18. Программирование имеет возрастные ограничения
Языки программирования, как и любые иностранные языки, вы можете начать изучать в любом возрасте. Возрастные рамки отсутствуют. Самое главное — это ваше желание учиться, развиваться и познавать.
Как показывает практика, уже с 8-9 лет дети способны понимать основные концепции ЯП и успешно создавать собственные программы. Если говорить об относительно великовозрастных людях, с ними работает то же правило — никогда не поздно учиться и узнавать нечто новое. Более того, активная мозговая деятельность (как такая, которая происходит в процессе программирования) является отличной профилактикой многих заболеваний мозга, связанных со старением. Так что программирование и юных развивает, и взрослых прокачивает + помогает держать в тонусе свои мысли.
Однако, при трудоустройстве возрастные ограничения могут иметь место. Это зависит от политики компании, которая ищет специалиста.
Миф 19. Программист — вымирающая профессия: роботы заменят этих специалистов
Очень распространенное мнение, имеющее право на жизнь. С одной стороны, все верно:
программ становится все больше и больше, а значит, потребность в программистах должна потихоньку отпадать (ведь скоро будет нечего программировать!);
при этом активно развивается искусственный интеллект, способный перенимать определенные функции человека, включая написание кода, на себя.
Поговорим о первом тезисе. Вроде бы все логично, но есть одно НО. Рассмотренная выше мысль будет абсолютно верна в том случае, когда мы говорим об информационных технологиях, как об области, которая находится в некоем вакууме, причем вакуум этот ограничен, то есть, имеет свой “потолок”, выше которого не прыгнешь. Наш же мир не является ограниченным, по крайней мере, человечество еще не смогло определить его грани.
Да, человеческие возможности упираются в определенные физические ограничения (невозможно поднять руками или ногами самолет, самостоятельно прыгнуть на высоту 5 метров, проглотить целиком кокос и т. д.), однако в нашем мозгу пока что не было замечено четких ограничений. Более того, с его помощью мы научились обходить естественные физические преграды: придумали и реализовали специальный транспорт, который может перевозить тяжелые объекты; изобрели джамперы, джетпаки (реактивные ранцы) для совершения высоких прыжков и полетов в воздух на относительно небольшие высоты; специальные предметы для разделывания экзотических фруктов и т. п.
Пока что ученые не смогли выжать максимум из нашего мозга и разглядеть границы нашего сознания. Если сложить вместе безграничность наших мыслей и безграничность мира, можно прийти к выводу, что любая сфера нашей жизни поддается совершенствованию и всегда есть, куда дальше двигаться.
Все области нашей жизнедеятельности неразрывно связаны между собой, хотим мы того или нет. Особенно сфера IT — сейчас она находит свое отображение везде:
музыка;
киноиндустрия;
компьютерные игры;
банковская сфера;
транспортная инфраструктура;
сфера безопасности (физическая и кибербезопасность);
СМИ;
медицина;
аграрная отрасль;
все, что связано с космическими разработками;
научные исследования любых направлений;
сфера образования...
Так можно продолжать, пока не будут перечислены все отрасли человеческой деятельности.
Давайте обратимся к сухим фактам и посмотрим на то, как “умерли” некоторые профессии в результате их совершенствования:
человечество уже научилось создавать искусственные фрукты и овощи, но фермеры никуда не пропали; более того — некоторые страны имеют острую нехватку профессионалов в аграрном деле;
существуют дистанционно управляемые боевые машины, дроны и другие приспособления для ведения боевых действий, но никто не говорит о роспуске армейских подразделений; набор призывников и добровольцев продолжается и приветствуется во всех странах;
в супермаркетах появились терминалы самообслуживания, однако продавцов никто не уволил; посмотрите вакансии на данную должность — их пруд пруди;
пассажирские самолеты обустроены очень надежными и серьезными компьютерными системами, которые выполняют много работы за человека и даже имеют функцию автопилота, но никто не спешит увольнять самих пилотов; более того, в мире острая нехватка данных специалистов, а их зарплаты считаются одними из самых высоких в мире;
такая же ситуация и с поездами — сегодня не надо кидать уголь в печь и разгонять поезд (как в XIX веке), но водители поездов никуда не испарились;
беспилотные автомобили уже разъезжают по улицам некоторых городов, однако водители государственных и коммерческих предприятий тоже никуда не делись, вакансии пестрят предложениями для водителей;
множество других примеров.
Примерно та же ситуация и у программистов. Правда, сфера IT настолько многогранна, что профессии в результате развития данной области будут просто эволюционировать. Например, веб-мастер двухтысячных стал современным FullStack девелопером, а само направление сайтостроения поделилось на два лагеря — FrontEnd и BackEnd. На границе программирования и системного администрирования образовалась DevOps инженерия. IT-специальности будут попросту перерождаться и образовывать новый виток с новыми должностями.
Для осознания системности нашего мира советуем прочесть книгу “Введение в системный анализ” (Ф. И. Перегудов, Ф. П. Тарасенко). Прекрасный труд, который очень хорошо демонстрирует взаимосвязанность всего, что нас окружает. Воспитывает системное мышление и заставляет смотреть на вещи более адекватно и трезво, находить логические связи между всевозможными событиями и процессами в нашем мире.
Поговорим о втором тезисе. Он касается искусственного интеллекта (ИИ). Сюда же добавим системы генерации кода, существующие в наше время. К примеру, взглянем на сервисы, которые позволяют создавать собственные сайты без знания IT технологий.
Действительно, сегодня существуют подобные системы, использование которых исключает необходимость владения языками верстки и программирования, однако они предоставляют достаточно шаблонные решения. В них вы не сможете воплотить все свои задумки — это сможет сделать только живой специалист. Системы генерации кода хорошо справляются с типичными задачами, однако в реальных ситуациях, где не всегда всё просто и зачастую необходимо импровизировать, сохраняя при этом код “в чистоте”, они бессильны.
Возвращаясь к разработчикам сайтов: верстальщики, FrontEnd и BackEnd разработчики не исчезли и спрос на них является одним из самых высоких среди направлений в IT.
ИИ уже давно разрабатывается и ученые демонстрируют потрясающие результаты: системы, которые обыгрывают шахматных гроссмейстеров и легенд покера, робот София, системы по распознаванию образов и т. д. Однако, какого-то обвала рынка программистов не последовало, массовые увольнения специалистов замечены также не были. Все спокойно.
Посетите, например, такие ресурсы по поиску работы, как https://jobs.dou.ua/, grc.ua, hh.ru — вакансий для сайтостроителей много, равно как и для других айтишных специальностей. Не похоже на упадок эпохи программирования.
Несмотря на то, что сфера IT и без того находится на пике популярности, она испытывает дефицит рабочих кадров. Так что вы сможете прекрасно себя реализовывать в IT еще несколько десятилетий как минимум. Главное — следите за тенденциями в IT и ловите попутный ветер.
Итоги
Большинство мифов, касающихся IT, рождены обыкновенным незнанием предметной области и изрядной долей ложных предубеждений. Сегодня мы постарались разрушить некоторую часть из них и показать вам, что программирование — не башня из слоновой кости, а вполне реальная и податливая сфера человеческой деятельности, в которой кипит жизнь и которая нуждается в пополнении своих рядов.
Не бойтесь делать шаги навстречу программированию. Разрушайте стены незнаний и непониманий. Если вы в чем-то неуверенны, интересуйтесь у знакомых-программистов, пишите на форумах, спрашивайте на стримах. Все зависит только от вас!
Желаем вам всевозможных успехов и профессиональных свершений!
Оставайтесь с ITVDN!
ТОП 20 тестових завдань на інтерв'ю для Java розробника
Автор: Армен Маїлян
Напишіть програму на Java для перевертання рядка, змінивши розташування символів у рядку задом наперед без використання вбудованих в String функцій
Напишіть програму на Java для перевороту послідовності символів у рядку без використання вбудованої в String функції reverse()
Напишіть програму на Java для того, щоб поміняти місцями значення, що зберігаються у двох змінних за допомогою третьої змінної
Напишіть програму на Java, щоб поміняти місцями значення, що зберігаються у двох змінних, без використання третьої змінної
Напишіть програму Java для підрахунку кількості конкретних слів у рядку, використовуючи HashMap
Напишіть Java-програму для ітерації об'єкта типу HashMap з використанням циклу while та покращеного циклу for
Напишіть програму на Java, щоб дізнатися, чи є число простим, чи ні
Напишіть Java-програму, щоб визначити, чи є рядок або число паліндромом, чи ні
Написати програму на Java для обчислення серії чисел Фібоначчі
Напишіть Java-програму для обходу ArrayList з використанням циклу for, while та покращеного циклу for
Напишіть програму на Java, щоб продемонструвати явну перевірку умов очікування
Напишіть Java-програму для демонстрації прокручування вгору / вниз
Напишіть програму на Java, щоб відкрити усі посилання на gmail.com
Напишіть код для Selenium, щоб перейти до попередньої вкладки
Напишіть програму на Java, щоб знайти повторювані символи в рядку
Напишіть Java-програму, щоб знайти друге за величиною число в масиві
Напишіть Java-програму для перевірки, чи є введене число числом Армстронга
Напишіть Java-програму для видалення всіх пробілів з рядка за допомогою replace()
Напишіть Java-програму для видалення всіх пробілів з рядка без використання replace()
Напишіть Java-програму для читання даних із таблиці Excel
У цій статті ми наведемо досить багато прикладів програм з тих, що просять написати претендентів під час проходження інтерв'ю на вакансію Java розробника. Вказані тестові завдання ми наводимо з реальними прикладами коду, заданими в інтерв'ю як початківцям, так і досвідченим кандидатам.
Сьогодні серед інтерв'юерів стало звичайною практикою давати тестові практичні завдання під час інтерв'ю, не фокусуючись лише на теоретичних питаннях. Такі завдання зазвичай задають на технічному етапі інтерв'ю Java розробника.
Для того, щоб допомогти претендентам на відповідні вакансії пройти такі інтерв'ю, ми хочемо перерахувати кілька дуже важливих прикладів програм на Java разом з належним описом кожного.
Крім того, ми також додаємо відповідні пояснення коду. Ці пояснення дадуть вам чітке уявлення, як працює кожна програма.
Найпопулярніші питання інтерв’ю Java-програмування.
Q # 1) Напишіть програму на Java для перевертання рядка, змінивши розташування символів у рядку задом наперед без використання вбудованих в String функцій.
Відповідь:
Для початку ініціалізуємо рядкову змінну st і використовуємо клас StringBuilder.
Об'єкт класу StringBuilder strB буде надалі використовуватися для додавання значення, що зберігається в рядковій змінній st.
Після цього ми використовуємо вбудовану в StringBuilder функцію reverse() і зберігаємо нову – обернений рядок в stB.
Нарешті ми виводимо на екран stB.
public class FirstTask{
public static void main(String[] args) {
// ITVDN.com 1 із ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st = "Задача1";
StringBuilder stB = new StringBuilder();
stB.append(st);
stB = stB.reverse(); // використовуємо StringBuilder для перевороту рядку
System.out.println(stB);
}
}
На екрані отримаємо:
1ачадаЗ
Q # 2) Напишіть програму на Java для перевороту послідовності символів у рядку без використання вбудованої в String функції reverse().
Відповідь
Спосіб 1:
Є кілька способів, за допомогою яких ви можете перевернути ваш рядок, якщо вам дозволено використовувати інші вбудовані функції рядка.
У цьому способі ми ініціалізуємо рядкову змінну з ім'ям st значенням заданого рядка. Потім ми конвертуємо цей рядок у масив символів за допомогою функції toCharArray(). Після цього ми використовуємо цикл for, щоб взяти всі символи у зворотному порядку і вивести їх так на екран по черзі.
public class SecondTask {
public static void main(String[] args) {
//ITVDN.com 2 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st = "Вчимося програмувати";
char symbols[] = st.toCharArray(); // конвертуємо рядок у масив символів, потім виводимо символи на екран у зворотному порядку
for(int x= symbols.length-1; x>=0; x--) {
System.out.print(symbols [x]);
}
}
}
На екрані отримаємо:
итавумаргорп ясомичВ
Спосіб 2:
Це ще один спосіб виконати завдання з переворотом послідовності символів у рядку. У цьому способі ви оголошуєте рядкову змінну st, а потім використовуєте клас Scanner, оголошуючи об'єкт scannerQ для роботи зі стандартним потоком введення даних.
У цьому випадку програма набуде рядкового значення через командний рядок (при його виконанні).
Далі ми використовували метод nextLine(), який прочитав наш рядок під час введення його через консоль з пробілами між словами рядка. Після цього ми використовували метод split() для поділу рядка на його підрядки (тут не вказується роздільник). Потім ми виводимо рядок у зворотному порядку, використовуючи цикл for.
import java.util.Scanner;
public class SecondTask {
public static void main(String[] args) {
// ITVDN.com 2 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st;
Scanner scannerQ = new Scanner(System.in);
System.out.println("Введіть ваш рядок:");
st = scannerQ.nextLine();
String[] temp = st.split(""); //використовуємо метод split для виведення рядку в зворотньому порядку
for(int x= temp.length-1; x>=0; x--)
{
System.out.print(temp [x] + "");
}
}
}
На екрані отримаємо:
Введіть ваш рядок:
asfasdf aasdfasdfadsf
fsdafdsafdsaa fdsafsa
Спосіб 3:
Це спосіб майже такий, як спосіб 2, але тут ми не використовуємо метод split(). Ми використовуємо клас Scanner та метод nextLine() для читання вхідного рядка. Потім ми оголосили цілочисельну змінну stringLength, присвоюючи їй значення довжини вхідного рядка.
Після цього ми вивели рядок у зворотному порядку, використовуючи цикл for. Однак ми використовували метод charAt(index), який повертатиме символ за конкретним індексом. Після кожної ітерації символ буде додано до нового рядка для отримання перевернутого значення рядкової змінної.
Потім ми виводимо змінну перевернутого рядка.
import java.util.Scanner;
public class SecondTask {
public static void main(String[] args) {
// ITVDN.com 2 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String mainString, reverseString = "";
System.out.println("Введіть рядок, щоб отримати перевернутий:");
Scanner scannerQ = new Scanner(System.in);
mainString = scannerQ.nextLine();
int stringLength = mainString.length();
for(int x= stringLength -1; x>=0; x--) {
reverseString = reverseString + mainString.charAt(x); //використовуємо вбудований метод charAt(), щоб перевернути рядок
}
System.out.println(reverseString);
}
}
На екрані отримаємо:
Введіть рядок, щоб отримати перевернутий:
Введений рядок
кодяр йинедевВ
Q # 3) Напишіть програму на Java для того, щоб поміняти місцями значення, що зберігаються у двох змінних за допомогою третьої змінної
Відповідь:
У цьому прикладі ми створюємо об'єкт класу Scanner для роботи зі стандартним потоком даних System.in. Ця програма прийматиме значення a та b через командний рядок.
Ми використали nextInt(), який буде поміщати введені користувачем значення цілочисельних змінних в a і b. Також оголошуємо тимчасову змінну.
Тепер логіка програми виглядає наступним чином: ми створюємо тимчасову або третю змінну з ім'ям temp, присвоюємо їй значення, що зберігається в змінній a, а потім присвоюємо значення b, і знову присвоюємо b значення temp. Таким чином, після виконання всіх операцій temp буде зберігати в собі значення a, a отримає значення b, а b матиме значення temp (яке дорівнює a).
import java.util.Scanner;
public class ThirdTask {
public static void main(String[] args) {
// ITVDN.com 3 з ТОП 20 тестових задач на інтерв’ю для Java розробника
int a, b, temp;
System.out.println("Введіть значення a та b");
Scanner scannerQ = new Scanner(System.in);
a = scannerQ.nextInt();
b = scannerQ.nextInt();
System.out.println("До обміну значеннями " + a + b);
temp = a;
a = b;
b = temp;
System.out.println("Після обміну значеннями " + a + b);
}
}
На екрані отримаємо:
Введіть значення a і b
23
45
До обміну значеннями 2345
Після обміну значеннями 4523
Q # 4) Напишіть програму на Java, щоб змінити місцями значення, що зберігаються у двох змінних, без використання третьої змінної.
Відповідь:
Спочатку все буде так само, як і в наведеному вище прикладі. Лише подальша логіка зміниться. Тут ми спочатку присвоюємо змінній a значення a + b, что означає, що a буде тепер мати в собі значення як a, так і b.
Потім ми присвоюємо змінній b значення a - b, що означає, що ми віднімаємо значення b із суми (a + b). Досі a все ще зберігає у собі суму початкових a і b. Але b має тепер значення первісного a.
Нарешті, на третьому кроці ми присвоюємо a значення a - b, що означає, що ми віднімаємо значення змінної b (яка зараз вже має в собі значення a) із суми (a + b). В результаті цих дій ми змінили місцями значення, які зберігаються у змінних.
import java.util.Scanner;
public class FourthTask
{
public static void main(String args[])
{
int a, b;
System.out.println("Введіть потрібні значення a та b");
Scanner scannerQ = new Scanner(System.in);
a = scannerQ.nextInt();
b = scannerQ.nextInt();
System.out.println("До обміну значеннями\na = "+a+"\nb = "+b);
a = a + b;
b = a - b;
a = a - b;
System.out.println("Після обміну значеннями без проміжної змінної\na = "+a+"\nb = "+b);
}
}
На екрані отримаємо:
Введіть потрібні значення a та b
23
45
До обміну значеннями
a = 23
b = 45
Після обміну значеннями без проміжної змінної
a = 45
b = 23
Q # 5) Напишіть програму Java для підрахунку кількості конкретних слів у рядку, використовуючи HashMap.
Відповідь:
Ця програма працює з класом-колекцією, в якій ми використовували HashMap для зберігання рядка.
Насамперед, ми оголосили нашу рядкову змінну з іменем st. Потім ми використовували функцію split() з одиночним пробілом, щоб можна було розбити рядок на масив з декількох слів.
Після цього ми створили екземпляр HashMap та цикл for. Всередині циклу for ми використовуємо оператор if else. Ми заходимо до кожного елементу масиву split та додаємо елементи цього масиву. Слова ми додаємо як ключі екземпляру HashMap. У якості значень HashMap ми будемо додавати те число, скільки разів при обході масиву слів нам це слово зустрілося. Якщо в наш екземпляр HashMap ми вже додали дане слово – при обході ми збільшимо значення, записане в HashMap відповідно до даного слова-ключа.
Щоразу, коли слово буде зустрічатися повторно (ми бачимо, що слово в екземпляр HashMap ми вже додавали) – значення-лічильник збільшується на 1. Якщо таке слово раніше не зустрічалося – значення-лічильник встановлюється на 1.
Зрештою, ми виводимо на екран HashMap.
Зверніть увагу: ту ж програму можна використовувати і для підрахунку кількості символів у рядку. Все, що вам потрібно зробити, це видалити один пробіл (видалити пробіл, вказаний в методі split) і прописати String [] words = st.split (“”);
import java.util.HashMap;
public class FifthTask{
public static void main(String[] args) {
// ITVDN.com 5 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st = "Current task posted for Java developers developers";
String[] words = st.split(" ");
HashMap<String,Integer> keyValue = new HashMap<String,Integer>();
for (int i=0; i<= words.length-1; i++) {
if (keyValue.containsKey(words[i])) {
int counter = keyValue.get(words[i]);
keyValue.put(words[i], counter+1);
}
else {
keyValue.put(words[i], 1);
}
}
System.out.println(keyValue);
}
}
На екрані отримаємо:
{Java=1, task=1, developers=2, for=1, Current=1, posted=1}
Q # 6) Напишіть Java-програму для ітерації об'єкта типу HashMap з використанням циклу while та покращеного циклу for.
Відповідь:
Тут ми спочатку вставили три елементи в змінну типу HashMap з ім'ям keyValue, використовуючи функцію put().
Розмір змінної keyValue можна одержати за допомогою методу size(). Після цього ми використовували цикл While для обходу keyValue, яка містить по одній парі ключ-значення для кожного елемента. Ключі та значення можуть бути отримані за допомогою методів getKey() та getValue().
Аналогічно ми використовуємо розширений цикл for, на елементах «qurentMe2» у HashMap.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class SixthTask{
public static void main(String[] args) {
// ITVDN.com ТОП 20 тестових завдань на інтерв’ю для Java розробника
HashMap<Integer,String> keyValue = new HashMap<Integer,String>();
keyValue.put(1, "Hello");
keyValue.put(2, "World");
keyValue.put(3, "Have a nice day!");
System.out.println(keyValue.size());
System.out.println("Цикл While:");
Iterator iter = keyValue.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry qurentMe = (Map.Entry) iter.next();
System.out.println("Ключ это " + qurentMe.getKey() + " Значення це " + qurentMe.getValue());
}
System.out.println("Цикл For:");
for(Map.Entry qurentMe2: keyValue.entrySet()) {
System.out.println("Ключ це: " + qurentMe2.getKey() + " Значення це: " + qurentMe2.getValue());
}
}
}
На екрані отримаємо:
3
Цикл While:
Ключ це 1 Значення це Hello
Ключ це 2 Значення це World
Ключ це 3 Значення це Have a nice day!
Цикл For:
Ключ це: 1 Значення це: Hello
Ключ це: 2 Значення це: World
Ключ це: 3 Значення це: Have a nice day!
Q # 7) Напишіть програму на Java, щоб дізнатися, чи є число простим, чи ні.
Відповідь:
Ми оголосили дві цілочисельні змінні temp та number і використали клас Scanner з nextInt (оскільки у нас може бути на розгляді тільки ціле число).
Оголошуємо логічну змінну numberIsPrime і встановлюємо її значення – true. Після цього ми використовуємо цикл for зі значенням змінної ітератора, що починається з 2. Кількість ітерацій, необхідне нам, дорівнюватиме половині введеного числа. Лічильник ітерацій збільшується на 1 після кожної ітерації. У змінну tempNumber ми поміщуватимемо залишок від поділу значення введеного числа на значення лічильника. Якщо залишок від поділу в одній з ітерацій дорівнюватиме 0, тоді numberIsPrime буде встановлений у false, введене число не буде простим, і ми виходимо з циклу. Якщо у всіх ітераціях ми отримуватимемо в temp деякі залишки від розподілу, відмінні від нуля – число буде простим.
Ґрунтуючись на значенні numberIsPrime, ми приходимо до висновку, чи є наше число простим, чи ні.
import java.util.Scanner;
public class SeventhTask {
public static void main(String[] args) {
// ITVDN.com ТОП 20 тестових завдань на інтерв’ю для Java розробника
int temp, number;
boolean numberIsPrime = true;
Scanner scannerQ = new Scanner(System.in);
number = scannerQ.nextInt();
scannerQ.close();
for (int x = 2; x<= number /2; x++) {
tempNumber = number %x;
if (tempNumber == 0) {
numberIsPrime = false;
break;
}
}
if(numberIsPrime)
System.out.println(number + " число є простим");
else
System.out.println(number + " число не є простим");
}
}
На екрані отримаємо:
27
27 число не є простим
Q # 8) Напишіть Java-програму, щоб визначити, чи є рядок або число паліндромом, чи ні.
Відповідь:
Щоб перевірити, чи є число або рядок паліндромом, чи ні, ви можете використовувати будь-яку програму, що перевертає рядки, з описаних вище.
Що вам потрібно зробити, то це додати один оператор if-else. Якщо вихідний рядок дорівнює перевернутому рядку, то число є паліндромом, інакше – ні.
import java.util.Scanner;
public class EighthTask{
public static void main (String[] args) {
// ITVDN.com ТОП 20 тестових завдань на інтерв’ю для Java розробника
String inputString, reversedString = "";
Scanner scannerQ = new Scanner(System.in);
int stringLength;
System.out.println("Введіть число або рядок");
inputString = scannerQ.nextLine();
stringLength = inputString.length();
for (int x = stringLength -1; x>=0; x--) {
reversedString = reversedString + inputString.charAt(x);
}
System.out.println("перевернутое значение: " + reversedString);
if(inputString.equals(reversedString))
System.out.println("Введене значення є паліндромом");
else
System.out.println("Введене значення не є паліндромом");
}
}
На екрані отримаємо:
Для рядка-
Введіть число або рядок
dfggg
перевернуте значення: gggfd
Введене значення не є паліндромом
Для числа-
Введіть число або рядок
777
перевернуте значення: 777
Введене значення є паліндромом
Q # 9) Написати програму Java для обчислення серії чисел Фібоначчі.
Відповідь:
Ряд Фібоначчі - це така серія чисел, де після перших двох чисел кожне число, яке зустрічається, є сумою двох попередніх чисел.
Приклад: 0,1,1,2,3,5,8,13,21 ………
У цій програмі ми знову використали клас Scanner з nextInt (описувалося вище). Спочатку ми вводимо (через командний рядок) деяке число, яку кількість чисел Фібоначчі ми повинні отримати. Ми оголосили цілочисленні змінні number, x, y та z, ініціалізували x та y нулем, а z – одиницею. Потім ми використовували цикл for для ітерації.
Процес рішення в циклі виглядає так – ми присвоюємо x значення, рівне y (яке в першій ітерації дорівнює 0), потім y присвоюємо значення змінної z (рівне у першій ітерації 1). Потім змінної z привласнюємо значення, що дорівнює сумі значень x та y.
import java.util.Scanner;
public class NinthTask{
public static void main(String[] args) {
// ITVDN.com 9 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
int number, x = 0, y=0, z =1;
Scanner scannerQ = new Scanner(System.in);
System.out.println("Введіть кількість значень");
number = scannerQ.nextInt();
System.out.println("Серія чисел Фібоначчі: ");
for (int i=0; i<= number; i++) {
x = y;
y = z;
z = x+y;
System.out.println(x + ""); // якщо ви хочете вивести в поточному рядку – використовуйте print()
}
}
}
На екрані отримаємо:
Введіть кількість значень
10
Серія чисел Фібоначчі:
0
1
1
2
3
5
8
13
21
34
55
Q # 10) Напишіть Java-програму для обходу ArrayList з використанням циклу for, while та покращеного циклу for.
Відповідь:
У цій програмі ми додали три елементи до ArrayList і вивели його розмір.
Потім ми використовуємо цикл While з ітератором. Щоразу, коли ітератор отримує наступний елемент, він буде відображати цей елемент, доки ми не досягнемо кінця списку. У нашому випадку це повторюватиметься тричі.
Аналогічно ми зробили для покращеного циклу for, де ми створили об'єкт o для ArrayList з ім'ям testList. Потім вивели на екран значення об'єкта.
Після цього ми створили цикл for, де ітератор i встановлено спочатку на індекс 0, а потім збільшується на 1 при кожній ітерації, поки не буде досягнута межа ArrayList. Нарешті, ми виводимо на екран кожен елемент, використовуючи метод get(index) для кожної ітерації циклу for.
import java.util.*;
public class arrayList {
public static void main(String[] args) {
// ITVDN.com 10 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
ArrayList testList = new ArrayList();
testList.add("50");
testList.add("60");
testList.add("70");
System.out.println(testList.size());
System.out.println("Цикл While:");
Iterator iter = testList.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
System.out.println("Покращений цикл For:");
for(Object o : testList) {
System.out.println(o);
}
System.out.println("Цикл For:");
for(int i=0; i< testList.size(); i++) {
System.out.println(testList.get(i));
}
}
}
На екрані отримаємо:
3
Цикл While:
50
60
70
Покращений цикл For:
50
60
70
Цикл For:
50
60
70
Q # 11) Напишіть програму Java, щоб продемонструвати явну перевірку умов очікування.
Відповідь:
У Selenium є два основні типи очікування – неявне (Implicit Wait) і явне (Explicit Wait). (Ми не розглядаємо явне (вільне) очікування у цьому прикладі).
Неявне очікування – оголошується один раз у коді поза операцією пошуку та діє до зміни. Це очікування виконується незалежно від будь-якої умови і застосовується до всіх наступних операцій пошуку неявно. У наведеній нижче програмі ви можете бачити застосування такого очікування для Google Chrome. Ми використовували кілька вбудованих методів для встановлення властивості, максимізації вікна, навігації по URL та пошуку веб-елементів.
WebDriverWait waitWD = new WebDriverWait(curentDriver, 20);
WebElement secondElement = waitWD.until(ExpectedConditions.visibilityOfElementLocated(By.partialLinkText("Тестування програмного забезпечення - Вікіпедія")));
secondElement.click();
У наведеному вище фрагменті коду можна побачити, що ми створили об'єкт waitWD для WebDriverWait, а потім ми здійснили пошук WebElement з ім'ям secondElement.
Умову встановлено таким чином, що веб-драйверу доведеться чекати, поки ми не побачимо посилання «Тестування програмного забезпечення – Вікіпедія» на веб-сторінці. Команда не виконається, якщо не знайде це посилання. Якщо посилання буде знайдено, веб-драйвер виконає клацання мишею за цим посиланням.
package Codes;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class EleventhTask{
public static void main(String[] args) {
// ITVDN.com 11 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
System.setProperty("webdriver.chrome.driver", "C:\\driver\\chromedriver.exe");
ChromeOptions cOptions = new ChromeOptions();
cOptions.addArguments("--disable-arguments");
WebDriver curentDriver = new ChromeDriver();
curentDriver.manage().window().maximize();
curentDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
curentDriver.navigate().to("https://www.google.com.ua");
WebElement curentElement = curentDriver.findElement(By.name("q"));
curentElement.sendKeys("Testing");
curentElement.submit();
WebDriverWait waitWD = new WebDriverWait(curentDriver, 20);
WebElement secondElement = waitWD.until(ExpectedConditions.visibilityOfElementLocated(By.partialLinkText("Тестування програмного забезпечення – Вікіпедія")));
secondElement.click();
}
}
Q # 12) Напишіть програму на Java для демонстрації прокручування вгору/вниз.
Відповідь:
Усі рядки кодів легко співвідносяться з кодом попереднього прикладу.
У код цієї програми ми включили JavascriptExecutor jscript, який виконуватиме прокручування. На останньому рядку коду ми передаємо window.scrollBy(argument1, argument2).
Якщо нам потрібно виконати прокручування вгору, тоді передаємо деяке значення в argument1, якщо нам потрібно прокрутити вниз – передаємо деяке значення в argument2.
package Codes;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
public class TwelfthTask{
public static void main(String[] args) {
// ITVDN.com 12 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
System.setProperty("webdriver.chrome.driver", "C:\\driver\\chromedriver.exe");
WebDriver qurentDriver = new ChromeDriver();
JavascriptExecutor jscript = (JavascriptExecutor) qurentDriver;
qurentDriver.manage().window().maximize();
qurentDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
qurentDriver.get("https://www.google.com.ua");
WebElement qurentElement = qurentDriver.findElement(By.name("q"));
qurentElement.sendKeys("SoftwareTestingHelp");
qurentElement.sendKeys(Keys.ENTER);
jscript.executeScript("window.scrollBy(0,900)");
}
}
Q # 13) Напишіть програму на Java, щоб відкрити всі посилання на gmail.com.
Відповідь:
Це типовий приклад застосування покращеного циклу for, який ми бачили у наших попередніх прикладах.
Після того, як ви відкрили веб-сайт, такий як Gmail, використовуючи get() або navigate().to(), ви можете використовувати локатор tagName, щоб отримати всі посилання на веб-сайті, які мають однакові теги.
У нас є покращений цикл for, в якому ми обходимо всі знайдені посилання за нашим тегом. Для кожного посилання типу WebElement у нашому листі посилань ми отримуємо самі посилання через getAttribute(«href») і тексти через getText().
package Codes;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class ThirteenthTask{
public static void main(String[] args) {
// ITVDN.com 13 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
System.setProperty("webdriver.chrome.drive", "C:\\driver\\chromedriver.exe");
WebDriver curentDriver = new ChromeDriver();
curentDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
curentDriver.manage().window().maximize();
curentDriver.get("https://www.gmail.com/");
java.util.List<WebElement> linkList = curentDriver.findElements(By.tagName("a"));
System.out.println(linkList.size());
for (WebElement thisLink: linkList) {
//виводимо на екран посилання http://google.com або https://www.gmail.com
System.out.println(qurentLink.getAttribute("href"));
//виводимо на екран текст посилання
System.out.println(qurentLink.getText());
}
}
}
На екрані отримаємо:
Starting ChromeDriver 2.38.551601 (edb21f07fc70e9027c746edd3201443e011a61ed) on port 16163
Only local connections are allowed.
3
https://support.google.com/accounts?hl=ru
Довідка
https://policies.google.com/privacy?gl=IN&hl=ru
Політика конфіденційності
https://policies.google.com/terms?gl=IN&hl=ru
Умови використання
Q # 14) Напишіть код для Selenium, щоб перейти на попередню вкладку.
Відповідь: Ми використовуємо клас Robot. Цей приклад досить важливий, тому що, якщо ви знаєте поєднання клавіш, ви можете використовувати різну навігацію в браузері та його вкладках.
Наприклад, якщо у вас у Chrome відкрито три вкладки, і ви хочете перейти на середню вкладку, то вам потрібно натиснути + 2 на клавіатурі. Те саме можна досягти за допомогою коду.
Використовуйте наступний код (відразу після того, як ми побачимо створення екземпляру класу Robot). Ми використовували об'єкт qurentRobot класу Robot, з двома вбудованими методами keyPress(KeyEvent.VK_*) та keyRelease(KeyEvent.VK_*).
package Codes;
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class FourteenthTask{
public static void main(String[] args) throws AWTException {
// ITVDN.com 14 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
System.setProperty("webdriver.chrome.driver", "C:\\driver\\chromedriver.exe");
WebDriver curentDriver = new ChromeDriver();
curentDriver.manage().window().maximize();
curentDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
curentDriver.get("https://www.google.com");
WebElement firstElement = curentDriver.findElement(By.name("q"));
firstElement.sendKeys("software testing help");
firstElement.sendKeys(Keys.ENTER);
String str = Keys.chord(Keys.CONTROL,Keys.RETURN);
curentDriver.findElement(By.partialLinkText("Software Testing Help - A Must Visit Software Testing Portal")).sendKeys(str);
Robot qurentRobot = new Robot(); // Ініціалізуємо екземпляр класу Robot
qurentRobot.keyPress(KeyEvent.VK_CONTROL); // застосовуючи клас Robot ви легко можете отримати необхідний результат, якщо ви знаєте потрібні комбінації кнопок
qurentRobot.keyPress(KeyEvent.VK_2); // тут ми натискаємо ctrl+2
qurentRobot.keyRelease(KeyEvent.VK_CONTROL); // як тільки ми натиснемо та відпустимо ctrl+2, ми перейдемо на другу вкладку.
qurentRobot.keyRelease(KeyEvent.VK_2); //якщо ви хочете знову повернутися до першої вкладки, натисніть і відпустіть vk_1
}
}
Q # 15) Напишіть програму на Java, щоб знайти повторювані символи в рядку.
Відповідь:
У цій програмі ми створюємо рядкову змінну st та ініціалізуємо цілочисельний лічильник, починаючи з нуля.
Потім ми створили масив символів для перетворення нашої рядкової змінної на цей масив. За допомогою двох циклів for ми проводимо порівняння символів із різними індексами.
Якщо два символи послідовного індексу збігаються, цей символ буде виведений на екран і лічильник кількості символів, що збігаються, збільшиться на 1.
public class FifteenTask{
public static void main(String[] args) {
// ITVDN.com 15 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st = new String("Hello");
int counter = 0;
char[] charsArray = st.toCharArray();
System.out.println("Повторювані символи тут:");
for (int i=0; i<st.length();i++) {
for(int j=i+1; j<st.length();j++) {
if (charsArray [i] == charsArray [j]) {
System.out.println(charsArray [j]);
counter ++;
break;
}
}
}
}
}
На екрані отримаємо:
Повторювані символи тут:
l
Q # 16) Напишіть Java-програму, щоб знайти друге за величиною число в масиві.
Відповідь:
У цій програмі ми ініціалізували масив із 10 випадковими елементами, з яких ми збираємося знайти друге за величиною число. Далі ми створили дві цілочисельні змінні, яким присвоюватимемо значення двох цілих чисел з масиву – найбільшого і другого за величиною. Обидві змінні спочатку набувають значення першого за індексом елемента масиву. Потім ми виводимо на екран усі елементи, використовуючи цикл for.
Подальша логіка роботи програми полягає в тому, щоб використовувати цикл for для обходу масиву.
При обході, якщо елемент масива з поточним індексом більше, ніж значення, що зберігається в змінній biggest, тоді змінній secondBiggest присвоюємо значення, що зберігається в biggest, а змінній biggest – нове найбільше значення відповідно до значення поточного елемента масиву. Знову ж таки, якщо елемент за поточним індексом більше, ніж secondBiggest, то привласність secondBiggest значення цього елементу.
Це буде повторюватися для кожної ітерації і, зрештою, після завершення обходу масиву в циклі ви отримаєте елементи – найбільший і другий за величиною елементи масиву в змінних biggest і secondBiggest відповідно.
public class SixteenthTask {
public static void main(String[] args)
{
// ITVDN.com 16 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
int numbersArray[] = { 10, 15, 32, 100, 16, 11, 98, 36, 95, 33 };
int biggest= numbersArray[0];
int secondBiggest = numbersArray[0];
System.out.println("Отриманий масив: ");
for (int i = 0; i < numbersArray.length; i++)
{
System.out.print(numbersArray[i] + "\t");
}
for (int i = 0; i < numbersArray.length; i++)
{
if (numbersArray[i] > biggest)
{
secondBiggest = biggest;
biggest = numbersArray[i];
}
else if (numbersArray[i] > secondBiggest && numbersArray[i] != biggest)
{
secondBiggest = numbersArray[i];
}
}
System.out.println("\nДруге за величиною число:" + secondBiggest);
}
}
На екрані отримаємо:
Отриманий масив:
10 15 32 100 16 11 98 36 95 33
Друге за величиною число: 98
Q # 17) Напишіть Java-програму для перевірки, чи введене число є числом Армстронга.
Відповідь:
Насамперед, нам потрібно зрозуміти, що таке число Армстронга. Число Армстронга – це число, значення якого дорівнює сумі цифр, з яких воно складається, зведених у ступінь, що дорівнює кількості цифр у цьому числі. Як приклад – число 371:
371 = 3 * 3 * 3 + 7 * 7 * 7 + 1 * 1 * 1 = 27 + 343 + 1 = 371
Якщо у вас число чотиризначне:
8208 = 8 * 8 * 8 * 8 + 2 * 2 * 2 * 2 + 0 * 0 * 0 * 0 + 8 * 8 * 8 * 8 = 4096 + 16 + 0 + 4096 = 8208
Виконуючи рішення, спочатку ми оголошуємо цілочисленні змінні tempNumber, x і y. Ми ініціалізували змінну y значенням 0. Потім ми створюємо змінну qurentNumber і присвоюємо їй ціле значення, яке ми збираємося перевірити, чи є воно числом Армстронга (у нашому випадку це 371). Потім ми надали нашій змінній tempNumber те значення, яке зберігається в перевіреній змінній qurentNumber.
Далі в циклі while ми змінній a привласнюємо залишок від ділення числа qurentNumber на 10 – і отримаємо число одиниць у початковому числі qurentNumber. Потім ми замінюємо значення змінної qurentNumber на результат діленого введеного числа на 10. Нашій змінній y, значення якої спочатку було встановлено як 0, присвоюється результат y + (x * x * x). Таким чином під час першої ітерації до y потрапить результат зведення в потрібний ступінь значення числа одиниць у початковому числі, при наступній ітерації до y до ступеня числа одиниць додасться результат зведення в ступінь числа десятків, і так далі по всіх розрядах до кінця числа qurentNumber з права на ліво.
Нарешті, ми використовуємо оператор if-else для перевірки, чи буде отримане значення змінної y дорівнювати значення змінної tempNumber (у якій зберігається вихідне число). Якщо y = tempNumber, то загадане число є числом Армстронга, інакше – ні.
public class SeventeenthTask{
public static void main(String[] args) {
// ITVDN.com 17 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
int y=0, x, tempNumber;
int qurentNumber=371; //Дане число ми перевірятимемо на те, чи є воно числом Армстронга
tempNumber = qurentNumber;
while(qurentNumber >0)
{
x= qurentNumber %10;
qurentNumber = qurentNumber /10;
y=y+(x*x*x);
}
if(tempNumber ==y)
System.out.println("Дане число є числом Армстронга");
else
System.out.println("Дане число не є числом Армстронга");
}
}
На екрані отримаємо:
Дане число є числом Армстронга
Q # 18) Напишіть Java-програму для видалення всіх пробілів з рядка за допомогою replace().
Відповідь:
Це проста програма, в якій ми маємо рядкову змінну st1.
Інша рядкова змінна st2 ініціалізується за допомогою методу replaceAll, який є вбудованим методом для видалення n числа пробілів. У результаті ми виводимо на екран st2, яка вже не містить пробілів.
public class EighteenthTask
{
public static void main(String[] args)
{
// ITVDN.com 18 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st1 = "Ми готуємось до інтерв’ю на вакансію Java розробника";
//Використовуємо метод replaceAll()
String st2 = st1.replaceAll("\\s", "");
System.out.println(st2);
}
}
На екрані отримаємо:
Миготуємосядоінтерв'юнавакансіюJavaрозробника
Q # 19) Напишіть Java-програму для видалення всіх пробілів з рядка без використання replace().
Відповідь:
Це ще один підхід до видалення всіх пробілів. Знову ж таки, для початку у нас є одна рядкова змінна st1 з деяким значенням. Ми перетворюємо цей рядок на масив символів, використовуючи toCharArray().
Потім ми маємо один об'єкт StringBuffer strB, який буде використовуватися для додавання значення, що зберігається за індексом chars [i] після того, як ми додали цикл і одну умову if.
Умову встановлено так, що елемент з індексом i масиву символів не повинен дорівнювати символу пробілу або табуляції. Зрештою, ми виводимо на екран наш об'єкт StringBuffer strB.
public class NineteenthTask
{
public static void main(String[] args)
{
// ITVDN.com 19 з ТОП 20 тестових завдань на інтерв’ю для Java розробника
String st1 = " Ми готуємось до інтерв’ю на вакансію Java розробника";
char[] charsArray = st1.toCharArray();
StringBuffer strB = new StringBuffer();
for (int i = 0; i < charsArray.length; i++)
{
if( (charsArray[i] != ' ') && (charsArray [i] != '\t') )
{
strB.append(charsArray [i]);
}
}
System.out.println(strB);
}
}
На екрані отримаємо:
Миготуємосядоінтерв'юнавакансіюJavaрозробника
Q # 20) Напишіть Java-програму для читання даних із таблиці Excel.
Відповідь:
Ці типи програм зазвичай використовують у рамках Selenium framework. Ми додали докладні коментарі для кожного кроку, щоб зробити програму більш зрозумілою.
Логіка починається з того моменту, як ми завантажили лист, на якому зберігаються дані. Ми намагаємося імпортувати електронну пошту та пароль. Для цього ми виймаємо комірку за допомогою методів getRow() та getCell(). Припустимо, у нас є електронна пошта та паролі в 1-й та 2-й комірці.
Далі ми встановлюємо тип комірки – рядкова. Після цього ми виконуємо звичайну операцію локатора веб-елемента (By.id), куди ми передали унікальні значення локатора, такі як "email" та "пароль", які ідентифікуватимуть ці елементи.
Нарешті, ми надсилаємо ключі, використовуючи element.sendKeys, де cell.getStringCellValue() – це ключ. Ця операція поверне вам значення, збережене в комірках № 1 і 2 відповідно.
@Test
public void ReadData() throws IOException
{
// Імпортуйте лист Excel з директорії web диску c.
//QurentSourceFile – це ім’я потрібного файлу excel
File sourceFile=new File("C:\\web\\QurentSourceFile.xls");
// На цьому кроці ми завантажуємо файл. Ми використовуємо FileInputStream для читання з
// файлу Excel. Якщо ви хочете проводити запис у файл –
// ви повинні використовувати FileOutputStream. Шлях до файлу передається
// в якості аргументу FileInputStream
FileInputStream fileInput = new FileInputStream(sourceFile);
// На цьому кроці ми завантажуємо робочу книгу Excel
// за допомогою HSSFWorkbook,
// в який ми передаємо fileInput в якості аргументу
HSSFWorkbook book = HSSFWorkbook(fileInput);
// На цьому кроці ми завантажуємо конкретний лист excel,
// на якому зберігаються дані.
qurentSheet= book.getSheetAt(0);
for(int i=1; i<= qurentSheet.getLastRowNum(); i++)
{
// Import data for Email.
qurentCell = qurentSheet.getRow(i).getCell(1);
qurentCell.setCellType(Cell.CELL_TYPE_STRING);
driver.findElement(By.id("email")).sendKeys(qurenrCell.getStringCellValue());
// Імпортуємо дані з комірок з паролями
qurentCell = qurentSheet.getRow(i).getCell(2);
qurentCell.setCellType(Cell.CELL_TYPE_STRING);
driver.findElement(By.id("password")).sendKeys(qurenrCell.getStringCellValue());
}
}
Резюме
У цій статті ми розглянули найпоширеніші тестові завдання для розробників Java з прикладами коду. Ці завдання застосовуються на більшості технічних інтерв'ю Java розробників.
Ми розглянули такі прийоми роботи з даними за допомогою Java, як базові маніпуляції з рядками, цілими числами та символами, використання Selenium, читання даних із файлу. Теперь у вас є достатньо інформації про те, як проходити технічний етап інтерв'ю на вакансію Java розробника.
Звичайно, вам потрібні будуть інші навички. Вивчення напам'ять рішень 20 завдань може бути головним способом підготуватися до інтерв'ю. Вам слід мати гарний набір теоретичних знань та практичних навичок. З нашого боку портал ITVDN.com готовий запропонувати програму підготовки Java розробника, що включає відео курси як із Java, так і за супутніми технологіями.
Також рекомендуємо вам ознайомитись із серією відео «Підготовка до співбесіди в IT компанії. Питання та відповіді. Хитрощі. Трюки»
За матеріалами статті.
Введення в розробку програм під iOS. Частина 7
Автор: Volodymyr Bozhek
Здравствуйте, дорогие читатели.
В этом уроке мы:
выполним рефакторинг проекта “Warehouse” под “iOS”. Создадим удобную структуру расположения модулей в проекте.
научимся пользоваться библиотекой “Alamofire”.
научимся сохранять объекты в настройки телефона и извлекать их.
реализуем функциональность для работы со всеми сервисами, созданными в данном уроке.
Откройте проект “Warehouse” под iOS, который мы делали на протяжении всех уроков.
Запросы на сервер можно отправлять синхронно и асинхронно. Основное требование Apple, чтобы приложение не зависало и большие операции выполнялись в фоновом режиме.
Представим ситуацию: у нас мобильный интернет “GPRS EDGE”, скорость соединения около 54 кб/сек. Если отправлять запрос на получение данных на сервер синхронно, мы блокируем работу приложения и пользователь вынужден ждать. Наша задача - дать пользователю выбор, ждать или нет. Поэтому лучше всего отправлять такой запрос асинхронно.
На сегодняшний день существует такая библиотека “Alamofire”, где собраны лучшие практики асинхронного программирования и все оптимизировано за нас.
Откройте браузер и перейдите по адресу: “https://github.com/Alamofire/Alamofire”. На сайте нажмите кнопку “Clone of download” и в появившемся окне нажмите “Download Zip”.
Когда скачается архив, распакуйте его.
Выделите файл “Alamofire.xcodeproj” и папку “Source” и скопируйте их в папку проекта “Warehouse”.
Перейдите в проект “Warehouse”.
В панели навигации выполните контекстное меню по папке “Warehouse” и в контекстном меню выберите “Add files to 'Warehouse'...”. В открывшемся диалоговом окне выбора файлов выберите файл “Alamofire.xcodeproj”.
Выполните в меню “Product -> Build”, чтобы собрать проект.
Затем выделите в панели навигации папку “Warehouse”, в области содержимого откроются свойства проекта. Откройте вкладку “General”, пролистайте область содержимого в самый низ.
Установите свойство “Embedded binaries”, нажмите плюсик. В появившемся диалоговом окне, выберите “Alamofire.Framework.iOS” и нажмите кнопку “Add”.
Результат должен получиться таким.
Выполните в меню “Product -> Build”, чтобы собрать проект.
Итак, что мы только что сделали. Мы подключили исходники библиотеки “Alamofire” в проект “Warehouse”. Можно было это сделать и отдельным проектом, но так проще для восприятия. Затем мы подключили сборку “Alamofire.framework.iOS” в приложение “Warehouse” для возможности обращения к классам данной библиотеки из нашего проекта.
Теперь нам надо создать модели данных, в которые мы будем сохранять JSON объекты, полученные с сервера. Также необходимо предусмотреть удобство сохранения и извлечения данных, поскольку запросы будут асинхронными и просто вернуть нужный экземпляр из метода будет проблематично.
Именно поэтому мы поступим следующим образом.
Отправляем запрос на сервер асинхронно. В метод отправки добавляем callback метод, который вызовется после того, как модель была успешно сохранена в настройки телефона.
Ждем ответа. Когда ответ пришел, десериализуем объект JSON в нужную модель данных, объявленную в приложении.
Сохраняем заполненную модель в настройки телефона.
В контроллере, из которого был отправлен запрос, в callback методе извлекаем модель из настроек телефона и инициализируем данными элементы управления.
Создайте следующую структуру папок в панели навигации.
В папку “Warehouse” были добавлены папки “Controllers”, “Protocols”, “Models”, “Services”.
В папку “Controllers” были добавлены папки “Users”, “Registration”, “Authorization”, “Supplies”.
В папке “Controllers” будут храниться модули контроллеров.
В папке “Protocols” будут храниться модули протоколов.
В папке “Models” будут храниться модули моделей данных.
В папке “Services” будут храниться модули обращения к REST сервисам.
Сейчас нам необходимо провести некоторую подготовку, перед тем как начать реализовывать приложение. Пока, давайте, добавим нужные модули. Сам по себе добавленный модуль ничего не делает.
Перетащите в папку “Controllers / Supplies” модули “SuppliesViewController.swift”, “ProductViewController.swift”.
Перетащите в папку “Controllers / Authorization” модуль “ViewController.swift”.
Добавьте в папку “Controllers / Registration” модуль “RegistrationViewController.swift”.
Добавьте в папку “Controllers / Supplies” модуль “ImageCollectionViewController.swift”.
Добавьте в папку “Controllers / Users” модули “UsersViewController.swift”, “UserItemViewController.swift”.
Добавьте в папку “Protocols” модуль “SelectedImageProtocol.swift”.
Добавьте в папку “Models” модули “ProductModel.swift”, “UserModel.swift”.
Добавьте в папку “Services” модули “UserData.swift”, “ProductData.swift”, “Services.swift”, “AuthorizationService.swift”, “ProductService.swift”, “UserService.swift”.
У вас должна получиться следующая структура.
Как видите, проект стал больше. Он станет еще больше, как только мы заполним добавленные модули.
Откройте модуль “Models / UserModel.swift”. Заполните его, как показано ниже.
В этом модуле мы создаем модель пользователя, в которую будем десериализовывать JSON объект пользователя, полученный от сервера.
В классе “UserModel” мы добавляем поля модели “_id”, “userName”, “userPwd”, “userDesc”, которые декларировали при объявлении схемы базы данных “Mongo DB” на сервере.
На 9 строке мы подключаем пространство имен “Foundation”.
На 11 строке мы объявляем класс с именем “UserModel”, который наследует класс “NSObject”, являющийся базовым классом для всех классов в iOS платформе. Наследуемся от протокола “NSCoding”, данный протокол необходимо реализовать, чтобы объект можно было сохранять в настройках телефона и извлекать оттуда.
Рассмотрим протокол “NSCoding”.
Протокол содержит метод “encode” и инициализатор.
Экземпляр класса “NSCoder” позволяет кодировать и раскодировать свойства класса, значения которых могут сохраняться в хранилище настроек телефона.
В методе “encode” данные извлекаются из свойств и кодируются в формат, нужный для сохранения в настройки телефона.
В инициализаторе данные извлекаются и раскодируются из настроек телефона, затем сохраняются в нужные свойства класса.
Вот так это работает.
Теперь вернемся и рассмотрим дальше модуль “UserModel.swift”.
На 12 строке мы объявляем поле “_id” типа “String” и инициализируем его значением по умолчанию, пустая строка.
На 13 строке мы объявляем поле “userName” типа “String” и инициализируем его значением по умолчанию, пустая строка.
На 14 строке мы объявляем поле “userPwd” типа “String” и инициализируем его значением по умолчанию, пустая строка.
На 15 строке мы объявляем поле “userDesc” типа “String” и инициализируем его значением по умолчанию, пустая строка.
На 17 строке мы переопределяем инициализатор по умолчанию для класса “NSObject”, тем самым мы говорим, что создание объекта без передачи аргументов в инициализатор будет возможно.
На 18 строке мы вызываем базовый инициализатор из класса “NSObject”.
На 21 строке мы объявляем параметризированный инициализатор. Данный инициализатор инициализирует поля класса “_id”, “userName”, “userPwd”, “userDesc” значениями, переданными в аргументы инициализатора.
На 31 строке мы реализуем конструктор протокола “NSCoding”. Ключевое слово “required” обозначает, что вызов этого инициализатора обязателен в первую очередь. Ключевое слово “convenience” гарантирует то, что после вызова этого инициализатора будут вызваны остальные инициализаторы классов, связанных с этим классом по цепочке вверх.
На 32 строке мы раскодируем из настроек телефона значение свойства “_id” путем вызова на экземпляре “aDecoder” типа “NSCoder” метода “decodeObject”, аргумент “forKey” которого принимает название поля/свойства, которое было сохранено в настройки телефона.
Этот метод возвращает значение “Any?”, т.е. неопределенный тип (аналог в C# - это Object, в Visual Basic - это Variant). Нам необходимо это значение вручную привести к нужному типу поля.
В настройках телефона содержится большой файл, который имеет древовидную структуру словаря ключ и значение.
Чтобы привести к нужному значению, мы используем конструкцию приведения к типу “as”. Причем, мы можем управлять этой конструкцией.
Например, нам надо привести тип “Any?” к типу “String”. Для этого мы используем конструкцию “as!”.
Если же нам надо привести к типу “String?” (nullable тип), тогда мы используем конструкцию “as?”. Зачем мы это делаем.
Настройки телефона разбиваются на разные файлы, для каждого приложения используется свой файл с настройками.
Когда приложения устанавливаются на macOS или iOS платформу, они устанавливаются в виде папки и имеют тип “Bundle”.
Apple довольно кардинально подошла к этому вопросу, она сделала так, что все настройки для каждого приложения хранятся внутри “Bundle” данного приложения. И когда приложение удаляется, удаляется “Bundle” со всеми настройками с диска операционной системы и не остается никаких следов после удаления.
Чего не скажешь о таких платформах, как Windows и Android, которые засоряют все пространство на диске операционной системы вокруг папки, в которую было установлено приложение, и выше. И когда приложение удаляешь, есть риск, что после этого будет необходимо еще и переустановить операционную систему.
Отчасти именно из-за этого подхода техника Apple пользуется такой популярностью и их устройства выигрывают по отказоустойчивости программного обеспечения по сравнению с конкурентами.
Если приложение обновляется, то файл настроек остается старый. Чтобы обновить его, надо выполнить запись в него, тогда старые конструкции удалятся, а новые добавятся. Представим себе ситуацию, сейчас в нашей модели “UserModel” всего 4 поля. Мы сохранили эту модель в файл настроек телефона. Отправили в магазин, пользователи скачали, установили приложение.
Вышла новая версия приложения, в которой мы добавили новое поле в эту модель с именем “userRole” типа “String”.
Разумеется, первая операция, которая будет выполнена как обычно - это запрос данных из настроек телефона, но для поля “userRole” установлено значение “nil” (аналог в C# null) и произойдет ошибка и креш приложения, так как мы не обработали эту ситуацию, а пытаемся через метод “decodeObject” получить значение этого поля и еще и явно привести его к типу “String” через, например, оператор “as!”.
Но в поле “userRole” - значение “nil” и оно не приведется к типу “String” через конструкцию “as!”, но может привестись без ошибок к типу “String?” через “as?”.
Надеюсь, я прояснил понятно, зачем нужна данная проверка. Продолжим.
На 32 строке мы проверяем, доступно ли нам значение поля “_id”. Чтобы проверить, равно ли возвращаемое значение “nil”, можно было бы использовать такую конструкцию: “aDecoder.decodeObject(..) == nil”, но такая конструкция не слишком информативна.
Поэтому мы используем конструкцию “(aDecoder.decodeObject(..) as? String) == nil”. Далее, если данная конструкция возвращает значение “true”, тогда мы через тернарный оператор присваиваем константе “_id” значение по умолчанию пустая строка, иначе присваиваем реальное значение поля “_id” из настроек телефона.
С 33 по 35 строку мы выполняем те же операции проверок для полей “userName”, “userPwd”, “userDesc”.
На 36 строке мы вызываем параметризированный конструктор и инициализируем его аргументы объявленными ранее константами.
На 47 строке объявлен вспомогательный метод “Populate”, который переносит данные из экземпляра “dictionary” типа “NSDictionary” в поля класса “UserModel”. Этот метод мы будем использовать, когда будем создавать запросы к серверу и получать ответы. В ответе содержится словарь типа “NSDictionary”, в котором находятся свойства с теми же именами, что и поля нашего класса.
С 48 по 51 строку мы инициализируем поля класса “UserModel” значениями из экземпляра “dictionary” c теми же проверками, что мы рассматривали выше.
На 53 строке объявлен вспомогательный метод “PopulateArray”, который переносит данные из экземпляра “array” типа “NSArray” в массив объектов типа “UserModel”. Этот метод мы будем использовать, когда в ответе от сервера придет массив JSON объектов типа “User”, чтобы сконвертировать массив типа “[NSDictionary]” в массив типа “[UserModel]”.
На 55 строке мы объявляем пустой массив типа “[UserModel]” c именем “result”.
На 56 строке мы выполняем цикл “foreach” по элементам массива “array”.
На 58 строке мы создаем новый экземпляр типа “UserModel” с именем “newItem”.
На 59 строке мы проверяем, содержит ли элемент массива значение “nil”.
На 60 строке мы вызываем на экземпляре “newItem” метод “Populate”, который перенесет данные из словаря типа “NSDictionary” в поля экземпляра “newItem”.
На 62 строке мы обращаемся к экземпляру “result” и вызываем у него метод “append”, чтобы добавить новый элемент в массив, и добавляем проинициализированный выше экземпляр “newItem”.
На 64 строке мы возвращаем сконвертированный массив типа “[UserModel]” из метода “PopulateArray”.
Откройте модуль “Models / ProductModel.swift”, заполните его в соответствии с содержимым ниже.
В этом модуле мы создаем модель пользователя, в которую будем десериализовывать JSON объект пользователя, полученный от сервера.
В классе “UserModel” мы добавляем поля модели “_id”, “userName”, “userPwd”, “userDesc”, которые декларировали при объявлении схемы базы данных “Mongo DB” на сервере.
На 9 строке мы подключаем пространство имен “Foundation”.
На 11 строке мы объявляем класс с именем “UserModel”, который наследует класс “NSObject”, являющийся базовым классом для всех классов в iOS платформе. Наследуемся от протокола “NSCoding”, данный протокол необходимо реализовать, чтобы объект можно было сохранять в настройках телефона и извлекать оттуда.
Рассмотрим протокол “NSCoding”.
Данный модуль не нуждается в объяснении, поскольку он по образу и подобию такой же, как и модуль “UserModel.swift”, с разницей лишь в том, что он построен на модели “Product” и содержит соответствующие поля.
Итак, модели данных мы создали, теперь мы сможем с уверенностью сохранять результаты ответа от сервера в нормальном виде и потом использовать их при инициализации элементов управления в соответствующих представлениях :)
Теперь приступаем к реализации классов для отправки запросов на сервер и сохранения/загрузки полученных данных в настройки телефона.
Откройте модуль “Services / Services.swift”, заполните его в соответствии с содержимым ниже.
На 11 строке объявлен класс с именем “Services”. Данный класс будет содержать вспомогательные свойства и методы, которые мы будем часто использовать при работе с ответом от сервера.
На 12 строке объявлено статическое свойство “host” типа “String” и мы ограничили его функциональность только одним аксессором доступа “get”. Т.е. значение свойства получить можно, но изменить нельзя. В данном свойстве содержится адрес, по которому доступен сервер.
В прошлом уроке, когда мы делали сервер, мы использовали адрес “http://localhost:3000” , внутри приложения клиента имеется свой “localhost”, именно поэтому мы используем явный IP адрес “127.0.0.1”, чтобы запрос шел именно на сервер, а не внутрь приложения.
На 18 строке объявлен статический метод “ejectJsonArray”, который принимает аргумент “data” типа “NSData?” и возвращает массив типа “NSArray?”. Данный метод конвертирует экземпляр класса “NSData” в экземпляр класса “NSArray”. В экземпляре “data” будет содержаться массив JSON объектов, полученных от сервера.
На 19 строке объявлена переменная “json” типа “NSArray?” и инициализировано значение по умолчанию “nil”.
На 20 строке мы используем конструкцию “do .. try .. catch” (аналог в C# - “try .. catch”). Символ “_” в блоке “catch” на месте аргумента используется потому, что мы не будем использовать этот аргумент в блоке “catch”.
В случае возникновения исключения мы не будем завершать аварийно приложение, а просто вернем значение “nil” по умолчанию, которое будет говорить о том, что конвертация не удалась, поскольку в NSData не содержится JSON объект.
Аналог JSON объекта в Swift - это класс “NSDictionary”.
На 21 строке мы правым операндом обращаемся к классу “JSONSerialization” и вызываем от его экземпляра статический метод “jsonObject”, который первым аргументом принимает данные типа “Data” для конвертации, вторым аргументом принимает опции конвертации.
Метод “jsonObject” возвращает экземпляр типа “Any”. Далее мы приводим это значение к нужному нам типу “NSArray?”, если приведение было проведено не успешно, будет возвращено значение “nil”.
Результат конвертации присваивается объявленной выше переменной “json”. Обратите внимание на ключевое слово “try” перед конструкцией “JSONSerialization.jsonObject(..)”, использование этого ключевого слова является обязательным, так как метод “jsonObject” имеет в своей сигнатуре ключевое слово “throws”, это означает, что данный метод может генерировать исключения и их необходимо обрабатывать.
На 23 строке в случае исключения вызванного методом “jsonObject” возвращается значение “nil”.
На 25 строке в случае успеха операции возвращается экземпляр типа “NSArray?”.
На 28 строке объвлен метод “ejectJsonDictionary” с той же сигнатурой, что и метод “ejectJsonArray”, но с разницей в возвращаемом значении.
В этом методе возвращается экземпляр типа “NSDictionary?”. Метод подразумевает, что в экземпляре “data” типа “NSData?” содержится единичный JSON объект и пытается его сконвертировать в тип “NSDictionary” , являющийся аналогом JSON объектов в Swift. Реализацию метода смысла описывать не вижу, она такая же, как и в методе “ejectJsonArray”.
Реализуем сохранение моделей данных “UserModel” и “ProductModel” в настройки телефона.
Откройте модуль “Services / UserData.swift”, заполните его в соответствии с содержимым ниже.
Модуль большой, поэтому будем разбирать его, разбив на две части по функциональности.
В первой его части будут описаны методы сохранения / извлечения единичного экземпляра типа “UserModel” и экземпляра типа массив “[UserModel]”.
Во второй его части будут описаны вспомогательные методы для обработчиков ошибок ответа от сервера и сохранения данных в настройки телефона.
Данные методы были созданы для того, чтобы не копипастить один и тот же код много раз, а стандартизировать и сделать его повторно используемым.
На 9 строке подключено пространство имен “UIKit”, необходимое для классов “UserDefaults”, “NSKeyedArchiver”, “NSKeyedUnarchiver”. В пространстве имен уже содержится подключенное пространство имен “Foundation”, поэтому нет смысла его тоже подключать для классов “NSArray”, “NSDictionary”, “NSData”.
На 10 строке подключено пространство имен “Alamofire”, необходимое для класса “DataResponse”.
На 12 строке объявлен класс с именем “UserData”.
На 13 строке объявлен метод “saveUserModel”, который принимает аргумент “userModel” типа “UserModel”. Метод будет сохранять поля модели “UserModel” в настройки телефона.
На 14 строке правым операндом мы обращаемся от экземпляра “UserDefaults” к его статическому свойству “standard”, это свойство является источником данных по отношению к файлу настроек телефона и содержит необходимые методы для сохранения / извлечения данных.
На 15 строке правым операндом мы обращаемся к экземпляру “NSKeyedArchiver” и вызываем статический метод “archivedData”. Этот метод принимает один аргумент “withRootObject” типа “Any” и возвращает экземпляр типа “Data”. Метод упаковывает экземпляр типа “UserModel” в экземпляр типа “Data” (аналог NSData) и возвращает его.
На 16 строке мы от экземпляра “userDefaults” вызываем метод “set”, который принимает два аргумента. Первый аргумент “_” типа “Any?” принимает экземпляр, который необходимо сохранить в настройках телефона. Второй аргумент “forKey” типа “String” необходим, чтобы задать название ключа в словаре настроек телефона. По этому ключу будет произведено сохранение экземпляра “UserModel”.
На 17 строке мы обращаемся к экземпляру “userDefaults”, вызываем от него метод “synchronize”. Данный метод выполняет сохранение экземпляра модели “UserModel” в настройки телефона (аналог в C#, например, 'using(StreamWriter sw = File.CreateText(“test.txt”)) { sw.Write(“some text”); sw.Flush(); }', из этого примера метод “Write” аналогичен методу “set”, а метод “Flush” аналогичен методу “synchronize”).
На 20 строке мы объявили статический метод “getUserModel”, который не принимает аргументов и возвращает экземпляр типа “UserModel”. В этом методе мы излекаем модель “UserModel” из файла настроек телефона по ключу, указанному в предыдущем методе при сохранении этой модели.
На 21 строке мы получаем экземпляр источника данных настроек телефона.
На 22 строке мы проверяем, если модель “UserModel” ранее не сохранялась в настройки телефона, тогда необходимо создать пустую модель по умолчанию.
Вызываем на экземпляре “userDefaults” метод “object”, который принимает аргумент “forKey” типа “String” и возвращает значение типа “Any?”. В качестве аргумента мы передаем ключ, под которым сохраняется данная модель в настройках телефона.
Если значение было найдено в словаре, будет возвращен экземпля , который надо будет привести у нужному типу.
Если значение не было найдено, будет возвращено значение “nil”.
На 23 строке мы создаем пустой экземпляр типа “UserModel”.
На 24 строке мы возвращаем этот экземпляр из метода.
На 26 строке код будет выполнен при наличии записи по ключу в словаре. Правым операндом мы обращаемся к экземпляру “userDefaults”, вызываем метод “object” и передаем в аргумент “forKey” ключ, по которому надо извлечь значение. Полученное значение приводим к типу “NSData”.
На 27 строке мы обращаемся к экземпляру “NSKeyedUnarchiver” и вызываем от него статический метод “unarchiveObject”, который принимает аргумент “with” типа “Data” и возвращает значение типа “Any”. Метод конвертирует экземпляр типа “Data” в экземпляр типа “Any”. Возращаемое значение метода мы явно приводим к типу “UserModel”.
На 28 строке мы возвращаем из метода полученный из словаря экземпляр типа “UserModel”.
На 31 строке объявлен метод “saveListOfUsersModel”, который принимает аргумент “listOfUsersModel” типа “[UserModel]” и ничего не возвращает. Метод сохраняет массив “[UserModel]” в файл настроек телефона. Описывать данный метод не имеет смысла, его работа аналогична методу “saveUserModel”.
На 38 строке объявлен метод “getListOfUsersModel”, который не принимает аргументов и возвращает экземпляр типа массив “[UserModel]”. Метод извлекает из настроек телефона массив типа “[UserModel]” по ключу, указанному в методе “saveListOfUsersModel”. Описывать данный метод не имеет смысла, его работа аналогична методу “getUserModel”.
Откройте модуль “ProductData.swift”, заполните его в соответствии с содержимым ниже.
В первой части описано сохранение / извлечение моделей типа “ProductModel”, “[ProductModel]” в настройки телефона. Описывать код не имеет смысла, его работа точно такая же, как и в модуле “UserData.swift”.
Во второй части описаны вспомогательные методы для обработки запросов от сервера и сохранения данных. Описывать код не имеет смысла, его работа точно такая же, как и в модуле “UserData.swift”.
Теперь, наконец то, мы можем реализовать первый класс для работы с сервисом авторизации пользователя и разобрать работу с библиотекой “Alamofire”.
Откройте модуль “Services / AuthorizationService.swift”, заполните его в соответствии с содержимым ниже.
На 12 строке объявлен класс с именем “AuthorizationService”.
На 13 строке объявлен метод “login”, который принимает три аргумента и ничего не возвращает.
Первый аргумент “userName” типа “String” - это имя пользователя (логин).
Второй аргумент “userPwd” типа “String” - это пароль пользователя.
Третий аргумент - это “callback” метод, который принимает аргумент типа “Bool” и ничего не возвращает.
Метод с такой сигнатурой, переданный в третий аргумент, будет вызван по завершению обработки ответа от сервера. В случае наличия ошибок в аргумент этого метода будет передано значение “false”. В случае успешно проведенной операции - значение “true”.
Да, можно было сделать этот аргумент другим типом, например, отдельным классом ошибок, в который можно было сохранять подробно, что за ошибка произошла.
Но это учебный пример, он итак очень большой. Я решил не усложнять этим жизнь ни себе, ни вам :)
На 14 строке правым операндом мы вызываем метод “request” из библиотеки “Alamofire”.
Метод принимает 5 аргументов и возвращает значение типа “DataRequest”.
Данный метод используется для отправки HTTP/HTTPS запросов на сервер.
Первый аргумент “_” типа “URLConvertible” принимает адрес, по которому необходимо отправить запрос. Мы его инициализируем значением “\(Services.host)/auth”, которое после подстановки свойства “host” будет выглядеть вот так: “http://127.0.0.1:3000/auth”.
Второй аргумент “method” типа “HTTPMethod” принимает HTTP глагол. Тип “HTTPMethod” является перечислением и может принимать следующие значения.
Мы инициализируем данный аргумент значением “.post” (это сокращенная запись, полная запись “HTTPMethod.post”)
Третий аргумент “parameters” типа “Parameters?”. Принимает словарь, ключ - значение, мы его заполняем теми же данными, что отправляли в прошлом уроке через утилиту “Postman”. Конкретно, мы передаем значение имени пользователя и его пароль.
Четвертый аргумент “encoding” типа “ParameterEncoding”. Этим параметром задается заголовок запроса “Content-Type”, т.е. указывается тип данных, который будет отправлен на сервер, сами данные задаются в третьем аргументе “parameters”. Мы инициализируем аргумент значением “JSONEncoding.default”, что аналогично записи “application/json”. Тип “ParameterEncoding” является протоколом и имеет следующие реализации себя в классах “URLEncoding”, “JSONEncoding”, “PropertyListEncoding”. Про эти классы вы можете почитать в официальной документации на сайте “Alamofire”.
Пятый аргумент “headers” типа “HTTPHeaders?” имеет значение по умолчанию “nil”, в нашем вызове метода он не используется. Данный аргумент задается, если вы желаете отправить в заголовках запроса на сервер какую- либо информацию, например, “cookie”. Если у вас сервер написан на Web API и вы используете для авторизации OWin , с настройкой надо использовать “cookie” как ключ авторизации пользователя. Тогда при отправке запросов вам будет необходимо заполнять этот аргумент. Пример его заполнения может выглядеть вот так:
Этот пример взят из моего приложения “Smart Payment”, написанного на Swift 2.2 c минимальной версией iOS 8.3 библиотекой “Alamofire” версии 2. Чтобы этот пример работал на Swift 3, надо заменить значение параметра “encoding” на “JSONEncoding.default”. Этот пример не относится к нашему приложению и его переписывать не нужно.
На 16 строке мы вызываем от экземпляра “r” типа “DataRequest” метод “responseJSON”. Этот метод выполняет запрос на сервер и содержит три аргумента.
Первый аргумент “queue” типа “DispatchQueue?” по умолчанию содержит значение “nil”. Используется при переопределении потока выполнения запроса.
Второй аргумент “options” типа “JSONSerialization.ReadingOptions” по умолчанию содержит значение “.allowFragments”. Используется для задания опция отправки запроса. Тип “ReadingOptions” является структурой и имеет такое объявление.
Третий аргумент “completionHandler” типа делегат “@escaping (DataResponse) -> Void”. Данная сигнатура принимает один аргумент типа “DataResponse” и ничего не возвращает. В этом аргументе содержится ответ от сервера. Сам метод вызывается после запроса, когда сервер отправил ответ на клиент.
Обратите внимание, на 16 строке, когда мы вызываем метод “responseJSON”, у нас нет круглых скобок после метода, а идут сразу фигурные скобки, это называется “closure” подход. В фигурных скобках мы объявляем лямбда метод с сигнатурой, соответствующей третьему аргументу метода “responseJSON”.
Запись “response in” означает следующее: “response” - это аргумент лямбда метода, а само тело метода начинается после ключевого слова “in”. Аргумент “response” имеет тип “DataResponse”.
На 17 строке мы проверяем, если ответ пришел с ошибкой, то это означает, что данных нет и пришло пустое значение типа “nil”. Мы обращаемся к экземпляру “response” и вызываем свойство “result” типа “Result” и от свойства “result” вызываем свойство “value” типа “Value?”. Если в свойстве “value” пришло пустое значение, значит запрос не был выполнен, возможно, по причине недоступности сервера или отсутствия подключения к интернету.
На 18 строке мы вызываем “callback” метод “completion” со значением “false”.
На 21 строке, в случае если проблем с подключением к серверу не возникло, мы правым операндом вызываем от экземпляра “Services” метод “ejectJsonDictionary”, который ранее уже подробно рассматривали. В аргумент “data” мы передаем значение свойства “data”, вызванного от экземпляра “response”. Свойство “data” имеет тип “Data?”. Левому операнду “json” присваивается экземпляр “NSDictionary”, в котором содержится JSON объект пользователя, прошедшего авторизацию, который был отправлен сервером на клиент.
На 22 строке мы проверяем, если пришел JSON объект с ошибкой, тогда возвращаем нужный флаг в “callback” методе “completion”. Помним, что в предыдущем уроке мы создавали JSON объект с ошибкой на сервере, в случае если пользователь ввел неверные данные авторизации, JSON выглядел вот так '{ “Error”: “Authorization error”}'.
У словаря “json” есть возможность получить все ключи, для этого надо вызвать свойство “allKeys”, которое имеет тип “[Any]”. Но, поскольку выполнить поиск строки в массиве объектов нельзя, мы оборачиваем массив объектов в массив типа “Array” и явно приводим его к нужному нам типу массива “[String]”. Мы это делаем, поскольку знаем наверняка, что ключи JSON объекта имеют тип “String”. Затем от полученного экземпляра типа “[String]” мы вызываем метод “contains”, в аргумент которого передает название свойства JSON объекта ошибки, значение “Error”. Если такой JSON объект пришел в ответе, значит пользователь ввел неправильные логин или пароль, или ввел несуществующего пользователя.
На 23 строке мы вызываем “callback” метод “completion” с аргументом “false”.
На 25 строке мы создаем пустой экземпляр “userModel” типа “UserModel”.
На 26 строке мы вызываем на экземпляре “userModel” метод “Populate” в аргумент “dictionary”, передаем экземпляр “json”. Метод “Populate” перенесет данные из экземпляра “json” типа “NSDictionary” в поля экземпляра “userModel”.
На 27 строке мы обращаемся к экземпляру “UserData” и вызываем статический метод “saveUserModel”, чтобы сохранить данные экземпляра “userModel” в настройки телефона. В аргумент “userModel” мы передаем экземпляр “userModel”.
На 28 строке мы вызываем “callback” метод “completion” и передаем в него результат операции “true”.
Позже в контроллере, где мы будем вызывать метод “login”, будет передан “callback” метод “completion”, объявленный в этом контроллере. Тем самым решается проблема ожидания ответа от сервера в контроллере, так как есть “callback” метод. Внутри этого метода мы проверим результат операции и затем обратимся к экземпляру “UserData” и вызовем метод “getUserModel”, который вернет нам экземпляр “UserModel”, с которым мы сможем работать на стороне контроллера.
Откройте модуль “Services / UserService.swift”, заполните его, как показано ниже.
На 12 строке объявлен класс “UserService”.
На 13 строке объявлен метод “get”, который принимает два аргумента и ничего не возвращает.
Первый аргумент “id” типа “String?” принимает идентификатор пользователя “_id”. Если передать в аргумент значение “nil”, будет возвращен весь список пользователей, если передать конкретный идентификатор, будет возвращен конкретный пользователь.
Второй аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 29 строке объявлен метод “create”, который принимает 4 аргумента и ничего не возвращает. Метод отправляет запрос на создание пользователя на сервер.
Первые три аргумента “userName”, “userPwd”, “userDesc” имеют тип “String” и содержат данные пользователя. Идентификатор пользователя в операции создания не нужен. Так как идентификатор пользователю присваивается на стороне сервера и данный метод выполняет как раз эту операцию.
Четвертый аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 37 строке объявлен метод “update”, который принимает 5 аргументов и ничего не возвращает. Данный метод отправляет запрос на обновление данных пользователя на сервер.
Первые 4 аргумента “id”, “userName”, “userPwd”, “userDesc” имеют тип “String” и содержат данные пользователя.
Пятый аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 45 строке мы объявляем метод “delete”, который принимает два аргумента. Этот метод отправляет запрос на удаление пользователя на сервер.
Первый аргумент “id” типа “String?” принимает идентификатор пользователя “_id”.
Второй аргумент “completion” типа делегат “(Bool) -> Void”, используется для получения сигнала, что ответ был получен и основные операции были выполнены.
Откройте модуль “Services / ProductService.swift”, заполните его, как показано ниже
На 12 строке объявлен класс “ProductService”.
На 13 строке объявлен метод “get”, который принимает два аргумента и ничего не возвращает.
Первый аргумент “id” типа “String?” принимает идентификатор товара “_id”. Если передать в аргумент значение “nil”, будет возвращен весь список товаров, если передать конкретный идентификатор, будет возвращен конкретный товар.
Второй аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 29 строке объявлен метод “create”, который принимает 4 аргумента и ничего не возвращает. Метод отправляет запрос на создание товара на сервер.
Первые три аргумента “productImage”, “productName”, “productDescription” имеют тип “String” и содержат данные товара. Идентификатор товара в операции создания не нужен. Так как идентификатор товару присваивается на стороне сервера и данный метод выполняет как раз эту операцию.
Четвертый аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 37 строке объявлен метод “update”, который принимает 5 аргументов и ничего не возвращает. Данный метод отправляет запрос на обновление данных товара на сервер.
Первые 4 аргумента “id”, “productImage”, “productName”, “productDescription” имеют тип “String” и содержат данные пользователя.
Пятый аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
На 45 строке мы объявляем метод “delete”, который принимает два аргумента. Этот метод отправляет запрос на удаление товара на сервер.
Первый аргумент “id” типа “String?” принимает идентификатор товара “_id”.
Второй аргумент “completion” типа делегат “(Bool) -> Void” используется для получения сигнала, что ответ был получен и основные операции были выполнены.
Заметьте, адреса запросов, используемых нами при отправке на сервер, совпадают с теми, что мы использовали в прошлом уроке в утилите “Postman”.
На этом урок завершен.
На следующем уроке мы продолжим разработку клиента, создадим контроллеры и представления для редактирования пользователей, подключим работу сервисов к странице входа в систему, создадим форму регистрации пользователя.
150+ питань з .NET для Junior, Middle та Senior
Автор: Редакція ITVDN
Дорогие друзья! Предлагаем вашему вниманию перевод статьи, опубликованной на DOU.ua 10 ноября 2020 года. Оригинальная версия на украинском языке доступна по ссылке.
На DOU размещено более 450 вакансий для .NET-разработчиков, что свидетельствует о популярности этой технологии. Редакция DOU собрала вопросы, которые .NET-разработчики задают своим коллегам на технических собеседованиях. Готовьтесь на здоровье :)
Вопросы для Junior
Общее
1. Назовите основные принципы ООП.
2. Что такое наследование, инкапсуляция, абстракция, полиморфизм: приведите примеры (желательно из собственного опыта). От какого класса неявно наследуются все классы в .NET? Разрешено ли множественное наследование в C#?
3. Что такое рекурсия?
4. Что такое лямбда-выражение?
5. Что такое параллельное программирование (многопоточность) и его назначение? Какие классы используются?
6. Что такое JSON?
7. Как вы понимаете REST?
8. Расскажите о SPA concept.
9. Какие GoF-паттерны использовали?
10. Какая разница между GET и POST HTTP методами?
11. Какую проблему решает Docker? Каковы его плюсы и минусы?
12. Чем принципиально отличаются unit-тесты от интеграционных тестов?
Обработка исключений
13. Что такое Exception?
14. Для чего служат try, catch, finally? В каком случае может не выполниться блок finally?
15. Что такое call stack? Какие ключевые слова вы знаете?
Платформа .NET
16. Что такое ASP.NET?
17. Какие существуют типы Action filters?
18. Что такое Web Service?
19. Что такое CLR?
20. Что такое сборщик мусора (Garbage Collector) на базовом уровне?
21. Что такое делегат?
22. Отличается ли Delegate от Action?
23. Что такое LINQ и для чего используется? Приведите несколько примеров применения LINQ.
24. Что такое пространство имен (namespace) и зачем это нужно?
Типы данных, коллекции и структуры данных
25. Какие типы данных вы знаете?
26. Какие примитивные типы знаете?
27. Что такое Nullable-тип?
28. Что такое тип значения, а что такое тип ссылки? Что из этого class, а что struct? В каком участке памяти они хранятся?
29. Чем отличаются value от reference type? String - это reference или value?
30.В чем отличие между string builder и string?
31. Что такое дженерики? Какие проблемы они решают?
32. Что такое boxing / unboxing?
33. Что такое Array, List, HashSet, Dictionary? Приведите примеры использования этих структур данных. Какая сложность операций с ними (поиск, вставка, удаление)?
34. Какие знаете коллекции?
35. Что делает оператор yield?
Классы, структуры и интерфейсы
36. Что такое класс?
37. Чем отличается класс от абстрактного класса?
38. Чем отличается абстрактный класс от интерфейса? Для чего нужны интерфейсы и какие задачи они выполняют?
39. Какие вы знаете модификаторы доступа?
40. В чем разница между обычным классом и статическим?
41. В чем разница переопределения метода между ключевыми словами new и override?
42. Какое различие между const и read only?
43. Разница между структурой и классом. Приведите примеры структур.
44. Может ли экземпляр структуры храниться в куче (heap)? Как это сделать?
Асинхронность
45. Что такое асинхронность и чем она отличается от многопоточности?
46. Какие есть ключевые слова для использования асинхронности в коде?
47. Что означают ключевые слова async / await?
Базы данных
48. Разница между реляционными и нереляционными базами, плюсы и минусы использования обоих вариантов.
49. Что такое индексы в RDBMS?
50. Какие типы JOIN существуют в SQL?
Тестирование
51. Для чего нужны unit-тесты?
52. Какие преимущества и недостатки использования unit-тестов?
53. Из каких трех логических блоков состоит unit-тест?
Вопросы для Middle
Общее
54. Вы набираете google.com в браузере. Расскажите как можно подробнее, что происходит в это время на HTTP-уровне?
55. Как работает HTTPS?
56. Как вы понимаете SOLID?
57. Какие протоколы сериализации вы знаете и где они применяются?
58. Что такое в вашем понимании чистая функция? Какие у нее преимущества?
59. Что такое dependency injection и зачем оно нужно?
60. Что такое cohesion и coupling (связанность и связность)?
61. Что такое IaaS, PaaS, SaaS и каковы различия между ними?
62. Какие способы отладки программы вы используете?
63. Какие знаете паттерны? Объясните суть перечисленных.
64. В чем суть паттерна Singleton? Почему его еще называют антипаттерном?
65. Для чего нужен паттерн Strategy?
66. Какие ключевые различия между распределенными системами и монолитными?
67. Какие паттерны проектирования распределенных систем вы знаете?
68. Какие есть принципы работы Message bus? Почему могут возникать дубликаты в очередях?
69. Какие принципы построения идемпотентных сервисов знаете?
70. Расскажите, как работают асинхронные методы? Чем асинхронность отличается от параллелизма?
Платформа .NET
71. Какие исключения нельзя остановить в блоке catch?
72. Какая разница между .NET Standard Class Library и .NET Core Class Library?
73. Объясните разницу между отложенным и немедленным исполнением в LINQ. Приведите примеры.
74. Для чего нужен метод ConfigureServices в Startup.cs?
75. Какая разница между services.AddTransient и services.AddScope в ASP.NET Core?
76. Что такое Kestrel?
77. Опишите ASP.NET MVC request pipeline.
78. Как в ASP.NET WebAPI настроить кэширование ответов на HTTP-запросы?
Управление памятью
79. Что такое куча и стек? Различия, принцип работы.
80. Как работает сборщик мусора?
81. Зачем нам зарезервированное слово using в C#, если в .NET есть автоматическое управление памятью? Как с этим связан disposable-паттерн и зачем такой сложный паттерн для managed и unmanaged ресурсов?
82. Какие особенности работы с Large Object Heap?
Типы данных, коллекции и структуры данных
83. Когда генерируется дженерик-класс конкретного типа - при выполнении программы или во время компиляции?
84. Что такое рефлексия?
85. Расскажите о коллекции LinkedList <T>. Чем она отличается от других коллекций?
86. Что такое индексатор?
87. Что такое immutable object? Какие преимущества дает использование immutable object? Предложите способ реализации его в .NET.
88. Когда использовать StringBuilder, а когда string? Как работает StringBuilder?
89. Что такое балансирование деревьев?
90. Что такое Key-value структуры?
100. Что такое хэш-функция и зачем нужны хэш-таблицы?
101. Какими свойствами должна обладать идеальная хеш-функция?
102. Что такое коллизии и как с ними бороться?
103. В чем заключается сложность CRUD-операций в Dictionary <K, V> в .NET?
104. Где хранятся массивы? Массивы примитивных типов?
105. В чем отличие между массивом (T [ ]) и списком (List <T>)?
106. В чем разница между IList <T> и IEnumerable <T>?
107. Зачем нужны Enumerable, Observable, AsyncEnumerable и какие модели получения данных они реализуют?
108. В чем разница между IEnumerable и IQueryable?
109. Что такое enum flags?
Базы данных
110. Расскажите о нормальных формах в СУБД.
111. Что такое индекс в БД?
112. Когда следует использовать индексы? Преимущества и недостатки.
113. Какие типы индексов существуют? Чем они отличаются?
114. Что такое ACID?
115. Какие вы знаете уровни изоляции транзакций?
116. Что такое план выполнения запроса (execution plan) в MS SQL?
117. Проблема: запрос долго выполняется. Какие есть методы ее диагностики и решения?
118. Как ORM (Entity Framework или Entity Framework Core) транслируют C# код в язык запросов базы данных? Что для этого используется?
Параллелизм
119. Для чего использовать Task.ConfigureAwait?
120. Например, есть веб-сервер, который по HTTP-запросу делает выборку из базы данных. Всего на сервере 16 тредов (threads). Каждый HTTP-request выполняет запрос в базу и ожидает результатов, в этом случае тред блокируется. Можно ли оптимизировать эту работу средствами .NET?
121. Зачем нужен ThreadPool? Опишите механику работы: как поток выделяется и возвращается обратно в ThreadPool.
Вопросы для Senior
Общее
122. Какие ещё практики, кроме ООП, использовали (AOP, FP и т. д.)?
123. Назовите три самые сложные проблемы, которые вам приходилось решать. Как вы это сделали, как пришли к этому решению?
124. Что такое слабосвязанный код? Чем он лучше сильносвязанного кода? Как бы вы достигали более слабой связности кода?
125. Использование статических классов повышает или понижает связность кода?
126. Как можно измерить performance кода? Влияет ли факт замеров на производительность?
127. Для чего используются и как работают multi-stage билды в Docker?
128. Как понять, что какая-то часть кода утилизирует много памяти или долго выполняется? Что может быть ботлнеком в разных случаях? Какие есть способы уменьшения памяти и трафика памяти?
129. Как бы вы реализовали cross-cutting concern (например, логирование, валидация, транзакции)?
130. Расскажите о Rest Maturity Model.
131. Что такое CPU и IO-bound задачи?
132. Что такое маршалинг?
133. Как работает async / await (подробно)? Почему нельзя использовать async void методы?
Платформа .NET
134. Как работает lock? Можно ли использовать структуры внутри выражения lock?
135. Что такое Expression Tree?
136. Как работает сборщик мусора (подробно)? Почему в GC три поколения, а не, скажем, пять, десять или два?
137. Как бы вы организовали трассировки Web API сервисов?
138. Как в .NET Core можно настроить хранение секретов на компьютерах разработчиков и на рабочих средах?
139. Как бы вы организовали процесс CI/CD .NET Core сервисов для их деплоймента в облачную инфраструктуру?
140. Как включить CORS в AspNetCore?
Типы данных, коллекции и структуры данных
141. Как реализованы дженерики?
142. Как создать собственный immutable-тип?
143. Как работает IEnumerable <T> (подробно)?
144. Какой алгоритм использует коллекция STACK?
145. Какие структуры данных вы реализовывали сами для платформы .NET? Расскажите, чем они отличались от стандартных реализаций.
146. Чем отличается интерфейс от абстрактного класса? В каких случаях вы использовали бы и то, и другое?
147. Почему в структуре нет конструктора по умолчанию?
Базы данных
148. Как БД сохраняет данные?
149. Какие типы БД вы знаете?
150. Как и когда БД лучше использовать?
151. Что такое денормализации?
152. Когда и какие уровни изоляции транзакций можно использовать?
153. Как в популярных СУБД реализованы принципы ACID (SQL Server, PostgreSQL и т. д.)?
154. Приходилось ли вам оптимизировать запрос в БД? Если да, то как?
155. Опишите, какие вы знаете потенциальные проблемы, связанные с параллельными запросами к БД.
156. Какую базу данных вы бы использовали для реализации distributed lock механизма? Расскажите детали реализации.
Микросервисы
157. Какую проблему решают микросервисы?
158. Какие есть способы коммуникации микросервисов?
159. Расскажите варианты реализации распределенных транзакций в микросервисах.
160. Что такое circuit breaker?
161. Каким образом вы будете налаживать систему, состоящую из множества микросервисов, если нужно отследить полный путь обработки запроса?
162. Что такое брокеры сообщений? Что такое at-least-once, at-most-once семантика? Есть ли какие-то брокеры, которые гарантируют exactly-once семантику?
163. Как должен работать код клиента брокера в зависимости от выбранной семантики?
164. Какие инструменты для работы с очередями вам известны (как в .NET, так и отдельные продукты), какой инструмент/продукт вы бы выбрали и почему?
MS Azure
165. Какие виды сервисов бывают в Service Fabric?
166. Какие особенности и ограничения Azure Table Storage?
167. Как бороться с проблемой холодного старта в Azure Functions?
168. В чем отличие очередей и топиков в Azure Service Bus?
Практические задания
Junior only
Опишите, как бы вы реализовали калькулятор. Если понадобится добавить поддержку для римской системы счисления, что будете делать?
Напишите программу, чтобы проверить, является ли число простым или нет.
Найдите наименьший элемент в массиве.
Упорядочите структуры папок в файловой системе.
Напишите программу, которая симулирует fizz-buzz.
Расскажите, что делает этот код, и предложите, как его улучшить.
а)
public bool IsArrayEmpty(string[] array)
{
if (array.Length > 0)
return false;
else
return true;
}
б)
protected string GetClass(object url)
{
string result = string.Empty;
if (SiteMap.CurrentNode != null && SiteMap.CurrentNode.Url == url.ToString())
result = "class=\"active\"";
return result;
}
Middle
Напишите программу для тиражирования последовательности Фибоначчи для заданного числа.
Спроектируйте базу данных врачей и пациентов в SQL. Создайте stored procedure или запрос в SQL, который вернет врачей, имеющих больше, чем N пациентов.
Проверьте коллекцию на наличие дубликатов.
Спроектируйте thread-safe класс Singleton в C#.
Надо записать некую сущность в базу данных и отправить событие в брокер сообщений. Как это сделать с минимальным риском потери данных?
Имеются три сервиса. Выполняется HTTP-запрос на первый. Первый должен записать данные во второй, а второй - в третий. Как гарантировать, что данные при такой сложной коммуникации не потеряются? Как предотвратить записи дубликатов данных?
Будет ли работать этот код и почему?
a)
SomeClass myClass = null;
myClass.SomeMethod();
б)
var table = GetTable();
table.Draw();
…
private SomeClass GetTable()
{
using(var table = new SomeClass())
{
table.ID = "www";
table.Width = "95%";
table.Controls.Add(tr);
return table;
}
}
Расскажите, что делает этот код, и предложите, как его улучшить:
а)
Shape shape = GetNextShape();
if(shape is Circle)
Console.WriteLine(((Circle)shape).Radius);
б)
public int Quantity
{
get
{
try
{
return int.Parse(TxtQuantity.Text);
}
catch (Exception)
{
return 0;
}
}
}
Что не так с кодом?
try
{
SomeMethod();
}
catch(Exception e)
{
Log(e.Message);
throw e;
}
Senior
Как бы вы спроектировали FTP-сервер? Web-сервер? Хостинг картинок с разделением прав доступа? Систему, состоящую из front- и back-офисов? Создайте клон любой популярной социальной сети (Instagram, Facebook и т.д.)
Допустим, есть сущность сделки (Bid). И в сделке есть список партнеров, с которыми эта сделка заключается. Сами сделки хранятся в сервисе сделок (bidding service), а партнеры - в своем сервисе (partners service). И когда показывается список сделок, на начальном скрине нужно отразить количество партнеров. Как бы вы спроектировали хранение и изображение этого счетчика?
Задача на знание LINQ: с исходной коллекции данных необходимо получить новую коллекцию по определенным требованиям (например, отсортированную, без дубликатов и т. д.).
Напишите программу, которая проверяет, является ли поле судоку 9×9 правильно заполненным.
Реализуйте свой Select, Where, SelectMany со всеми характеристиками LINQ (отложенное исполнение, одномоментная (eager) проверка входных данных).
С помощью LINQ напишите метод, который вернет такие элементы коллекции, которые делятся на N без остатка, отсортированные от наибольшего значения к наименьшему. Сигнатура метода: List <int> Filter (IEnumerable <int> collection, int n) ;.
Напишите функцию, которая вернет сумму всех четных элементов массива, который передается в функцию (JavaScript).
Выражаем благодарность за подготовку статьи Алексею Краевому, Максиму Шнуренку, Андрею Ткаченку, Владу Медведовскому, Сергею Селецкому, Андрею Губскому, Сергею Мащуренку, Александру Скриннику, Андрею Соболю, Богдану Нановскому, Святославу Аксёнчикову, Ивану Корнелюку, Михаилу Пивоварову, Алексею Дерке, Евгению Гуреву, Михаилу Меркулову, Юрию Воронину.
Як вибрати курси програмування?
Автор: Армен Маїлян
Что важно и с чего начать
Критерии выбора курсов программирования
Тренер
Место проведения занятий
График проведения занятий
Размеры учебных групп
Стоимость часа обучения/полного курса
Общение с тренером
Наличие и контроль выполнения домашних заданий
Актуальность учебной программы
История учебного центра
Вывод
Что важно и с чего начать
Если у вас есть высшее образование в области программной инженерии или компьютерных наук, то скорее всего вы знаете, какие курсы выбирать. Но если раньше вы не были связаны с IT, то выбор курсов программирования будет затруднителен.
До наступления эпохи интернета главным источником знаний были ВУЗы и книги. Желающим научиться программировать приходилось тратить 5 лет на изучение целого комплекса наук, получать профессию «прикладного математика» либо другую, достаточно сложную профессию.
Самообучение по книгам требовало высокой самоорганизации, умения выбрать правильные книги. Если учесть, что, что языки программирования и новейшие технологии разрабатываются преимущественно англоговорящими специалистами, доступ к самым новым знаниям сдерживался сроками, необходимыми для качественного перевода и издания книги на русском языке.
Зачастую книги являлись некими справочниками – источниками информации для улучшения уже существующих навыков и знаний. Редкие книги позволяли полноценно провести человека, изучающего программирование, с нулевого уровня знаний, до уровня полноценного разработчика, который уже может устроится на работу по этой профессии.
Популярной альтернативой для тех, кто хочет стать программистом, стали курсы программирования. Хорошие курсы помогают сформировать базовые знания и навыки по выбранной специальности, сориентировать в мире технологий и задать дальнейший вектор развития для начинающего специалиста.
Критерии выбора курсов программирования
Любой сложный выбор можно попытаться разбить на элементы, выделить важные части и сделать акцент именно на них. Так поступили и мы. Давайте рассмотрим вопрос выбора курсов программирования по пунктам.
Тренер
Как все мы понимаем, любые оффлайн курсы примерно на 90% времени состоят из общения студента с тренером. Именно такое общение является решающим для получения качественных навыков и знаний. Тренер читает лекцию, демонстрирует учебный материал, отвечает на возникшие у студентов вопросы, задает и проверяет домашние и классные задания. Для качественного учебного процесса этот тренер должен соответствовать неким критериям.
Как нам правильно выбрать тренера по программированию?
В первую очередь обратите внимание на уровень квалификации тренера. Очень важно, чтобы человек, обучающий других, сам был хорошо подкован и имел нужные знания. Если смотреть на курсы программирования, то таким критерием объективно может быть наличие общепризнанных сертификатов, профильное образование и, конечно же, опыт работы в реальных компаниях на реальных проектах, практический опыт решения проблемами с использованием новейших технологий. Обычно и сертификаты, и участие тренера в неких проектах, и образование можно вполне легко проверить.
Вторым важным моментом в вопросе выбора тренера будет форма подачи материала, умение внятно и доступно рассказать, показать - любым способом донести учебный материал. Такие навыки тренера сложно проверить дистанционно.
Именно поэтому многие компании, предоставляющие услуги обучения, дают возможность пройти бесплатно пробное занятие. Зачастую такого занятия будет достаточно, чтобы оценить подходит ли вам умение подать материал данного учителя или нет.
Место проведения занятий
Место получения вами знаний является важным фактором при выборе курсов программирования. Сам по себе этот пункт мы можем разделить на две части:
территориальное расположение (локация) учебного центра;
характеристика помещений и наполнения учебного центра.
Если говорить о локации, то все мы понимаем, насколько удобнее приезжать в учебный центр, расположенный либо в центре города, либо возле серьезной транспортной развязки. Добираться после работы или учебы в другой конец города, тратя полтора-два часа только на проезд, воспринимается как вполне серьезное неудобство. Если учесть, что курсы будут длиться несколько месяцев, то вопрос расположения учебного центра становится вовсе не второстепенным.
Другая часть пункта «Место» — это непосредственно наполнение учебного центра. Приходя учиться программировать, вы должны иметь возможность не только слышать тренера, но и видеть его действия с кодом и иметь возможность самим писать код, выполняя полученные задания.
По этой причине важным будет вопрос оснащенности учебных аудиторий компьютерами с нужным установленным ПО, наличие нормального доступа в Интернет, иногда и возможность наблюдать за действиями тренера на большом экране телевизора или проектора, оснащенность учебного центра иными необходимым оборудованием.
Если говорить о самих помещениях – большие аудитории для больших групп, маленькие аудитории - для малых. В зависимости от того, насколько индивидуальными будут ваши занятия, важно чтобы в учебном центре были подходящие помещения. Не стоит забывать и о комфорте. Занятия летом в помещении без кондиционера и вентиляции, а зимой без отопления, сделают процесс учебы мучительным и сложным.
График проведения занятий
Работа или учеба - так или иначе у всех нас присутствует каждодневная загрузка. Для того, чтобы изучать что-то новое, чтобы получить нужные навыки или целую новую профессию – нам приходится изыскивать время. Поэтому важно чтобы наши возможности по наличию свободного времени мы могли реализовать в учебном центре. Иногда важно иметь возможность посещать курсы в выходной день – часто бывает так, что это единственная возможность выделить время для учебы. Таким образом наличие групп обучения в удобное для вас время, это важный фактор при выборе учебного центра с нужными курсами программирования.
Размеры учебных групп
Все мы еще со школы помним, как удобно в больших классах думать какие-то свои отвлеченные мысли, заниматься какими-то своими делами, пока преподаватель задает вопросы вашему однокласснику. Но когда мы осознанно выбираем необходимость учиться, мы понимаем, что чем больше внимания мы уделим предмету учебы и чем больше преподаватель сможет уделить время нам - тем лучше для усвоения нами материала.
Фактически лучше всего мы могли бы учиться, получая уроки индивидуально. Однако, в данном случае все упирается в стоимость учебы.
Представьте ситуацию – имеется квалифицированный разработчик с полноценным опытом работы в различных программных проектах. Этот разработчик сдал в свое время необходимые экзамены и получил сертификат Microsoft/Oracle/Другая ТОП компания. Кроме того, этот программист владеет далеко не простым навыком – он умеет учить! Он может нормально передать знания, которыми владеет сам, объяснить, подсказать и т.д. Сколько стоит час времени такого разработчика? Как вы понимаете – не мало! А ведь есть еще дополнительные расходы – оплата подходящего места, доступ к интернету и др.
Получая индивидуальные уроки, вы будете оплачивать час такого разработчика самостоятельно. Что вовсе не является таким уж рациональным подходом.
Дело в том, что если образовательный процесс построен правильно, если правильно подобрана учебная информация, хорошо составлены лекции, студенту представлены нужные учебные материалы – студент ничего не потеряет, если будет слушать такую лекцию не индивидуально, а в компании с другими студентами. Далеко не всегда индивидуальные занятия дадут вам заметную разницу в усвоении материала. Скорее вам будет комфортнее получить знания в компании с другими студентами.
Конечно же это должна быть не группа в 30+ человек. Важно выдержать баланс. Практика показывает, что группы в 8-10 человек достаточно сбалансированы между вопросом цены обучения и показателем качества получаемых знаний.
Стоимость часа обучения/полного курса
Мы понимаем, что время хорошего тренера будет стоить определенную сумму. Также мы понимаем, что аренда удобного помещения, с хорошим расположением и наличием нужного оборудования, тоже не будет бесплатной.
Снизить цену на обучение нам поможет занятие в группе, когда общая стоимость курса будет разделена на всех студентов.
Для себя вам нужно будет найти баланс между ценой и качеством услуг.
Общение с тренером
Посещая курсы, вы конечно же общаетесь с тренером. Как мы писали выше – около 90% времени, когда вы физически находитесь в учебном центре, проходит у вас в общении с вашим тренером. Вы слушаете лекцию, решаете заданные практические задания, задаете вопросы, получаете ответы.
Важным моментом во время обучения является возможность получения разъяснений за рамками стандартного учебного процесса.
Ряд студентов во время учебы начинает выполнять свой учебный проект и нуждается в консультации и разъяснении вопросов вне учебной программы. Кому-то необходимо получить дополнительные задания и проконтролировать их выполнение. Кто-то недостаточно усидчиво работал во время предыдущих уроков и теперь нуждается в повторении и разъяснении уже пройденного ранее материала.
Все эти вопросы могут быть разрешены если формат обучения в выбранном вами учебном центре позволяет получить такие консультации от тренера. Конечно же не бесплатно.
Наличие и контроль выполнения домашних заданий
Многие еще со школы не любят домашние задания. Выполнение домашних заданий и подготовка различных контрольных и курсовых проектов в вузе также мало радости приносят студентам. Но нельзя не заметить, что правильно организованный учебный процесс требует подкрепления полученных на уроке знаний.
Именно механизмом подкрепления знаний и закрепления учебного материала выступает выполнение домашнего задания на курсах программирования.
Каждому студенту необходимо не только услышать и увидеть различные синтаксические конструкции изучаемого языка программирования/фреймворка/библиотеки. Для получения устойчивого навыка студенту необходимо постоянно использовать эти конструкции в различных вариациях.
Кроме того, при использовании таких конструкций студентом, обязательно будут появляться ошибки. Именно выполнение правильно составленных заданий, а также поиск ошибок в своих решениях этих заданий и является той важной составляющей учебного процесса, которая позволит студенту закрепить полученные на уроках навыки.
Стоит заметить, что важным будет не только сам факто того, что тренер эти задание задаёт, но и то, как он контролирует выполнение этих задания, есть ли у студента возможность получать разъяснения по домашнему заданию. Входит ли такая возможность в цену обучения или она осуществляется за отдельную доплату.
Актуальность учебной программы
IT сфера является одной из самых динамичных в мире. Постоянно происходит появление новых языков программирования, новых программных библиотек, новых технологических решений. Постоянно выходят дополнения и изменения уже существующих инструментов разработки. Постоянно меняются требования к соискателям на рынке труда.
Студенту, желающему получить современную IT профессию, а также действующему специалисту, желающему улучшить имеющиеся навыки и изучить новые инструменты, важно получать знания о технологиях, актуальных именно на сегодняшний день, а также перспективных на день завтрашний.
По этой причине, выбирая подходящие для себя курсы программирования, желательно обращать внимание на то, как часто пересматривается учебная программа курсов. Насколько детально она проработана. Важно понимать, насколько данная программа близка вашему текущему уровню знаний.
Будете вы изучать технологии создания сайтов в сети Интернет или писать приложения для персональных компьютеров, важно - сможете ли вы начать изучать данный курс, будет ли вам понятен материал этого курса или вам лучше начинать с более базовых уроков. Есть ли такие базовые уроки в данном учебном центре?
Также, выбирая курсы, важно понимать, как часто выходят дополнения к базовым материалам и насколько текущий курс учитывает последние изменения в современных инструментах разработки, насколько глубоко или широко представлены курсы по важным сопутствующим технологиям.
Такое понимание, к сожалению, достаточно сложно для новичков в мире разработки программного обеспечения. Если бы студент имел понимание обо всех современных тенденциях и изменениях – возможно ему и не нужны были бы курсы.
В таком случае стоит положиться на мнение более опытных коллег и поискать отзывы и рекомендации в сети Интернет.
И тут мы подходим к последнему пункту нашего перечня из ответов на вопрос «как выбрать курсы программирования?».
История учебного центра
Мода на «войти в айти» все еще актуальна в постсоветских странах. В виду этого рынок услуг обучения не стоит на месте.
Постоянно открываются новые учебные центры, постоянно закрываются те, которые не выдержали конкуренции. Не выдержали по разным причинам: не смогли предоставить нужное качество обучения, не смогли обеспечить рыночную цену за обучение, обучали не актуальным технологиям по старым материалам, а также по другим причинам.
В таких условиях срок жизни учебного центра является важным показателем стабильности обучающего процесса и качества учебных материалов. Достаточная длительность существования учебного центра будет показателем как степени соответствия курсов актуальным технологиям в разработке ПО, так и своевременного внедрения новых технологий самого обучения.
Хорошим подспорьем при выборе учебного центра, в таком случае, будет наличие большого числа отзывов на соответствующих информационных площадках. Однако следует учитывать, что как положительные отзывы, так и отрицательные, могут оказаться маркетинговым ходом обучающих компаний. Положительные, могут оказаться выложены самими учебными центрами, а отрицательные – от недобросовестных конкурентов. Такая ситуация, к сожалению, далеко не нова на постсоветском рынке образования.
Помочь разобраться в отзывах может понимание того, на какой площадке расположен отзыв, насколько анонимен автор отзыва и как реагирует администрация учебного центра, особенно если этот отзыв - негативен.
Вывод
Конечно, лучшим вариантом курсов программирования было бы индивидуальное обучение у высококвалифицированного и опытного специалиста по последним технологиям в удобное для вас время, с проверкой выполненных домашних заданий и возможностью получения бесплатных консультаций в любое время. И хотелось бы бесплатно!
К сожалению, объективная реальность заставляет признать – такой комбинации не бывает. Однако и на сегодняшнем рынке образования можно найти курсы программирования с хорошим качеством обучения, подходящей ценой и устраивающими вас другими критериями выбора, упомянутыми в нашей статье.
Если вы для себя решили получить профессию разработчика или, уже обладая этой профессией, решили изучить некую новую технологию, мы рекомендуем вам потратить немного времени и сопоставить предложенные в этой статье пункты с различными курсами, которые вы можете найти в сети. Мы уверены – с правильным подходом к выбору вы не разочаруетесь.
Если вы уже знакомы с видео курсами ITVDN, вам могут понравиться очные и онлайн курсы нашего постоянного партнера – учебного центра CyberBionic Systematics. Более трети видео курсов, представленных в нашем каталоге, созданы тренерами этого учебного центра.
Введення в розробку програм під iOS. Частина 6
Автор: Volodymyr Bozhek
Здравствуйте, дорогие читатели.
В этом уроке мы:
Установим менеджер установки пакетов “Brew”;
Научимся пользоваться базой данных “Mongo DB” и изучим библиотеку “Mongoose”;
Установим и сконфигурируем базу данных “Mongo DB”;
Научимся запускать демон (службу) “Mongod” и подключаться к ней;
Научимся просматривать документы базы данных “Mongo DB” с помощью приложения “Robomongo”;
Создадим REST сервисы для товаров и пользователей с использованием “NodeJS”;
Научимся тестировать REST сервисы с помощью расширения “Chrome” браузера “Postman”;
Итак, вы скорее всего задались вопросом, почему же я выбрал именно такие технологии для написания REST сервисов. Почему не выбрал то, что уже давно у всех на слуху, такие технологии как Web API + Entity Framework. Ответ прост, я выбрал ровно те технологии, которые явно подходят платформе, на которой мы ведем разработку macOS.
Да, можно было установить Xamarin Community Edition, в нем создать ASP.NET проект и сделать сервисы на Mono.NET.
Еще был вариант сделать сервисы на виндовс платформе, развернутой на виртуальной машине в веб сервере IIS с сетевым адаптером мост. Это для того, чтобы веб сайт и сервисы были доступны на хостовой машине macOS в браузере.
Возможно, в одном из моих будущих видео курсов по разработке iOS приложений я буду использовать именно платформу Windows и Visual Studio для разработки REST сервисов.
Но в этом уроке мы будем делать сервисы на NodeJS, и вы сами убедитесь в том, насколько это просто и как мало кода надо для этого написать :)
В качестве среды разработки я буду использовать WebStorm, он довольно хорошо себя оправдал как один из продуктов JetBrains.
В этом уроке мы не будем работать с приложением “Warehouse” под iOS, поскольку мы должны приступить к изучению библиотеки “Alamofire”. А для нее необходимы REST сервисы, которых у нас сейчас нет. Показывать работу на всяких httpbin, parse и других сервисах тестирования я не буду, этого полно в сети интернет, вы и сами будете видеть это в поиске нужной вам информации.
“Alamofire” - это асинхронная сетевая библиотека для работы с REST сервисами. В большинстве туториалов не рассказывается, как сделать REST сервисы и клиент к ним на разных платформах.
Мне хотелось бы нарушить этот стереотип и предоставить вам учебные примеры по созданию полнофункционального REST сервиса и полнофункционального iOS клиента в составе приложения “Warehouse” к нему. Проще говоря, дать готовое решение для быстрого понимания, что и как делается.
Это то, чего обычно не хватает новичкам и то, чего обычно нигде не выкладывают. Приходится самостоятельно разбираться и искать нужную информацию.
Откройте “WebStorm”. Выберите в меню “File -> New Project”. Выделите тип проекта “Node.js Express App”.
В поле “Location” укажите название проекта “Warehouse” и путь, по которому вы будете сохранять данный проект.
В поле “Template” выберите “EJS”. Нажмите кнопку “Create”.
Вы увидите диалоговое окно с предложением, откуда взять библиотеку “NodeJS”.
Оставьте пункт по умолчанию “Download from the Internet” и нажмите кнопку “Configure”.
Будет загружен из сети интернет “NodeJS”, “Express” и другие библиотеки.
Панель навигации будет выглядеть у вас так:
Рассмотрим, что создал нам данный шаблон.
В папке “bin” хранится файл “www”, в нем находится настройка сервера “NodeJS”, с этого файла происходит запуск нашего сервера.
В папке “node_modules” находятся библиотеки, которые были автоматически загружены шаблоном проекта NodeJS.
В папке “public” находятся ресурсы, используемые нашим сервером (картинки, CSS стили, скрипты).
В папке “routes” находятся скрипты, которые должны содержать маршрутизацию относительно текущего URL, введенного пользователем.
Например, файл с именем “users.js” вызывется, когда пользователь введет в браузере URL: “http://localhost:{port}/users”.
Файл “index.js” вызывется, когда пользователь введет URL: “http://localhost:{port}”.
Если сравнивать с паттерном MVC (Model View Controller), то в папке “routes” находятся “контроллеры”.
В папке “views” находятся представления. Расширение “*.ejs” говорит о том, что содержимое данного файла будет транслироваться через библиотеку “Express JS”.
Данная библиотека создана в помощь, чтобы можно было проще создавать маршрутизацию в NodeJS и проще создавать пользовательский интерфейс.
Внутри этих файлов “*.ejs” содержится обычная “HTML” разметка. Файл маршрутизации и файл представления должны называться одинаково, если они будут связаны.
В текущем проекте связаны только “index.js” и “index.ejs”. Маршрут “users.js” не имеет явного представления, для него преставление генерируется средствами “NodeJS”.
Наша задача создать только REST сервисы, поэтому работу с “EJS” мы не будем рассматривать в данном уроке.
Рассмотрим содержимое файла “www” в папке “bin”.
На 7 строке через библиотеку “Require JS” мы подключаем содержимое модуля “app.js” в текущий исполняемый модуль “www” и запускаем это содержимое на исполнение.
В файле “app.js” содержатся настройки для работы нашего сервера.
На 8 строке мы задаем режим отладки для нашего сервера.
На 9 строке мы подключаем модуль “http” для работы с HTTP запросами.
На 15 строке задается порт “3000”, на этом порту сервер будет принимать входящие запросы к нему.
На 16 строке устанавливает этот порт для свойства “port” сервера.
На 22 строке создается сервер на основе настроек экземпляра “app”.
На 28 строке сервер запускается и начинает прослушивание на порту “3000”.
На 29 строке привязывается функция “onError”, вызываемый при возникновении ошибок.
На 30 строке привязывается функция “onListening”, вызываемая при начале прослушивания запросов сервером.
Остальной код в данном модуле не представляет для нас интереса.
Чтобы запустить приложение, выполните в меню “Run -> Run bin/www”, сервер запустится.
Затем откройте браузер и введите URL: “http://localhost:3000”, вы увидите приветствие.
Чтобы остановить сервер, выполните в меню “Run -> Stop”.
Теперь необходимо установить базу данных “Mongo DB”. Подробнее об установке вы можете прочитать тут. Проще всего устанавливать базу данных через менеджер пакетов “Brew”.
Давайте установим его. Перейдите в браузере по адресу, на сайте будет предложено скопировать строку “/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)” в терминал.
Откройте терминал и скопируйте в него строку установки “Brew”. После установки вы увидите следующее:
Теперь необходимо установить базу данных “Mongo DB”. Установка базы данных через “Brew” проще тем, что “Brew” сам заполняет нужные переменные среды окружения и вам не надо этого делать вручную в случае с установкой базы данных вручную.
Перейдите в “WebStorm”, откройте внутри него терминал. И введите команду “brew install mongodb && mongod”, данная команда скачает и установит “Mongo DB”, а также выполнит настройки по умолчанию для демона “mongod”.
Выполните в терминале команду “sudo mkdir -p /data/db”. Введите свой пароль и нажмите кнопку “Enter”. Данная команда создаст папку “data” и в ней папку “db”, эта папка используется по умолчанию для добавления базы данных “Mongo DB”.
Команда “sudo” используется, чтобы выполнить остальную часть команды от имени администратора.
Теперь необходимо установить в наш проект библиотеку “Mongoose” для осуществления возможности работы с базой данных их кода приложения сервера .
Выполните в терминале команду “npm install mongoose”. Документацию по этой библиотеке можно почитать тут:
Данную команду мы выполняли через встроенный в NodeJS менеджер пакетов, так как устанавливаем данную библиотеку исключительно для нашего проекта, а не глобально.
Откройте терминал, введите команду “mongod”.
Данная команда запускает сервер базы данных “Mongo DB”, который принимает входящие подключения на порту “27017”. На картинке выше вы можете в этом убедиться “waiting for connections on port 27017”.
Демон запустили, теперь перейдите в “WebStorm”, в терминале внутри введите команду “mongod –version”, вы увидите версию базы данных “Mongo DB”, которая была установлена на ваш компьютер.
Теперь необходим клиент с GUI для работы с базой данных. Откройте браузер, перейдите по адресу, нажмите кнопку “Download”, скачайте и установите приложение.
После установки приложения откройте его. Нажмите на иконку:
Откроется приложение “Robomongo”.
Нажмите кнопку “Connect to local or remote Mongo DB instance”. Откроется диалоговое окно подключения к базе данных.
Нажмите кнопку “Create”. Откройте диалоговое окно настройки подключения к базе данных.
Заполните настройки, как показано на картинке выше и только на вкладке “Connection”, нажмите кнопку “Save”. После этого в окне “Mongo DB connections” у вас появится добавленное подключение. Выделите его и нажмите кнопку “Connect”.
Нажмите правой кнопкой в панели навигации по “Warehouse” и в контекстном меню выберите пункт “Create Database”.
В поле “Database Name” введите “warehouse”, нажмите кнопку “Create”.
На картинке выше видно, что база данных “warehouse” была успешно добавлена.
Супер :)
Теперь надо добавить в проект модель и схему базы данных, они создаются с помощью библиотеки “Mongoose”.
Перейдите в “WebStorm”. В панели навигации, нажмите правой кнопкой по “Warehouse”, в контекстном меню выберите “New -> Directory”. Создайте папку с именем “models”.
Создайте в папке “models” модули “Product.js” и “User.js”. А в папке “routes” создайте модули “auth.js”, “products.js”.
Откройте модуль “Product.js”, заполните его, как показано на картинке ниже:
На 1 строке мы подключаем библиотеку “mongoose” и создаем ее экземпляр с именем “mongoose”.
На 2 строке мы правым операндом создаем схему таблицы товаров и присваиваем ее левому операнду с именем “ProductSchema”. Таблица товаров содержит поля:
скрытое поле “_id”, типа “String”, в этом поле содержится уникальный идентификатор строки.
скрытое поле “__v”, типа “Number”, в этом поле содержится версия API по умолчанию 0. Версия API нужна для того, чтобы можно было менять реализацию сервисов и иметь несколько версий для обратной совместимости.
поле “productImage”, типа “String”, тут будет храниться название картинок из нашего проекта под iOS, названия от “tool001” до “tool012”.
поле “productName”, типа “String”, тут будет храниться название товара.
поле “productDescription”, типа “String”, тут будет храниться описание товара.
На 7 строке мы правым операндом создаем экземпляр модели с именем “Product” и указываем создать экземпляр на основе схемы, описанной в экземпляре “ProductSchema”. Библиотека “mongoose” создает данный экземпляр и присваивает его левому операнду.
Свойство “exports” содержит экземпляры объектов, которые будут доступны из модуля при подключении его через библиотеку “Require JS”.
Откройте модуль “User.js”, заполните его, как показано на картинке ниже:
На 1 строке мы подключаем библиотеку “mongoose” и создаем ее экземпляр с именем “mongoose”.
На 2 строке мы правым операндом создаем схему таблицы товаров и присваиваем ее левому операнду с именем “UserSchema”. Таблица пользователей содержит поля:
скрытое поле “_id”, типа “String”, в этом поле содержится уникальный идентификатор строки.
скрытое поле “__v”, типа “Number”, в этом поле содержится версия API по умолчанию 0. Версия API нужна для того, чтобы можно было менять реализацию сервисов и иметь несколько версий для обратной совместимости.
поле “userName”, типа “String”, тут будет храниться логин пользователя.
поле “userPwd”, типа “String”, тут будет храниться пароль пользователя.
поле “userDesc”, типа “String”, тут будет храниться описание пользователя.
На 7 строке мы правым операндом создаем экземпляр модели с именем “User” и указываем создать экземпляр на основе схемы, описанной в экземпляре “UserSchema”. Затем присваем созданный экземпляр свойству “exports” данного модуля.
Откройте модуль “auth.js”, заполните его в соответствии с содержимым ниже:
На 1 строке подключаем модуль “express” и создаем его экземпляр с именем “express”.
На 2 строке создаем экземпляр маршрутизатора запросов с именем “router”.
На 3 строке подключаем модуль “User.js” и создаем его экземпляр с именем “User”.
На 4 строке строим маршрут типа “http://localhost:3000/auth/”.
На экземпляре “router” вызываем функцию “post”. Вызов данной функции говорит, что созданный с помощью него маршрут будет доступен только через HTTP глагол POST. Первым аргументом задаем маршрут, вторым аргументом задаем функцию обработчик, который будет вызван при переходе по данному маршруту с учетом заданного HTTP глагола. Т.е. когда пользователь отправит POST запрос по адресу.
Функция обработчик принимает три аргумента.
Первый аргумент “request” содержит экземпляр запроса по данному маршруту. Второй аргумент “response” содержит экземпляр ответа по запросу по данному маршруту. В этот экземпляр можно добавлять любой ответ, который вы хотите, чтобы пользователь получил.
Третий аргумент “next” содержит текущий итератор в стеке запросов по маршрутизации. Данный экземпляр необходим, чтобы была возможность перейти на следующую итерацию в стеке маршрутизации.
Сам по себе сервер NodeJS работает асинхронно и никогда никого и ничего не ждет, это нужно учитывать при работе с ним.
На 5 строке мы выводим сообщение на консоль.
На 6 строке мы на экземпляре “User” вызываем функцию “find”, в которую единственным аргументом передаем специальную функцию обработчик, в которой будет содержаться результат данной операции. Функция “find” получает все записи из таблицы “User” и присваивает их в JSON формате второму аргументу функции обработчика с именем “data”.
Для примера, в “data” может содержаться такой JSON:
“[{_id:'789787sdfsd78sdfsd7', __v:0, userName: “Test1”,...},{_id:'4444447sdfsd78sdfsd7', __v:0, userName: “Test2”,...}]”.
Где квадратные скобки - это массив, а в фигурных скобках - это экземпляр JSON.
В аргументе “err” содержится экземпляр ошибки, если произошла ошибка при выполнении данной функции “find”.
На 7 строке мы создаем экземпляр объекта с именем “res”, данный объект содержит свойство “Error”, которое инициализировано значением по умолчанию “Authorization Error”. Как вы уже поняли, это текст ошибки, который вернется на клиент в случае ошибки авторизации клиента.
На 8 строке мы проверяем, если при получении данных через функцию “find” произошла ошибка и аргумент “err” не содержит значение “undefined”, мы обращаемся к экземпляру “next” и говорим серверу выполнить следующую итерацию маршрутизации и вернуть ошибку на клиент. На клиент средствами NodeJS будет возвращена “Stack Trace” ошибки.
На 9 строке, если ошибки не было, мы выполняем цикл foreach по массиву JSON объектов “data”. В функцию “forEach” передается специальная функция, которая содержит три аргумента.
Первый аргумент “item” содержит текущий итерируемый экземпляр JSON.
Второй аргумент “i” содержит позицию итератора и имеет тип Number.
Третий аргумент “data” содержит исходный массив объектов JSON, по которым проводится итерация.
На 10 строке мы задаем условие поиска пользователя по его имени и паролю в списке всех пользователей полученной функцией “find”. В условии сравниваются имя и пароль, которые пришли в POST запросе на сервер, с именем и паролем, полученным от функции “find”.
На 12 строке, если пользователь был найден, мы присваиваем экземляру “res” JSON объект найденного пользователя.
На 15 строке мы обращаемся к экземпляру ответа “response”, вызываем на нем функцию “json”, которая принимает данные и возвращает ответ на клиент в JSON формате. В аргумент данного метода мы передаем или экземпляр найденного пользователя, или экземпляр ошибки, что пользователь не найден и авторизация не удалась.
На 18 строке мы экспортируем из модуля экземпляр маршрутизации “router”.
Откройте модуль “index.js”. Заполните его в соответствии с содержимым ниже:
На 1 строке подключаем модуль “express” и создаем его экземпляр с именем “express”.
На 2 строке создаем экземпляр маршрутизатора запросов с именем “router”.
На 3 строке строим маршрут типа “http://localhost:3000/index/”, доступный через HTTP глагол GET.
На 4 строке, на экземпляр ответа вызываем функцию “render”. Данная функция возьмет представление, указанное в первом аргументе функции и вернет его на сторону клиента. Вторым аргументом задается словарь, в который можно добавить свойства со значениями, к которым можно будет обращаться со стороны представления.
На 6 строке мы экспортируем из модуля экземпляр маршрутизации “router”.
Откройте модуль “index.ejs”. Заполните его в соответствии с содержимым ниже:
Библиотека Express JS позволяет нам обращаться к свойствам, которые мы указали во втором аргументе метода “render” путем добавления следующей конструкции “<%= Название_Свойства %>”. На этапе отрисовки страницы данные конструкции будут заменены на значение указанного свойства.
Откройте модуль “products.js”, заполните его в соответствии с содержимым ниже:
На 1 строке подключаем модуль “express” и создаем его экземпляр с именем “express”.
На 2 строке создаем экземпляр маршрутизатора запросов с именем “router”.
На 3 строке подключаем модуль “Product.js” и создаем его экземпляр с именем “Product”.
На 4 строке строим маршрут типа “http://localhost:3000/products/”. Данный маршрут будет доступен по HTTP глаголу GET.
На 5 строке мы вызываем на экземпляре “Product” функцию “find”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON массив со списком всех товаров.
На 10 строке строим маршрут типа “http://localhost:3000/products/”. Данный маршрут будет доступен по HTTP глаголу POST.
На 11 строке мы вызываем на экземпляре “Product” функцию “create”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON объект успешно созданного товара.
На 16 строке строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу GET.
На 17 строке мы вызываем на экземпляре “Product” функцию “findById”. Из названия функции ясно, что функция выполняет поиск по идентификатору товара. Для поиска используется скрытое поле “_id” в модели “Product”, объявленной в модуле “Product.js”. Внутри функции мы проверяем, были ли ошибки, и возвращаем в ответе JSON объект успешно найденного товара.
На 23 строке строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу PUT.
На 24 строке мы вызываем на экземпляре “Product” функцию “findByIdAndUpdate”. Из названия функции ясно, что выполняется поиск товара по его идентификатору “_id”, затем, в случае если найден, обновляются все его поля, кроме скрытых полей. В ответе возвращается JSON объект успешно обновленного товара.
На 30 строке строим маршрут типа “http://localhost:3000/products/{_id}” (например, “http://localhost:3000/products/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу DELETE.
На 31 строке мы вызываем на экземпляре “Product” функцию “findByIdAndRemove”. Из названия функции ясно, что выполняется поиск товара по его идентификатору “_id”, затем, в случае если найден, товар удаляется. В ответе возвращается удаленный JSON объект.
Откройте модуль “users.js”, заполните его в соответствии с содержимым ниже:
В модуле “users.js” я не вижу смысла расписывать подробно, что происходит, поскольку тут все то же самое, что и происходило в модуле “products.js”, рассмотренном выше. Разница только в том, что мы используем другую модель данных “User”. Опишу только доступные маршруты.
На 4 строке строим маршрут типа “http://localhost:3000/users/”. Данный маршрут будет доступен по HTTP глаголу GET.
На 10 строке строим маршрут типа “http://localhost:3000/users/”. Данный маршрут будет доступен по HTTP глаголу POST.
На 16 строке строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу GET.
На 23 строке строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу PUT.
На 30 строке строим маршрут типа “http://localhost:3000/users/{_id}” (например, “http://localhost:3000/users/789w66s2322kks4676s” ). Данный маршрут будет доступен по HTTP глаголу DELETE.
Откройте модуль “app.js”, обновите его в соответствии с содержимым ниже:
Конкретно в модуль “app.js” были внесены следующие изменения.
С 8 по 11 строку были подключены модули “index.js”, “users.js”, “products.js”, “auth.js”.
На 13 строке был подключен модуль “mongoose”.
На 14 строке мы задаем свойство “Promise” в значение “global.Promise”, чтобы начать обмениваться сообщениями с базой данной “Mongo DB”.
На 15 строке мы подключаемся к базе данных “Mongo DB” c названием “warehouse”, мы ее ранее создавали в приложении “Robomongo”. Источник данных “mongodb://localhost/warehouse” можно разбить так: “mongodb://” протокол взаимодействия (аналоги “http://”, “tcp://”, “file://”). “localhost” - это адрес машины, на которой хостится база данных. “warehouse” - имя базы данных.
На 16 строке метод “then” вызывается в случае успешного подключения к базе данных.
На 17 строке метод “catch” вызывается в случае сбоя подключения к базе данных.
С 33 по 36 строку мы задаем доступные глобальные маршруты, которые будет обрабатывать сервер. Надеюсь, все помнят разницу между сервером и сервисами, это не одно и то же.
Сервер - это просто приложение, которое внутри себя способно содержать различные ресурсы. Сервер может запускать внутри себя различные потоки для обработки данных.
Сервис - это функционал, который как раз запускается в отдельном потоке внутри сервера.
Сам по себе сервис без наличия приложения ,в котором его можно разместить, работать не будет. В случае с WebAPI сервером является Microsoft IIS или отдельно стоящее исполняемое приложение с функцией self хостинга.
Мы закончили написание сервера. Запустите демон “mongod” в терминале.
Запустите сервер. Вы увидите обновленное приветствие.
Я бы рекомендовал вам прослушать курс “Angular JS”, автор Дмитрий Охрименко. Это один из лучших курсов, которые я когда-либо слушал. После данного курса вы без особых проблем сможете сделать клиентскую часть для сервера на “Angular JS”.
Теперь давайте научимся тестировать наши REST сервисы. Тестировать сервисы можно и через Fiddler, но на macOS у него есть проблемы с отрисовкой и работой. Поэтому откройте браузер и перейдите по адресу.
“Postman” - это аналог “Fiddler”, доступный в качестве расширения для браузера “Google Chrome”.
После установки расширения “Postman” откройте его.
Выполним первый запрос к сервису “users” по HTTP глаголу GET.
Заполните поле адрес, выберите глагол “GET”, нажмите кнопку “Send”. Результат придет - пусто. Обратите внимание, что в терминале “WebStorm” тоже пишется логирование вызовов при обращения к сервисам.
Откройте приложение “Robomongo”, подключитесь к базе данных. Разверните папку “Collections” базы данных “warehouse”, выполните щелчок правой кнопкой мыши по таблице “users”, в контекстном меню выберите “View Documents”. В “Mongo DB” нет колонок, там есть всего одна колонка, в которой хранится JSON объект. Строки называются документами. Причем, в каждую строку одной таблицы можно класть абсолютно разный по структуре JSON объект. База данных позволяет такую операцию.
Видим, что данных нет, это не удивительно, ведь мы еще ничего не добавляли.
Давайте добавим. Перейдите в “Postman”, выберите глагол “POST”.
Выберите “Content-Type” - “x-www-form-urlencoded”. Внимание, через другие типы “form-data” “raw” работать не будет. Заданные свойства “userName”, “userPwd”, “userDesc” будут отправлены в запросе как JSON объект “{ userName: 'admin', userPwd: 'abc123!', userDesc: 'Administrator' }”. Нажмите кнопку “Send”. Придет ответ:
В ответе мы видим JSON объект модели “User”, созданной на сервере.
Перейдите в приложение “Robomongo”, выполните пункт контекстного меню “View Documents” на таблице “users”.
Отлично, у нас появился первый пользователь :)
Перейдите в “Postman”, выберите HTTP глагол “GET”. Нажмите кнопку “Send”. Вы увидите такой результат.
Обратите внимание на квадратные скобки, в ответе пришел массив JSON объектов, а не один объект.
Теперь давайте изменим пароль пользователю “admin”, на “12345678”. Нам понадобится идентификатор пользователя, выделите и скопируйте значение свойства “_id”.
Выберите HTTP глагол “PUT”. Обновите данные так, как показано ниже.
Обратите внимание на добавление идентификатора пользователя после основного маршрута “../users/”. Нажмите кнопку “Send”. В ответе получим старый JSON объект до внесения изменений.
Теперь для того, чтобы посмотреть измененный объект, воспользуемся сервисом , который возвращает пользователя по его идентификатору “_id”. Выберите HTTP глагол “GET”, обновите адрес, добавьте в конец адреса идентификатор пользователя. Нажмите кнопку “Send”. Получим такой результат.
Видим, что пароль пользователя был успешно обновлен и мы получили в ответе один JSON объект, а не массив пользователей.
Теперь попробуем удалить пользователя. Адрес оставьте тот же. Выберите HTTP глагол “DELETE” и нажмите кнопку “Send”.
Видим, что в ответе содержится JSON объект удаленного пользователя.
Теперь проверим, доступен ли пользователь после удаления. Адрес оставьте тот же. Выберите HTTP глагол “GET”, нажмите кнопку “Send”.
Видим, что пользователь действительно был удален. Также можете проверить это в приложении “Robomongo”.
Теперь приступим к тестированию сервиса “products”.
Получим список товаров.
Товаров нет. Добавим первый товар.
Обновим описание товара.
Получим обновленный товар по его идентификатору.
Удалим товар.
На этом мы завершаем урок.
К данной статье прикреплен архив с готовым сервером на NodeJS , в помощь учащимся :)
На следующем уроке мы:
выполним рефакторинг проекта “Warehouse” под “iOS”;
научимся пользоваться CollectionViewController;
научимся пользоваться библиотекой “Alamofire”, в конце урока будут доступны исходники полноценного рабочего приложения;
научимся сохранять объекты в настройки телефона и извлекать их;
реализуем функциональность для работы со всеми сервисами, созданными в данном уроке;
добавим представление регистрации;
добавим представление редактирования пользователей.
Материалы к статье тут.
Асинхронне програмування на JavaScript
Автор: Дмитро Охріменко
План:
1. Різниця між синхронним та асинхронним кодом
2. Багатозадачність процеси й потоки, у чому різниця
3. Особливості багатозадачності в JavaScript
4. Асинхронні операції на практиці HTTP-запит як найпоширеніший кейс
5. Підходи до написання асинхронного коду:
Promise
Async/Await
Observable
6. Практичні поради
Різниця між синхронним та асинхронним кодом
Для початку давайте визначимо ці два терміни:
Синхронний код - це код, який виконується послідовно, функція за функцією.
Асинхронний код - код, який може виконуватися паралельно: наступна функція запускається, не чекаючи завершення попередньої.
Щоб провести аналогію з реального життя, уявімо кухаря. Якщо кухар працює синхронно, то поки він не завершить приготування однієї страви, не переходить до наступної. Але це неефективно й призводить до втрати часу. Якщо ж кухар діє асинхронно, то поки м’ясо запікається в духовці, а на плиті закипає вода, він нарізає овочі. Тобто він один, але не стоїть без діла - виконує інші задачі, поки щось готується саме.
Уявімо, що кухар - це процесор. А запікання м’яса в духовці - це завантаження файлу з мережі. Кухар може просто стояти й дивитись, як м’ясо готується. А може нарізати овочі, перевіряти, чи не з’явились нові замовлення, або скролити стрічку в соцмережі. Так само і з програмами: поки мережева карта завантажує файл, процесор не мусить чекати - він може малювати інтерфейс, оновлювати прогрес-бар чи виконувати обчислення у фоні. Але для цього потрібно правильно написати код - так, щоб він міг працювати асинхронно.
Код який виконується синхронно
```js
console.log("Початок");
console.log("Дія");
сonsole.log("Кінець");
```
Результат:
Початок
Дія
Кінець
Код який виконується асинхронно. і
``js
console.log("Початок");
setTimeout(() => { // за допомогою setTimeout ми відкладаємо запуск коду на певний час
console.log("Дія через 2 секунди");
}, 2000);
сonsole.log("Кінець");
```
Результат:
Початок
Кінець
Дія через 2 секунди
Це не та багатозадачність, як у деяких інших мовах програмування. Тут не використовуються додаткові потоки, а все працює завдяки механізму подій. Але про це детальніше дал
Багатозадачність: процеси й потоки, у чому різниця
Багатозадачність в операційній системі - це можливість запускати та керувати кількома задачами одночасно. Наприклад, працювати в браузері, слухати музику, завантажувати файл і паралельно редагувати код у Visual Studio.
На практиці процесор дуже швидко перемикається між усіма цими задачами, створюючи ілюзію одночасного виконання. Якщо процесор багатоядерний - деякі задачі справді можуть виконуватись паралельно.
Багатозадачність тісно пов'язана з двома важливими поняттями - процесами та потоками.
Процес (process) - це окремий екземпляр програми у пам'яті, який має власні ресурси: виділену область оперативної пам'яті, дескриптори файлів, змінні оточення тощо.
Потік (thread) - це одиниця виконання всередині процесу. Потоки одного процесу працюють незалежно, але мають спільний доступ до пам'яті та ресурсів процесу.
Процеси дозволяють запускати різні програми одночасно - наприклад, Google Chrome, Visual Studio Code і т.д.
Потоки дають змогу виконувати кілька задач усередині однієї програми. Наприклад, у Visual Studio Code один потік відповідає за оновлення інтерфейсу, інший перевіряє помилки в коді, ще один формує підказки під час написання. Це, звісно, спрощений приклад - у реальності VS Code використовує ще й окремі процеси для розширень і мовних серверів.
Операційна система керує як процесами, так і потоками. Вона розподіляє процесорний час між ними, ставить у чергу, може призупиняти виконання або відновлювати його за потреби.
Давайте трохи адаптуємо наш приклад з кухарем із попереднього посту. Уявімо, що процес - це ресторан, а потік - це кухар. Ресторан має все необхідне для приготування їжі: кухонне приладдя, продукти, рецепти (це можна розглядати як пам’ять і доступ до інших ресурсів). Кухар читає рецепт і, використовуючи ресурси ресторану, готує страву - так само, як потік виконує інструкції нашої програми, використовуючи ресурси процесу. Якщо ресторан хоче готувати кілька страв одночасно, йому потрібно більше кухарів, які працюють паралельно на одній кухні. Аналогічно, якщо програма повинна виконувати кілька задач одночасно - завантажувати файли, обробляти введення, оновлювати інтерфейс - вона може використовувати кілька потоків.
Коли ми створюємо програму і хочемо зробити її зручною для користувача, а також ефективною з точки зору використання ресурсів, які виділяє операційна система на процес, ми іноді починаємо використовувати потоки та прийоми багатопотокового програмування. Це велика окрема тема, і ми її зараз чіпати не будемо. Одна з причин - у JavaScript немає прямого доступу до потоків.
Уточнення. Якщо ви хочете використовувати JavaScript і все ж таки працювати з потоками - у вас є Web Workers:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
А якщо JavaScript виконується не в браузері (наприклад, у Node.js), тоді можна використовувати модуль `worker_threads`. Але варто розуміти, що це не частина стандарту мови, а можливість середовища виконання.
Додаткові корисні ресурси по цій темі:
https://www.youtube.com/@CoreDumpped - канал з короткими відео про те як працює комп'ютер.
Modern Operating System by Andrew Tanenbaum - принцип побудови та роботи операційних систем (може бути викликом для новачка, але нормально як для технічної книжки)
Особливості багатозадачності в JavaScript
JavaScript працює в одному потоці - це означає, що в будь-який момент часу виконується лише один фрагмент коду. Увесь код, який ми пишемо, виконується у call stack: це структура, в яку потрапляють усі функції, що викликаються.
Якщо одна з функцій виконується довго (наприклад, важке обчислення), усі інші задачі - включно з обробкою кліків, рендерингом чи відповідями від сервера - будуть чекати, поки call stack не звільниться.
Щоб не блокувати цей єдиний потік, браузер надає асинхронні API (setTimeout, fetch, Web API). Коли ми викликаємо, скажімо, fetch(), у стек додається лише короткий виклик цієї функції. Власне мережевий запит виконується в окремому потоці, який створює браузер. Тобто, один потік виконує задачі які є у call stack, а інший потік чекає поки відповідь поверне сервер.
Але асинхронна операція колись завершиться і треба механізм який віддасть нашому головному потоку результат роботи іншого потоку. Коли це стається колбек або проміс‑резолвер не додається одразу у call stack. Спершу він потрапляє до черги подій (task queue). За роботою черги стежить event loop. Його правило просте - поки стек порожній дістати першу задачу із черги і покласти у стек.
Так ми досягаємо псевдобагатозадачності: основний потік виконує короткі шматки коду послідовно, а довгі операції «живуть» поза стеком. Коли довгі операції завершуються вони формують чергу задач, які треба виконати а event loop ці задачі закидає до стеку, коли call stack стає порожнім.
Це максимально спрощене пояснення, і без візуалізації може здатися складним. Якщо хочете краще зрозуміти, дуже раджу подивитись відео Jake Archibald — "In The Loop" на YouTube (англійською). https://www.youtube.com/watch?v=8aGhZQkoFbQ
Або приходьте на мій курс JavaScript Поглиблений, де ми розбираємо це на практиці.
Також корисна стаття на MDN, де ці процеси описані докладніше.
Асинхронні операції на практиці: HTTP-запит як найпоширеніший кейс
Один з прикладів асинхронної операції - це запит на сервер через HTTP-протокол. Якщо організовувати запит через JavaScript у браузері без використання React, Angular або Vue.js, то це можна зробити за допомогою:
fetch
XMLHttpRequest
Спеціалізована бібліотека, наприклад, Axios
Ось так буде виглядати простий код написаний на fetch
```js
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => console.log(data));
```
А так на axios (axios одразу повертає розпарсений JSON як response.data, на відміну від fetch, де потрібно викликати .json() вручну)
```js
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => console.log(response.data));
```
Якщо розглянути саме fetch то ось що відбулося під капотом:
fetch створює HTTP запит вказавши HTTP метод, заголовки, тіло тощо
Цей запит передається у вбудовану систему Web API - окрему від JavaScript середу, яка працює в іншому потоці.
JavaScript не чекає відповіді - основний потік продовжує виконувати інший код
fetch повертає Promise - об'єкт, що представляє асинхронну операцію, результат якої з’явиться пізніше
Коли відповідь від сервера приходить, Web API кладе callback в чергу.
Event Loop перевіряє, чи call stack порожній, і виконує цю мікрозадачу.
Така поведінка дозволяє браузеру одночасно виконувати інші задачі, не чекаючи завершення запиту.
Про використання асинхронного коду в JavaScript є [безкоштовний урок на YouTube](https://www.youtube.com/watch?v=cvR1EQ1R0EQ)
Також більше про синтаксис Promise можна дізнатися в уроці [Асинхронний код. Promise](https://itvdn.com/ua/video/javascript-essential-ua/js-promise-ua) на ITVDN, а детальніше про варіанти організації такого коду буде написано далі.
Підходи до написання асинхронного коду
Складність роботи з асинхронним кодом полягає в тому, що обробка результату операції відбувається не відразу, а через певний час після її запуску. Ми ініціюємо асинхронну операцію й можемо виконувати інші завдання, але все одно маємо якось дізнатися про її завершення та обробити результат. Проблема в тому, що в цей момент програма вже виконує інші дії.
Тому для обробки асинхронних операцій використовується push-модель взаємодії: отримувача даних (наш код) викликає провайдер даних - окремий механізм, який керує асинхронною операцією. По суті, розробнику потрібно відреагувати на подію завершення асинхронної операції. Для цього існує кілька підходів:
callback-функція
Promise
async/await (синтаксичний цукор над Promise)
Observables
Використання функцій зворотнього виклику (callback)
Почнемо з callback-функцій. Це найпростіший підхід, але він може призвести до заплутаного коду, особливо коли одна асинхронна операція запускає іншу, і так утворюється ланцюг.
Уявімо, що маємо функцію downloadImage(url, callback), яка завантажує зображення асинхронно, не блокуючи основний потік. Перший параметр - це адреса зображення, яке потрібно завантажити, а другий - функція, яку буде викликано після завершення завантаження. При цьому саме зображення буде передане як параметр у callback.
Приклад використання:
```js
downloadImage(url1, image => document.body.append(image))
```
На перший погляд усе просто. Але якщо потрібно завантажити кілька зображень послідовно, код стає менш зрозумілим:
```js
downloadImage(url1, image => {
document.body.append(image);
downloadImage(url2, image => {
document.body.append(image);
downloadImage(url3, image => {
document.body.append(image);
})
})
});
```
Така вкладена структура швидко ускладнюється, особливо якщо замість одного рядка з DOM-змінами з’являється додаткова логіка. Подібний стиль називають "Pyramid of Doom", і його краще уникати.
Один зі способів спростити обробку асинхронних викликів - це використання Promise.
Використання Promise
Promise - це об’єкт, який представляє асинхронну операцію. У перекладі з англійської promise означає «обіцянка». Можна уявити, що це обіцянка від браузера надати в майбутньому або результат операції, або помилку, пов’язану з її виконанням.
Приклад використання: перепишемо попередню функцію downloadImage, щоб вона повертала Promise.
```js
let promise = downloadImage(url1);
promise.then(image => document.body.append(image));
```
Тут ми все одно використовуємо callback-функцію, але передаємо її вже в метод .then() об’єкта promise. Це важливий момент:
тепер асинхронна операція має об’єктне представлення, яке можна передавати як параметр у різні частини коду;
можна будувати ланцюжки промісів, позбуваючись вкладеності, яка виникала з callback.
Приклад:
```js
downloadImage(url1) // отримуємо проміс
.then(image => { // вказуємо що робити коли promise перейде в стан resolved
document.body.append(image);
return downloadImage(url2); // виконуємо метод, який повертає promise
})
.then(image => { // результат роботи попереднього промісу передається як значення
document.body.append(image);
return downloadImage(url3);
})
.then(image => {
document.body.append(image);
});
```
Тепер код виглядає лінійним і набагато зручнішим для супроводу.
У прикладах вище ми не розглядали, як саме створюється Promise, адже важливо зрозуміти мотивацію використання цих об’єктів. Тим більше, що більшість API браузера вже повертають готові проміси. Наприклад:
```js
fetch('<https://jsonplaceholder.typicode.com/users>')
.then(res => res.json());
```
Якщо хочете детальніше розібратися зі створенням Promise вручну - перегляньте документацію на MDN або мій відео урок на ITVDN.
Async/await
Супроводжувати синхронний код завжди простіше, ніж асинхронний. У 2012 році в мові C# з’явився синтаксичний цукор, який значно спростив роботу з асинхронними операціями: замість вкладених callback можна було використовувати послідовний синтаксис з новими ключовими словами async та await. Згодом цю концепцію перейняли й інші мови програмування, зокрема Python та JavaScript. В JavaScript підтримку async/await додали у 2017 році.
Призначення ключових слів
async - додається до функції та вказує, що вона завжди повертає Promise.
await - використовується перед об’єктом-промісом, щоб "дочекатися" результату перед виконанням наступних рядків коду.
Перепишемо попередній приклад із завантаженням зображень, використовуючи async/await:
```js
let image = null;
image = await downloadImage(url1);
document.body.append(image);
image = await downloadImage(url2);
document.body.append(image);
image = await downloadImage(url3);
document.body.append(image);
```
Тепер код виглядає як звичайний синхронний: немає вкладених callback, усе читається рядок за рядком. Можна подумати, що await "зупиняє" виконання, очікуючи на результат промісу. Насправді ж код не блокує основний потік - під капотом він перетворюється на машину станів, де кожен стан описує дію до або після await.
Ще одна перевага async/await - знайомий синтаксис для обробки помилок:
```js
try {
let image = null;
image = await downloadImage(url1);
document.body.append(image);
image = await downloadImage(url2);
document.body.append(image);
image = await downloadImage(url3);
document.body.append(image);
} catch (ex) {
// обробка помилки
}
```
У результаті асинхронний код виглядає так само зрозуміло, як і синхронний, що значно спрощує його супровід.
Observable
Observable - це ще один підхід до організації асинхронного коду. Назва походить від однойменного патерна проєктування (Observer pattern), який описує створення об’єктів, за якими можна «спостерігати», отримуючи від них сповіщення. Тобто це реалізація подієвої моделі за допомогою ООП.
У сучасному JavaScript ця ідея пішла далі й стала основою реактивного програмування та бібліотеки RxJS.
Якщо Promise представляє одне майбутнє значення (успішне або помилкове), то Observable - це потік значень, які можуть з’являтися з часом.
Можна уявити:
Promise - це одна посилка, яку ви отримаєте в майбутньому;
Observable - це підписка на журнал, нові випуски якого надходитимуть регулярно.
Щоб створити Observable, використовують конструктор або готові оператори RxJS. Наприклад, функція downloadImages(urls) може завантажувати кілька картинок і відправляти їх «у потік» по мірі завантаження:
```js
import { Observable } from 'rxjs';
function downloadImages(urls) {
return new Observable(subscriber => {
urls.forEach(url => {
downloadImage(url, image => {
subscriber.next(image); // надсилаємо картинку у потік
});
});
subscriber.complete(); // повідомляємо, що потік завершено
});
}
```
Щоб використати Observable, на нього треба підписатися за допомогою subscribe:
```js
downloadImages([url1, url2, url3])
.subscribe({
next: image => document.body.append(image), // що робити з новим значенням
error: err => console.error(err), // обробка помилок
});
```
Переваги Observable
працюють із потоком даних, а не з одним результатом;
підтримують підключення операторів трансформації (фільтрація, мапінг, комбінування потоків);
можна легко скасувати виконання (відписатися від потоку).
Нижче приклад обробки даних через оператори. В RxJS оператори підключаються через метод pipe:
```js
import { filter, map } from 'rxjs/operators';
downloadImages([url1, url2, url3])
.pipe(
filter(image => image.width > 100), // пропускаємо лише великі картинки
map(image => {
image.classList.add('highlight');
return image;
})
)
.subscribe({
next: image => document.body.append(image),
error: err => console.error(err),
complete: () => console.log('Готово')
});
```
Таким чином, як і у випадку з Promise, можна будувати ланцюжки обробки. Але Observable значно гнучкіші: вони дозволяють працювати не лише з одним значенням, а з динамічною послідовністю даних у часі.
Для глибшого занурення рекомендую офіційний гайд Observable на RxJS.dev. та відео уроки Observables. Частина 1, та Observables. Частина 2[1]
Практичні поради по работі за асинхроним кодом
Не змішуйте підходи без потреби
Якщо почали писати з async/await, не вставляйте всередину .then() без особливої причини. Це ускладнює читання.
Обробляйте помилки
Використовуйте try/catch для async/await або .catch() для Promise. У випадку Observable завжди додавайте обробку error у subscribe().
Скасовуйте непотрібні операції
Для Observable використовуйте unsubscribe(), коли потік більше не потрібен. Для fetch можна застосувати AbortController, щоб зменшити навантаження й уникнути витоків пам’яті.
Уникайте "Pyramid of Doom"
Замість вкладених callback застосовуйте Promise, async/await або Observable.
Використовуйте паралельне виконання
Якщо операції незалежні, запускайте їх одночасно через Promise.all().
Ізолюйте логіку обробки
Розділяйте завантаження даних та маніпуляції з DOM. Це спростить тестування й повторне використання коду.
Логуйте стан і помилки
Під час розробки виводьте у консоль ключові події асинхронних операцій, щоб відстежувати їх послідовність.
Пам’ятайте про event loop
Розуміння різниці між мікрозадачами й макрозадачами допоможе прогнозувати порядок виконання коду.
Перевіряйте сумісність середовища
Деякі API можуть бути відсутні у певних середовищах (наприклад, fetch у Node.js доступний лише починаючи з версії 18).
Чи замінить штучний інтелект розробників?
Автор: Влад Сверчков
Що таке ШІ?
Визначення та типи
Використання ШІ в ІТ
Нейронні мережі
Людський мозок vs Штучний інтелект
Український ринок ІТ після буму AI
Чи варто боятися розробникам: що думає про це сам штучний інтелект?
Майбутнє співробітництва ШІ та людини
Висновки
Сучасний технологічний ландшафт зазнає змін з неймовірною швидкістю, і штучний інтелект (ШІ, AI) сьогодні є одним з його Еверестів. Він вже допомагає нам в автоматизації різних завдань і процесів: спрощує написання текстів та програмного коду, генерує зображення та відеоряди, консультує нас із різних запитань, допомагає вчити іноземні мови.
Як відомо, більшість гір виникають на місці зіткнень тектонічних плит під супровід землетрусів – схожий процес маємо і з активним розвитком ШІ. Людство розділилося на два великі табори: перші захоплено спостерігають за процесом утворення технологічних Гімалаїв, а другі пророкують ряд катастроф – однією з найстрашніших вказують можливе безробіття, особливо серед розробників.
ITVDN – це освітня онлайн платформа для вивчення програмування та ІТ, тому ми не могли пройти осторонь теми перспективності професії розробника. Пропонуємо поринути в неї і разом розібратися, чия літосферна плита буде зверху та хто візьме гору в цій боротьбі – програмісти чи штучний інтелект?
Що таке ШІ?
Визначення та типи
Штучний інтелект означає здатність машин імітувати людські когнітивні функції, такі як навчання, вирішення проблем і прийняття рішень. Він охоплює широкий спектр технологій і застосунків, що дозволяють машинам виконувати завдання, які зазвичай вимагають людського інтелекту, як-от візуальне сприйняття, розпізнавання мови, прийняття рішень і мовний переклад.
Відповідно до відеоролику “The 7 Types of AI” з офіційного YouTube-каналу IBM Technology (IBM – один із найбільших в світі провайдерів ІТ-інфраструктури, ПЗ та консультаційних послуг), AI має щонайменше 7 типів, але реалізацію мають лише три з них.
Назва
Короткий опис
Artificial Narrow Intelligence або вузький AI (слабкий AI)
Призначений для виконання конкретного завдання або вузького кола завдань. Серед прикладів – сучасні сервіси Alexa, Siri, Cortana тощо.
Artificial General Intelligence або загальний AI (сильний AI)
Здатен розуміти, навчатися і застосовувати знання в широкому спектрі завдань – подібно до людини. Цей рівень ШІ все ще залишається здебільшого теоретичним і наразі існує хіба що в науковій фантастиці.
Artificial Super Intelligence або штучний суперінтелект
Гіпотетичний рівень ШІ, перевершує людський інтелект у всіх відношеннях і здатний перевершити найкращі людські розуми в кожній галузі.
Reactive Machines або реактивні машини
Штучний інтелект найпростішого типу, не має здатності формувати пам'ять або використовувати минулий досвід для прийняття обґрунтованих рішень. Працює виключно на основі поточних даних і не враховує контекст. Як приклад – Deep Blue від IBM, який може грати в шахи на рівні гросмейстера і прораховувати усі можливі сценарії.
Limited memory або обмежена пам’ять
На відміну від реактивних машин, ШІ з обмеженою пам'яттю може звертатися до минулого досвіду для прийняття рішень. Він може зберігати і згадувати попередні дані, що дозволяє йому адаптуватися до ситуації і відповідним чином реагувати. Приклади: самокеровані автомобілі, системи рекомендацій, спам-фільтри, чат-боти, пристрої розумного будинку тощо.
Theory of Mind або теорія розуму
Головна мета – зрозуміти і передбачити поведінку, емоції, відчуття людини, аби потім надати персоналізовані послуги. Цей тип ШІ враховує, що люди мають різні переконання, бажання та перспективи. Наразі Theory of Mind перебуває на стадії досліджень і розробок
Self-aware AI або самосвідомий ШІ
“Найстрашніший” вид штучного інтелекту, майбутнє ШІ, яке йому так пророкують. Володіє свідомістю і почуттям власної гідності. Він розуміє власне існування, емоції та процеси мислення, має власні потреби. Однак досягнення самосвідомості штучних систем все ще лежить в суто теоретичній площині.
Reactive Machines та Limited memory відносяться до Narrow AI, Theory of Mind – до General AI, а Self-aware – до суперінтелекту.
Використання ШІ в ІТ
В 2024 році штучний інтелект продовжує стрімко трансформувати різні сфери ІТ – ось декілька прикладів:
1. Кібербезпека
“Розумні” системи аналізують величезні потоки даних для проактивного виявлення загроз та їх мінімізації, вивчають моделі поведінки користувачів і позначають підозрілі дії, аналізують фінансові транзакції, допомагають у створенні звітів тощо.
2. DevOps і хмарні обчислення
ШІ автоматизує такі завдання, як виділення ресурсів, управління конфігурацією і масштабуванням, аналізує дані щодо продуктивності системи і пропонує оптимізацію для використання ресурсів і скорочення витрат. Також системи з інтелектом автоматично виявляють і усувають проблеми в хмарній інфраструктурі, підвищуючи відмовостійкість і час безвідмовної роботи системи.
3. Розроблення програмного забезпечення
Технологічні інновації в галузі AI дозволяють генерувати, рефакторити та пояснювати код для розробників, виявляти потенційні помилки, автоматизувати процеси тестування, прискорюючи розроблення, автоматично доповнювати код в реальному часі тощо.
4. Тестування і забезпечення якості
Інструменти тестування на основі ШІ здатні проводити аналіз коду, виявляти вразливості, вивчати результати попередніх тестів, автоматично генерувати тест-кейси тощо.
5. Управління даними та аналітика
Автоматизація завдань з опрацювання даних, виявлення та виправлення помилок, підготовка даних до аналізу, інтелектуальний аналіз даних, генерування звітів та презентацій, виявлення прихованих закономірностей, прогнозування майбутніх тенденцій та результатів, асистенція під час стратегічного планування та розподілу ресурсів – і це тільки загальні точки, в яких штучний інтелект впливає на домени Big Data, Data Science тощо.
6. Управління мережею
Машинний інтелект виявляє незвичайні шаблони мережевого трафіку, щоб запобігти кібератакам та/або перебоям у роботі мережі. Також він аналізує мережеві дані та автоматично налаштовує конфігурації для оптимальної продуктивності та використання ресурсів, прогнозує тенденції мережевого трафіку і динамічно розподіляє ресурси, щоб забезпечити безперебійну роботу і уникнути появи вузьких місць (так званих “bottlenecks”).
7. Управління ІТ-сервісами (ITSM)
Людино-машинна взаємодія із залученням ботів на основі ШІ сприяє автоматизації рутинних завдань як-от скидання паролів і вирішення інцидентів, зменшуючи навантаження на службу підтримки та підвищуючи її ефективність. Також ці системи аналізують дані ІТ-інфраструктури, щоб прогнозувати потенційні збої, вживати превентивні заходи, мінімізувати час простою, діагностувати першопричини проблем і пропонувати рішення, оптимізуючи процеси усунення несправностей.
І це тільки частина тих сфер, де штучний інтелект вже використовується і активно розвивається. До них можна додати:
ІТ-менеджмент;
інтернет речей (IoT) і робототехніка загалом (як цивільна, так і військова);
розроблення на основі NLP: віртуальні асистенти, чат-боти, інтерфейси, що активуються голосом;
електронна комерція: рекомендаційні, аналітичні системи;
віртуальна реальність, комп’ютерні ігри тощо.
Виходить, під загрозою “вимирання” не тільки розроблення, а й інші ІТ-галузі.
Нейронні мережі
Нейронна мережа – важливий фундамент в розумінні штучного інтелекту. Хоча це популярний та ефективний метод побудови штучних систем, існують й інші методи, наприклад системи на основі правил, дерева рішень, генетичні алгоритми, машини опорних векторів тощо. Ці методи часто використовують у поєднанні з нейромережами для створення більш складних інтелектуальних систем.
То яким чином нейронки пов’язані з розумними машинами?
Нейронні мережі складають кістяк алгоритмів глибокого навчання (Deep Learning, DL), глибоке навчання є підмножиною машинного навчання (Machine Learning, ML), а машинне становить підмножину AI. Окрім ML штучні системи можуть включати в себе оброблення природної мови (NLP), комп’ютерний зір, рух, озвучування тексту тощо. Картинка вище візуалізує ці моменти для кращого розуміння.
Якщо приклади AI навести досить просто (ChatGPT, Midjourney, GitHub Copilot і т. д.), то де в житті ми напряму зіштовхувалися з іншими трьома складовими? Ось деякі з них:
нейромережа може гарно розпізнавати голос, об’єкти на зображеннях та відео тощо;
Google Translate – яскравий приклад DL;
коли ви зайшли в інтернет-магазин, подивилися щось, а згодом отримали пропозиції з товарами, які “можуть вам сподобатися” – постаралося машинне навчання.
За своєю будовою штучні нейронні мережі дуже нагадують ті, що знаходяться в нашому мозку, але насправді навіть один людський нейрон більше нагадує велетенську нейромережу.
З чого складається типова нейронна мережа:
На картинці зображено глибоку нейромережу – це мережа, яка має більше трьох прихованих шарів (hidden layers) включно з вхідним (input) та вихідним (output). Якщо шари всього 3, маємо справу з найпростішою нейромережею. До речі, картинку вище можна розглядати як алгоритм глибокого навчання.
Зі схематичним зображенням познайомилися, а тепер нас цікавить робота нейронної мережі.
Кожен шар нейромережі складається з вузлів – штучних нейронів. Кожен вузол з'єднаний з нейронами наступного шару, причому кожен має свою вагу та поріг. На вхід вузла надходить певне значення, яке зазнає впливу ваги, і якщо потім це значення перевищує поріг, даний вузол “активується” за допомогою активаційної функції, і результуючі дані надсилаються до наступного шару мережі. Якщо вихідне значення вузла нижче за поріг, до наступного вузла воно не потрапляє.
Навчальні дані допомагають навчати нейронну мережу та покращувати її точність із часом. Коли алгоритми навчання якісно налаштовані, вони стають міцним фундаментом ШІ, оскільки можуть дуже швидко класифікувати та кластеризувати дані – це дозволяє суттєво прискорити різні операції. Класифікація нейронних мереж з прикладами реалізації тут наводитися не буде, але відмітимо пошуковий алгоритм Google – це, напевно, найвідоміша реалізація нейронки; саме завдяки їй пошуковик настільки швидкий та ефективний.
Людський мозок vs Штучний інтелект
Переваги мозку
Головний командний центр людини має низку сильних сторін, яким поки поступаються розумні системи:
Креативність та адаптивність. Мозок чудово генерує нові ідеї, вирішує проблеми нестандартними способами та адаптується до непередбачуваних ситуацій, в той час як ШІ та творчість – пара, яка потребує відпрацювання багатьох “па”.
Потужний інтелектуальний спектр. Під владою наших півкуль знаходяться складні міркування, абстрагування, емоційний інтелект тощо.
Когнітивна гнучкість та інтуїція. Люди можуть переносити досвід з інших сфер життя для адаптації до нових умов та вирішення нових проблем, з якими вони не стикалися, приймати судження та рішення на основі неповної інформації та інтуїції, з чим нейромережі мають проблеми.
Стійкість до “шуму” вхідних даних. Людський мозок легко проходить повз викривлення або невеликі збурення у вхідних даних, які в розумних системах можуть призвести до значних помилок або неправильних класифікацій.
Емоції та етичність. Етика в ШІ недостатньо розвинена: комп’ютери не настільки гарно працюють з людськими емоціями, емпатією та моральними нормами, в той час як для людини ці речі є фундаментально важливими та визначальними у прийнятті рішень.
Eco-friendly 💚 Людський мозок споживає до 20 Вт, що робить нас значно енергоефективнішими за штучні технології, які споживають великі об’єми електроенергії (особливо на етапі навчання) і не можуть існувати без живлення.
Переваги штучного інтелекту
Наші цифрові візаві також мають чим похизуватися:
Швидкість, об’ємність та ефективність. Машинне навчання дозволяє обробляти величезні обсяги даних, швидко аналізувати, робити висновки і виконувати конкретні завдання в рази швидше людини.
Розпізнавання та аналіз шаблонів. Штучні нейронні мережі чудово розпізнають закономірності та складні взаємозв'язки в даних, допомагаючи в таких завданнях, як розпізнавання зображень, переклад текстів і т. д.
Масштабованість і автоматизація. Системи штучного інтелекту можна масштабувати й автоматизувати, що дозволяє їм оперативно справлятися з великими робочими навантаженнями і репетативними задачами.
Налаштування та оптимізація. Алгоритми ШІ можна навчати і вдосконалювати під конкретні завдання, досягаючи високого рівня точності і результативності.
Доступність 24/7. Системи AI можуть працювати цілодобово, забезпечуючи безперебійне обслуговування, наприклад постійний моніторинг стану інфраструктури, відеоспостереження, регулювання температури в приміщенні тощо.
Відсутність деяких людських вад. Штучний інтелект не може забути про задачу, розлінитися, проспати, захворіти, мати похмілля, злякатися, бути не в настрої або просто не любити вас. Звісно, ніхто не застрахований від зникнення живлення, хакерських атак, проблем з програмно-апаратною начинкою абощо, але в плані працездатності, доступності і надійності комп’ютерні системи значно краще себе проявляють, ніж людина.
Зниження ризиків. ШІ можна застосовувати в умовах підвищеного ризику та небезпеки. Наприклад, роботи зі штучним інтелектом можуть виконувати небезпечні завдання на виробництві, брати участь у пошуково-рятувальних місіях і під час ліквідації наслідків стихійних лих, мінімізуючи загрозу для людей.
Український ринок ІТ після буму AI
Яким розробники бачать майбутнє ШІ?
Ще в 2017 році дослідники з Oak Ridge National Laboratory – американської лабораторії, яка займається науковою діяльністю і фінансується Міністерством енергетики США – опублікували статтю, в якій припускають, що до 2040 року більшість програмного коду будуть писати саме машини, і це радикально вплине на створення ПЗ як таке.
А зовсім нещодавно – 12-14 лютого 2024 року – на Всесвітньому урядовому саміті в Дубаї обговорювався штучний інтелект, і CEO компанії NVIDIA Дженсен Хуанг сказав, що якщо останні 10-15 років радили вчити програмування та комп’ютерні науки, то зараз ці поради неактуальні, адже ШІ перетворює кожну людину в програміста і дає можливість прокачати свої навички та суттєво підвищити продуктивність в інших затребуваних сферах, наприклад у цифровій біології, навчанні, виробництві, фермерстві тощо.
Також він додав, що якби міг почати все з початку, то фокусувався б на такому напрямку, як наука про життя (біонаука, цифрова біологія, біоінженерія), оскільки це надважлива комплексна галузь, розвиток якої в поєднанні з потужністю ШІ дозволить оптимізувати багато процесів, в тому числі пов’язані зі сталим розвитком.
Сучасні технологічні інновації дозволяють активно використовувати AI рішення, хоч і без ідеального результату. Давайте подивимось, як змінився ландшафт українського ІТ-ринку після “буму” ChatGPT та появи інших “штучних” гравців.
А зробимо ми це за допомогою аналітики від DOU – спільноти українських розробників. Точкою відліку візьмемо лютий 2023 року, коли “чат” став офіційно доступним в Україні і зникли блекаути.
Цьогоріч у лютому опубліковано аналітику, відповідно до якої кількість вакансій на DOU станом на січень 2024 року складає 5057. В лютому 2023 року ця цифра становила 3489, себто приріст на рівні 45%. Пам’ятаємо, що на минулорічну статистику могли вплинути блекаути, але в тому ж лютому енергетична ситуація почала значно покращуватися.
Порівняємо кількість технічних вакансій у лютому 2023 та в січні 2024 року:
Напрямок
Лютий 2023
Січень 2024
Java
138
119
QA
177
236
.NET
112
149
Front End
196
214
Android
44
36
Python
115
178
Node.js
107
153
PHP
141
177
DevOps
160
166
AI/ML
11
40
Big Data
19
18
Data Engineer
22
63
Data Science
29
58
React Native
25
33
Ruby
19
23
iOS/macOS
40
43
Як бачимо, в переважній більшості напрямків кількість вакансій зросла після того, як штучний інтелект вийшов на глобальний ринок, а в інших спостерігаємо несуттєве зменшення.
Яка ситуація на ринку початківців (0-3 роки досвіду)?
В усіх трьох категоріях маємо помітне зростання.
Що думає про це сам штучний інтелект?
Давайте послухаємо аргументи ШІ щодо того, чи зможуть нейромережі замінити розробників і зайняти їхнє місце.
ChatGPT
Відповідь ChatGPT версії 3.5:
“Розробники програмного забезпечення незамінні завдяки низці переваг:
Творче вирішення проблем: розробники застосовують креативне мислення для вирішення складних проблем, пропонуючи унікальні та ефективні розв’язки.
Навички критичного мислення: вони досягають успіху в розбитті складних проблем, їх аналізі та розробці ефективних рішень.
Адаптивність і здатність до навчання: розробники йдуть в ногу з часом, швидко адаптуючись до нових технологій та вивчаючи нові мови програмування, що дозволяє їм залишатися конкурентоспроможними в постійно мінливому середовищі розробки ПЗ.
Співпраця та комунікація: ефективна співпраця та комунікація дозволяють розробникам гармонійно співпрацювати з міжфункціональними командами, включаючи дизайнерів, Product менеджерів та тестувальників.
Етичні міркування: розробники повинні враховувати етичні аспекти у своїй роботі, забезпечуючи відповідність своїх творінь суспільним цінностям і нормам.
По суті, хоча ШІ може провести інтелектуальний аналіз даних та автоматизувати певні завдання, унікальне поєднання креативності, критичного мислення, адаптивності, співпраці та етичних міркувань розробників залишається незамінним для грамотного програмування та інших процесів розробки ПЗ”.
Gemini (Bard)
“Хоча я, Бард, велика мовна модель, володію вражаючими можливостями в розумінні та генеруванні тексту, повністю замінити розробників – це занадто, принаймні поки що. Ось чому:
Творчість та вирішення проблем. Хоча я можу писати рядки коду як програмісти, справжні інновації виникають завдяки здатності людського розуму уявляти унікальні рішення, що виходять за рамки усталених шаблонів.
Глибока експертиза в галузі. Хоча я маю доступ до величезного обсягу інформації та можу її обробляти, глибоке розуміння та контекст, якого розробники набувають завдяки багаторічному досвіду роботи в конкретних галузях, є незамінними.
Людські судження та етика в ШІ. Розробники привносять в роботу безцінні людські судження та етичні міркування, гарантуючи, що їхні творіння відповідають принципам відповідального використання. Орієнтування в етичних дилемах залишається поза межами сучасного ШІ.
Комунікація та співпраця. Розробка ПЗ процвітає завдяки командній роботі та ефективній комунікації. Хоча я можу обробляти інформацію та реагувати на неї, мені бракує навичок ефективної співпраці з різними командами, генерування ідей та орієнтації в складнощах людської взаємодії.
Адаптивність та здатність до навчання. Технологічні інновації носять досить динамічний характер, а розробники вміють постійно вчитися та опановувати нові технології. Хоча я також це можу, я обмежений в своїх параметрів, і моя здатність до навчання поступається людям-розробникам.
Майбутня співпраця, а не заміна.
Я уявляю собі майбутнє, в якому машинний розум розширює можливості розробників, слугуючи потужним інструментом для вирішення повторюваних завдань: автоматизація коду, пропозиції щодо його покращення та розширення, інші можливості для вирішення проблем. Ця співпраця відкриє шлях до більших інновацій та ефективності у сфері розробки ПЗ”.
Perplexity
Відповідь великої мовної моделі – розмовного пошуковика від Perplexity AI:
“Вплив штучного інтелекту на розробку ПЗ є безперечно трансформаційним, але він не може замінити унікальний людський підхід, який стимулює інновації та творчість. Штучний інтелект чудово справляється з автоматизацією повторюваних завдань, покращує якість коду та пропонує оптимізації. Однак йому бракує здатності мислити нестандартно, уявляти нові концепції чи розуміти нюанси складних систем.
Обмеження розумних систем полягають у їхній нездатності до справжньої творчості, яка є відмінною рисою розробників-людей. Вони навчаються на наявних даних, і хоча можуть генерувати код на основі шаблонів, але не можуть впроваджувати інновації чи створювати щось абсолютно нове.
В осяжному майбутньому програмісти будуть доповнені, а не замінені на ШІ. Він допоможе їм зосередитися на більш складних завданнях, підвищити ефективність і зменшити кількість помилок. У міру розвитку інтелектуальних машин розробникам потрібно буде адаптуватися і опановувати нові навички, щоб працювати разом з цими потужними інструментами.
Майбутнє розробки ПЗ – це людино-машинна взаємодія, симбіоз людини та AI, де другий розширює людські можливості, а розробники-люди продовжують стимулювати інновації”.
А довіряти цим відповідям, чи ні – вирішуйте самі, шановні читачі. Можливо, штучний інтелект вже зараз приховує від нас якісь плани і спеціально присипляє нашу увагу, аби в найбільш вдалий момент зробити з нас щось на кшталт сервісного персоналу або домашніх улюбленців?
Майбутнє співробітництва ШІ та людини
Сучасна людино-машинна взаємодія постає у вигляді ефективної кооперації, що спрощує рутинні задачі та оптимізує багато процесів, пов’язаних з ІТ, починаючи з інтерпретації, генерації та рефакторингу коду і закінчуючи повноцінними AI інструментами під кожну спеціальність.
Важко спрогнозувати точний рівень загрозливості штучних систем айтішним професіям у майбутньому, але зараз це корисний інструмент, який програмісти мають вивчати, аби з легкістю обходити рутину і більше часу приділяти дійсно важливим процесам.
Іншими словами, сучасні ШІ – це наші помічники, і в найближчому майбутньому вони не замінять нас, а органічно доповнять і переформатують наші ролі. Та хто знає, як буде в більш далекій перспективі.
Цілком ймовірно, що позиції розробників рівня Intern-Junior будуть зазнавати найбільших видозмін, оскільки робота нейронної мережі досить непогано заточена під генерування коду для нескладних задач. Але не забуваймо, що результат їхньої роботи все ще далекий від ідеалу.
Висновки
То чи замінить штучний інтелект розробників?
Вище ми проаналізували ситуацію на українському ринку ІТ, і зараз натяків на те, що програмісти залишатися без роботи, немає. Штучні системи поки що займають роль асистентів і спрощують рутинну та/або часовитратну роботу: написати шматок коду або частину документації, нагадати функціонал певного методу, дати пораду або пояснити код, провести його рефакторинг тощо.
Найрозумніший крок в даних умовах – вивчати AI. Навіть якщо деякі професії почнуть зникати, то досвідчені фахівці, котрі вміють делегувати рутину нейронкам і розробляти комплексні рішення, точно не залишаться осторонь. Тому фокусуйтеся на складних та креативних рішеннях, які нейромережа не осилить, масштабуйте знання та навички, отримуйте унікальний досвід, завжди слідкуйте за новинами світу ШІ, а також будьте відкриті до нових технологій та вмінь.
Бажаємо вам успіхів у розробці та вивченні AI інструментів! Ваші думки щодо майбутнього штучного інтелекту та його впливу на розробників пишіть у коментарях
Кар'єра в IT: хто такий Embedded-розробник, завдання та обов'язки
Автор: Влад Сверчков
Хто такий Embedded-розробник?
Сфери застосування Embedded-розробки.
Що має знати Embedded-розробник.
Як стати Embedded-розробником.
Де отримати знання.
М’які навички.
Ключові тренди в Embedded.
Кар’єрні перспективи та заробітна плата Embedded Engineer.
Кількість вакансій.
Конкуренція.
Заробітна плата.
Кар’єрні можливості.
Висновки.
Переважна більшість новачків в Україні обирає тестування або розробку із залученням C#./NET, Java, Python, JavaScript (фронтенд та Node.js). Ці напрямки мають найбільшу кількість вакансій в ІТ, низькі вимоги до вищої освіти, потужну розпіареність майже в усіх навчальних центрах, а також відносно простий перехід в ІТ з будь-якої не айтішної професії.
Спеціальність, яку ми сьогодні розглядатимемо, не є настільки популярною, трендовою. Однак вона є не менш важливою за інші ІТ-напрямки. Герой нашої статті займається розробленням вбудованих (embedded) систем, і його іменують Embedded-розробник.
Що це за спеціальність, наскільки вона важлива і де використовується, що потрібно вчити для її опанування, як виглядає день Embedded-ера, які кар’єрні перспективи та зарплати у фахівців цього напрямку – розбір цих та інших інтригуючих питань читайте нижче.
Хто такий Embedded-розробник?
Почнемо з поняття “Embedded система”. Трактування в різних джерелах відрізняється, але якщо звести все під один знаменник – це комп’ютеризована система, яка вбудована у механічну або електронну систему і виконує певну функціональність. Тобто, це система, в якій комп’ютер (як правило мікроконтролер або мікропроцесор) є невід’ємною частиною іншої системи.
Як приклад: мікроконтролер у вашій кавовій машині, мікрохвильовці або пральній машинці; пристрій, який забезпечує спрацювання подушок безпеки в автомобілі, контролер у літаку, який збирає певні дані про політ – навіть Bluetooth-навушники містять свою власну вбудовану систему.
Відповідно, Embedded-розробник – це спеціаліст, який займається розробкою подібних вбудованих систем, використовуючи програмування (software) та навички роботи із “залізом” (hardware).
Життєвий цикл продукту дуже тісно зав’язаний на розробнику вбудованих систем. Обов’язки Embedded-розробника можуть різнитися в залежності від мікроконтролерів / мікросхем / пристроїв тощо, з якими він працює, але основні фази, де відчутна роль цього спеціаліста, наступні:
Аналіз вимог. Комунікація з клієнтами, дизайнерами та інженерними командами для визначення функціональних і технічних вимог до вбудованої системи.
Проєктування системи. Розроблення архітектури та дизайну вбудованої системи, вибір апаратних та програмних компонентів.
Перевірка базової працездатності. Перед початком роботи Embedded розробник повинен перевірити функціонування плати, оновити драйвери за потреби і переконатися, що “залізо” справне.
Програмування. Написання програмного коду, вбудованих програм і системних протоколів.
Тестування та налагодження. Проведення тестів для перевірки працездатності embedded-системи.
Інтеграція. Забезпечення взаємодії вбудованої системи з апаратурою, зовнішніми пристроями та іншими складовими системи.
Оптимізація продуктивності. Вдосконалення ефективності та продуктивності вбудованої системи, враховуючи обмежені ресурси.
Документація. Формування діагностичних звітів, технічних інструкцій та документації щодо розроблення ПЗ. Також читання форумів, статей та інструкцій до плат та компонентів, з якими ведеться робота.
Співпраця з іншими командами. Взаємодія з hardware та software розробниками, участь в різних мітингах, обговореннях, комунікація з замовником тощо.
Безпека та надійність. Забезпечення високого рівня безпеки, надійності та працездатності вбудованої системи.
Типовий робочий день Embedded-розробника складається з усього потроху:
написання та налагодження коду, пошук багів;
робота з апаратною начинкою – інколи доводиться і паяльником почаклувати, і щось мультиметром виміряти, і осцилограф застосувати тощо;
читання документації, пошук інформації на форумах;
тестування вбудованих систем та їхніх складових;
код-рев’ю;
рефакторинг;
взаємодія з командою, керівництвом: мітинги, ділова переписка, зідзвони з певних питань (або живе спілкування) і т. д.
Сфери застосування Embedded-розробки
Embedded у XXI столітті має гарний приріст затребуваності у зв’язку з розвитком ІТ-індустрії, в особливості – мережевих технологій, операційних систем, мов програмування та внаслідок модернізації “заліза”. Наразі сфера застосування вбудованих систем досить об’ємна:
Побутова електроніка. Вбудовані системи є невід’ємними атрибутами затишного житла: це смарт-телевізори, холодильники, мультиварки та інша кухонна електроніка, пристрої безпеки, смарт-девайси для будинку.
Машинобудівна промисловість. Embedded технології є ключовими для сучасних транспортних засобів, починаючи від цивільного автопрому і закінчуючи потужними будівельними гуліверами. Вони входять до складу двигунів, навігаційних систем, клімат-контролю, розважальних блоків тощо.
Медицина. Сучасна медицина неможлива без комплексного цифрового обладнання, включаючи пристрої моніторингу пацієнтів, системи візуалізації, діагностичні інструменти тощо.
Промислова автоматизація. Вбудовані системи використовуються в промислових системах керування, програмованих логічних контролерах (PLC) і робототехніці для автоматизації та керування процесами.
Телекомунікації. Вбудовані системи – невід’ємна частина мережевого обладнання, маршрутизаторів, комутаторів і комунікаційних пристроїв для ефективної передачі даних.
Аерокосмічна промисловість. Сучасна авіоніка використовує ембедед для керування функціями літака, системами навігації та зв’язку. Якщо хтось хоче тераформувати Марс – без Embedded-у буде дуже важко.
Військова галузь. Вбудовані системи відіграють вирішальну роль у оборонних програмах як у мирний час (спостереження, аналіз, прогнозування, наведення тощо), так і під час війни (згадайте ті ж джавеліни, дрони, хаймарси, петріоти і т. д. – там дуже багато електроніки).
Розумні мережі. Розвиток розумних мереж (smart grid) підвищує ефективність і надійність розподілу електроенергії.
IoT (Інтернет речей). Пристрої IoT значною мірою залежать від вбудованих систем для збору, обробки та передачі даних для різних програм. Приклади: розумний будинок, автоматизовані системи поливу, розумні міста тощо.
Що має знати Embedded-розробник
В Embedded-і ціла палітра спеціальностей. Наприклад, приставки “Software”, “Firmware” вказують на фокус саме на створенні ПЗ, а "Hardware" та "Hardware Design" – на апаратній частині. Тайтл на кшталт "Embedded Systems Engineer" говорить про необхідність мати однаково гарні hardware та software знання. Також треба розуміти, що в різних компаніях один і той же тайтл може мати під капотом зовсім різні технології в залежності від апаратної начинки, різновиду вбудованої системи абощо.
Наш опис необхідних технологій буде охоплювати і апаратне, і програмне крило вимог, що робить його ближчим до тайтлу "Embedded Systems Engineer". Якщо ви хочете більше в software, вивчайте глибше технології, спрямовані на розроблення софту; якщо ж у hardware – більше уваги приділяйте апаратним навичкам.
Отже, ми проаналізували тематичні статті, думки популяризаторів ембедеду та вакансії українського ринку, і на виході отримали наступний технологічний стек Embedded девелопера.
Математика, фізика, електроніка
Найперше ембедед-розробники повинні гарно знати математику та фізику – особливо теми, пов’язані з електротехнічним напрямком, аби гарно розуміти принципи функціонування hardware: розбиратися в начинці плат, вміти правильно розводити всі сигнали на платі, правильно рахувати всі доріжки, перекриття сигналів, їхню цілісність тощо. Ці знання важливі як під час розроблення, так і на моменті проєктування, тестування, комунікування з іншими колегами і не тільки.
Які розділи потрібно знати:
базова математика та обчислення;
дискретна математика;
електричні кола;
основи електроніки;
цифрова та аналогова мікросхемотехніка;
цифрові та логічні схеми;
комп’ютерна архітектура.
Тестове обладнання
Перераховане нижче обладнання ви будете широко використовувати для перевірки працездатності мікросхем, пошуку несправностей тощо. Вам знадобляться наступні інструменти:
мультиметр;
логічний аналізатор;
осцилограф.
Мультиметр є досить відомим приладом серед “електротехнарів” – ним вимірюють низку фізичних величин, як-от сила струму, опір, напруга та інші.
Логічний аналізатор використовується для роботи з цифровими сигналами в електронних системах, а осцилограф – для візуалізації та дослідження електричних сигналів, а також відображення їхніх параметрів в режимі реального часу.
Мікроконтролери
Одні з головних об’єктів зацікавленості Embedded-розробників. Під них і пишеться код мовами C/C++ і не тільки. МК являє собою невеликий електронний пристрій з великою кількістю “ніжок”, якими відправляються/приймаються відповідні електричні сигнали.
Що важливо знати з цієї теми:
GPIO
ADC
DAC
Таймери
PWM
Переривання
Сторожовий таймер (watchdog)
Керування тактовою системою (clock management)
Кожна модель МК має свої особливості, з якими вам треба буде знайомитися через відповідну документацію. Також вам треба знати периферію обраного вами мікроконтролеру і бібліотеки для керування нею.
Найпопулярнішим сімейством МК наразі є STM32. Досвід роботи з Arduino також вітається.
RTOS
Real Time OS – операційна система реального часу. Надає середовище виконання для програм, які мають строгий графік виконання завдань. Embedded-розробники використовують RTOS для обробки завдань з високою точністю і низькими затримками.
Linux
Якщо ви плануєте працювати з вбудованими пристроями на базі Linux (а їх зараз дуже багато), вам слід гарно знати і цю операційну систему. Вона має відкритий вихідний код і є безкоштовною.
Говорячи про приклади проєктів, лінукс використовує велика кількість систем IoT, промисловий, мережевий, автомобільний, медичний Embedded тощо.
Паяльник
Так, в даній професії необхідно інколи й паяльником попрацювати. Якісь компоненти можуть потребувати пайки – десь щось вилучити/додати/замінити, поекспериментувати з макетом, зібрати проєкт тощо. Може виникнути будь-яка ситуація на “залізі”, і ембедед-фахівець має бути готовим до цього.
Основи програмування
Фундамент, який зробить ваш процес програмування продуктивним. Сюди віднесемо наступні теми:
алгоритми та структури даних – ембедерам потрібні сильні знання з алгоритміки;
шаблони проєктування – так, в ембедеді є свої патерни, які полегшують життя девелоперам, наприклад Opaque Pointer pattern;
UML (діаграма стану, state machine) – для моделювання та аналізу поведінки системи.
Програмування на C/C++
C/C++ – це потужні мастодонти у світі програмування. В порівнянні з іншими мовами є складнішими у вивченні, натомість дають можливість писати низькорівневий код з високим ступенем контролю “заліза”.
Також C/C++ дозволяють напряму керувати пам’яттю (а це дуже важливо при роботі з hard-елементами, які мають досить обмежений ресурс), контролювати велику кількість low-level процесів, а також підходять для програмування широкого спектру “заліза”. Обов’язковий інструмент в скарбничці Embedded-девелопера – можна вивчити або C, або C++, або обидві мови.
Інтерфейси та протоколи
Обмін інформацією між Embedded-елементами та їхніми складовими здійснюється через спеціальні інтерфейси та протоколи:
UART, I2C, SPI – базові, їх треба знати обов’язково;
Ethernet, USB – для роботи на високих швидкостях;
Bluetooth – дуже відома безпровідна технологія;
TCP/IP, UDP – для роботи з мережею;
CAN – система, яка використовується в транспортних засобах;
Modbus – для роботи на промислових об’єктах.
Git
Git – система керування версіями проєкту, дуже популярний інструмент під час написання будь-якого коду, оскільки дозволяє відслідковувати зміни в коді, створювати гілки для роботи над різними функціями, ділитися своїм кодом з іншими колегами і навпаки тощо.
Agile/Scrum
Agile – це сімейство гнучких (agile) методологій розроблення ПЗ, яке сприяє випуску продукту порціями, крок за кроком розширюючи та покращуючи його. Як результат – команда здійснює розроблення відносно невеликими інтервалами, сам продукт постійно покращується, якість коду – підвищується. Познайомитися з Agile вам допоможе відповідний маніфест з детальним описом основних принципів.
Scrum є однією з найпопулярніших реалізацій даного підходу, яка часто використовується в багатьох ІТ-командах, в тому числі і під час роботи з вбудованими пристроями.
Англійська мова
English на рівні вільного читання документації має бути у кожного Embedder-а, оскільки левова частка роботи припадає саме на вичитування матеріалів до тих чи інших мікроконтролерів та супутніх пристроїв. Якщо ж ви будете працювати в команді з іноземцями, то треба підтягнути ще й розмовну англійську.
Як стати Embedded-розробником
Де отримати знання
Є декілька опцій для вивчення розробки вбудованих пристроїв:
Можна вчитися самостійно за допомогою безкоштовних ресурсів у вільному доступі: YouTube, roadmap-и з посиланнями на GitHub-і, статті, тематичні форуми, безкоштовні курси та матеріали (MIT OpenCourseWare, embedded.com) тощо. На плати Arduino, паяльник, мультиметр і т. д., звісно, таки доведеться розщедритися.
Проходження відео курсів (платно) на Udemy, Coursera, edX, Pluralsight та інших платформах, придбання спеціалізованих книг.
Менторинг з боку діючого розробника – найефективніший спосіб навчання, оскільки у вас завжди буде допомога, наставництво, поради, чіткий напрямок руху та необхідні навчальні матеріали.
Проходження онлайн/офлайн навчання при компаніях. Такий формат пропонують, наприклад, GlobalLogic, Ajax Systems тощо. Мінуси: потрібна певна база знань перед стартом навчання, а головний плюс – можливість стажування і повноцінного працевлаштування в компанії.
Вища освіта в цій спеціальності не завадить, хоч і не є обов’язковою. Потрібну базу ви можете отримати у школі та вищих навчальних закладах на відповідних технічних спеціальностях – там же є і можливість потрапити на стажування, якщо кафедра таке практикує або має відповідні зв’язки з компаніями.
Soft skills
Вимоги до Embedded-розробників можуть різнитися в плані стеку, але що 100% знадобиться кожному, це наступні “м’які” навички:
критичне, алгоритмічне, аналітичне мислення;
навички problem-solving;
комунікативність, вміння працювати в команді;
організованість, зібраність, вміння ефективно розпоряджатися часом;
самомотивація;
увага до деталей;
терпіння та адаптивність;
пристрасть до Embedded;
бажання постійно розвиватися та вдосконалюватися.
Останні три пункти дуже важливі в професії, оскільки ембедед – це непроста галузь, і за 3-6 місяців “увійти в ІТ” не вийде. Потрібно більше зусиль, часу та витримки.
Ключові тренди в Embedded
ІТ-індустрія рухається вперед, і ембедед не відстає. Проаналізувавши низку ресурсів, ми визначили наступні тренди:
Інтеграція штучного інтелекту та машинного навчання в Embedded-системах стає все більш привабливою, що сприяє використанню вбудованими системами інтелектуальних алгоритмів та ML. Ця синергія дозволяє системам адаптуватися до змін в реальному часі, підвищуючи їхню ефективність.
Розвиток безпровідних технологій відкриває нові перспективи для вбудованих систем, особливо в областях Інтернету речей (IoT) та безпілотних систем, які керуються віддалено або за допомогою AI.
System-on-chip рішення. Інтеграція всіх ключових компонентів системи на одному чіпі (SoC) продовжує набирати оберти та використовуватися різними бізнесами. Їхня компактність в сукупності з високою продуктивністю, дешевизною, енергоефективністю та інтегральними мікросхемами спеціального призначення (ASIC), наприклад, вже успішно застосовується в гаджетах, які збирають та аналізують різні параметри здоров’я.
Більше уваги безпеці. Зростання кількості вбудованих систем, які мають вихід в Інтернет, призводить до збільшення кількості кіберзагроз, що підкреслює важливість удосконалення заходів безпеки для ембедед-пристроїв.
Оптимізація споживання енергії embedded-системами стає актуальною – на це впливають ідеї сталого розвитку, що сприяє поширенню більш енергоефективних рішень.
Розвиток AR та VR (доповнена та віртуальна реальності). Інтеграція вбудованих систем у AR та VR відкриває нові горизонти для інтерактивних інтерфейсів та розважальних застосунків. Embedded-розробники активно працюють над цими технологіями для створення вражаючого досвіду та візуальних ефектів.
Окремо зазначимо і розвиток вбудованих пристроїв у військовій сфері. Наразі зростає попит на ембеддерів, які здатні працювати з дронами (повітряними та морськими), БПЛА тощо. Сьогодні саме ці роботизовані системи дозволяють ефективно виявляти та ліквідовувати ворога, здійснювати розвідку, наводити та корегувати вогонь і – що найважливіше – берегти життя та здоров’я особового складу. Сьогодні це один з трендових напрямків конкретно в нашій державі.
Кар’єрні можливості в ІТ та заробітна плата Embedded Engineer
Кількість вакансій
Для визначення цього параметру використаємо дані з найвідоміших українських ресурсів для пошуку роботи в ІТ – DOU та Djinni – і порівняємо результати з найпопулярнішими спеціальностями.
Почнемо з DOU:
Embedded – 44 вакансії;
QA – 167;
FrontEnd – 160;
DevOps – 158;
PHP – 140;
Python – 138;
Java – 108;
.NET – 104;
Node.js – 101.
На Djinni пошук роботи кандидатами здійснюється анонімно. Кількість вакансій наступна:
C / C++ / Embedded – 170 вакансій;
JavaScript / FrontEnd – 354;
PHP – 295;
DevOps – 273;
Python – 208;
Node.js – 191;
C#/.NET – 166;
Java – 161;
QA Automation – 139;
QA Manual – 128.
Конкуренція
Для визначення конкуренції ембедерів на нашому ринку ІТ скористаємося співвідношенням кількості відгуків до кількості вакансій на DOU за листопад 2023 року (беремо найбільш відомі спеціальності). Ось що маємо:
Конкуренція у ембеддерів (від новачків до професіоналів) дуже низька: на 1 вакансію припадає приблизно 5 претендентів, що є дуже гарним результатом під час нинішньої ситуації на ринку ІТ.
Справжні “голодні ігри” традиційно у QA та FrontEnd: за 1 вакансією полюють в середньому 120.9 та 100.5 кандидатів відповідно.
Відповідно до ринкової статистики Djinni, для новачків конкуренція посилюється:
Зазначимо, що тут демонструється кількість кандидатів на вакансію, котрі зараз перебувають в “онлайні”. На Джині так позначаються ті кандидати, які знаходяться в активному пошуку роботи.
Бачимо, що в категорії “C / C++ / Embedded” без досвіду 27.75 шукачів на вакансію (окремої категорії для Embedded немає). Багатенько, але подивіться на аналогічну ситуацію в більш популярних напрямках:
JavaScript / FrontEnd – 426.55
C#/.NET – 200.5
Python – 185.6
Manual QA – 132.83
Node.js – 98
DevOps та FullStack – 83.5
Заробітна плата
Спершу розглянемо зарплатну статистику на Djinni для “C / C++ / Embedded” (за останні 6 місяців). Картина наступна:
Зарплатні вилки досить високі – 2300-4700 USD. При цьому зарплатні очікування кандидатів скромніші – від 700 USD до 4500 USD.
А скільки отримують Embedded за межами України? Звернімося до аналітики Stack Overflow Developer Survey 2023 (більше 90 000 респондентів із США, Індії, Німеччини, Сполученого Королівства, Канади тощо).
Деякі несуттєві для нашої статті спеціальності були прибрані з діаграми задля більшої компактності зображення.
Отже, медіанна зарплата Embedded-спеціалістів з різних країн складає 77 104 USD на рік, а це приблизно 6425 USD на місяць. Що цікаво, розробники вебу (FrontEnd, BackEnd, FullStack), ігор, мобільних та десктопних застосунків отримують меншу ЗП, хоча ці спеціальності більш поширені, а веб взагалі є найпопулярнішим в ІТ.
Вищу винагороду отримують ті, хто спеціалізується на базах даних, DevOps-процесах, великих даних, апаратній частині, блокчейні, безпеці тощо.
Кар’єрні перспективи
Embedded розробник – поняття досить розмите і обширне, оскільки фахівець цієї спеціалізації може працювати над:
створенням робототехнічних систем;
медичною апаратурою;
автомобільними системами;
аерокосмічними апаратами;
IoT-екосистемою;
AR та VR індустрією;
мережевими системами;
гаджетами для цивільних потреб;
безпекою embedded-пристроїв;
промисловими системами;
військовими розробками;
тощо.
В кожній із цих сфер будуть свої технологічні стеки, мікроконтролери, плати, інтерфейси, протоколи і т. д.
Ще не забувайте про ембедерський “фронтенд”, “бекенд” і “фулстек” – в кожній галузі ви можете концентруватися більше на програмній частині, апаратній, або ж на обох одразу.
Звісно, кожен напрямок може мати власну “кухню”, і щоб “скуштувати страву” в кожному із цих “ресторанів” цілого життя може не вистачити, але погодьтеся – список для самореалізації скромним не назвати.
Також ви можете в будь-який момент заглибитися в Software розробку і займатися написанням програмного коду (найпопулярніші стеки в Embedded-і – C/C++, Java, Python, Go), а можете піти у Hardware і більше уваги приділяти “залізу”. Якщо трохи підкачати знання, то можна перекваліфікуватися і в тестувальники, і в спеціаліста з безпеки, мережевого спеціаліста тощо. Можливості дуже широкі та дуже різноманітні.
Якщо ж говорити про вертикальний потенціал професії, розробник вбудованих систем стартує зі стандартної Intern/Trainee/Junior позиції. Далі – зі збільшенням досвіду, навичок та знань – отримує підвищення до Middle та Senior, а вище – керівні позиції на кшталт Team/Tech Lead, архітектор і т. д.
Висновки
Embedded – дуже цікава, незвична та багатогранна ІТ спеціалізація. Вона дозволяє відчути себе Архітектором, котрий вдихає життя в пристрої, дає їм все необхідне для їхнього руху, думок, аналізу навколишнього середовища, спілкування один з одним тощо. Так, для цього потрібно мати багато знань, але хто сказав, що бути Творцем – це так просто?)
Цей напрямок дає дуже важливий фундамент для інших ІТ-галузей, які сьогодні в зеніті популярності. Завдяки вбудованим системам ми можемо насолоджуватися безліччю автоматизованих процесів, починаючи ранковою свіжомеленою кавою та оглядом нашого сну у health-трекері, і закінчуючи автомобільною та медичною апаратурою.
Ембедед створений для справжніх любителів “заліза” та його програмування. При цьому він має багато нішевих розгалужень і щороку зростаючий попит. Сьогодні в Україні розробники вбудованих пристроїв мають великі можливості для реалізації, а в решті світу – тим більше.
Професія непроста, але вона щедро винагороджує сміливих, терплячих та цілеспрямованих, котрі не бояться складнощів та перешкод і готові на все, аби досягнути бажаного.
Бажаємо успіхів та натхнення всім, хто прагне розвиватися в напрямку Embedded!