Результати пошуку за запитом: Обучение c
Введення в розробку програм під 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”.
На этом урок завершен.
На следующем уроке мы продолжим разработку клиента, создадим контроллеры и представления для редактирования пользователей, подключим работу сервисов к странице входа в систему, создадим форму регистрации пользователя.
Розробка під Android - поради початківцям
Автор: Армен Маїлян
Овладейте языком
Хорошие навыки работы со средой разработки и другими правильными инструментами разработки
Знание компонентов приложения
Понимание фрагментации, Android-приложений, потоков, загрузчиков и задач
Правильный выбор необходимых инструментов разработки и последние советы
Вывод
Разработка приложений под различные мобильные платформы (Android, iOs) – это то, на что ориентируются многие опытные и начинающие разработчики программного обеспечения. Ведь именно приложения делают телефоны «умными» смартфонами. Благодаря своим преимуществам приложения кардинально изменили возможности и функции вчерашних «звонилок». Выбирая разработку под Android как целевую платформу, у нас есть выбор между Java и Kotlin - основными языками программирования для этой платформы. Сейчас мы не будем вдаваться в детали их различий, и отвечать на вопрос «на чем писать приложения для Android?». Мы уже затрагивали этот вопрос недавно в соответствующей статье «Kotlin vs Java: что лучше для Android-разработки?». Сегодня мы остановимся на Java. Попробуем сформулировать основные советы в разработке приложений под андроид для начинающих.
Овладейте языком
На сегодняшний день Java и XML являются двумя основными языками, используемыми при разработке приложений под Android. Поэтому знание и владение этими языками программирования и разметки является необходимым условием для разработки приложения под Android. Задав себе вопрос «с чего начать программирование под андроид?», вы получите достаточно простой ответ – изучите основной язык разработки и основы ООП.
Знания основ языка программирования Java должны включать в себя:
• Понимание и применение пакетов (Packages) в Java;
• Общее понимание ООП, понятия объектов и классов;
• Понимание механизмов наследования, понимание и умение работать с интерфейсами;
• Работа со строками и числовыми значениями, работа с дженериками;
• Понимание функционирования коллекций и работы с ними;
• Параллелизм.
Правильное понимание Java и XML поможет вам создать/разработать более надежное и элегантное приложение для Android.
Хорошие навыки работы со средой разработки и другими правильными инструментами разработки
Очень важно, чтобы, прежде чем приступить к полноценной разработке своего приложения, вы были хорошо знакомы с инструментами автоматизации сборки, а также с таким инструментом, как IDE - интегрированной средой разработки. В основном рекомендуется использовать Android App Studio IDE или Eclipse в качестве среды разработки. Применение их поможет вам изучить основы разработки и поможет вам улучшить качество вашего кода. Также советуем вам изучить такие механизмы как Apache Maven, Apache Ant и Gradle, поскольку они предоставляют собой мощный набор инструментов, помогающих управлять вашими сборками.
В процессе разработки важно, чтобы вы умели использовать инструменты и концепции контроля версий. Изучите git, а затем создайте репозиторий git-source (создав учетную запись в Bitbucket или GitHub). Чтобы получить представление об основных понятиях и условиях работы платформы, вы можете воспользоваться Git Pocket Guide.
Знание компонентов приложения
Компоненты приложения — при разработке андроид-приложений выступают в роли основных строительных блоков. В свою очередь каждый из таких блоков представляет из себя отдельную точку, с помощью которой в ваше приложение может войти система. Хотя каждый из компонентов существует как отдельная сущность и играет свою отдельную роль, есть ряд компонентов, которые зависят друг от друга. При этом не все из них окажутся фактическими точками входа.
Среди компонентов приложения выделяют пять разных типов, каждый из которых выполняет определенную роль и имеет свой собственный жизненный цикл, согласно которому он будет создаваться и уничтожаться. Они включают:
Операции (Activity): это компонент андроид, представляющий один экран с пользовательским интерфейсом (к примеру, приложение для работы с электронной почтой может иметь одну Activity, отображающую список входящих писем, другую Activity - составляющую e-mail, и третью - читающую эти письма). Операции работают вместе, чтобы сформировать единый пользовательский опыт в андроид-приложении. Несмотря на это, каждая из Activity является независимой.
Службы: компонент приложения в андроид, работающий в фоновом режиме и обеспечивающий выполнение длительных Activity и удаленных процессов. Этот компонент пользовательский интерфейс не предоставляет (например, пока пользователь обращается к интерфейсу другого приложения, он может проигрывать музыку в фоне).
Поставщики содержимого или Content providers: компонент андроид-приложения, управляющий общим перечнем данных приложения. Используя этот компонент данные, хранимые в базе данных SQLite, в Интернете или файловой системе, могут быть запрошены или изменены (если допускает поставщик содержимого). Этот компонент также применим как для записи, так и чтения тех данных, что являются частными, а не общими для вашего приложения.
Широковещательные приёмники или Broadcast receivers: компонент андроид-приложения, отвечающий на широковещательные сообщения, общие для системы. Большая часть таких сообщений происходят от системы. Broadcast receivers могут создавать в строке состояния уведомления, которые предупреждают пользователя, когда происходит широковещательное событие, хотя и не имеют пользовательского интерфейса. Как правило такой приёмник выполняет только минимальный объем работы, выступая для других андроид-компонентов в роли шлюза.
Активация компонентов: асинхронное сообщение, также имеющее название - намерение (Intent). Активирует 3 из 4 компонентов (то есть сервисы, Broadcast receivers и Activity). Intents также связывают отдельные компоненты друг с другом во время выполнения, вне зависимости от того, принадлежит ли вашему приложению данный компонент или нет.
Понимание фрагментации, Android-приложений, потоков, загрузчиков и задач
Android — это фрагментированный рынок с множеством видов устройств и различных версий операционной системы. Обратите внимание, что, чем больше ваше устройство поддерживает различных устройств и/или версий, тем больше оно требует обслуживания и тестирования и, соответственно, больше будет сопутствующих расходов. Вам также потребуются соответствующие шрифты, ресурсы и макеты, которые помогут обеспечить наилучшее взаимодействие с различными характеристиками экрана. Вы придется держать во внимании всё множество поддерживаемых Android датчиков и средств пользовательского интерфейса.
Распределение версий Android по данным statista.com
Иногда, для выполнения фоновых задач, вам приходится использовать службы, которые должны выполняться непрерывно. Но, в ряде случаев, применение их может оказаться невозможным. Если вы хотите создать отличный и удобный пользовательский интерфейс, всегда следите за тем, чтобы поток никогда не блокировался. Поэтому длинные операции (вычисления, ввод-вывод, сеть и т. д.) должны выполняться асинхронно в фоновом режиме (в основном в другом потоке выполнения). Вот почему важно изучить средства параллелизма языка Java.
Правильный выбор необходимых инструментов разработки и последние советы
Что нужно для создания приложения на андроид? Чтобы начать программировать под Android, вам подойдут весьма простые инструменты — это персональный компьютер с Mac или OS Windows, Linux. Сами же инструменты разработки ( IDE Eclipse, плагин ADT и Android SDK) – распространяются бесплатно.
Android имеет некоторые уникальные параметры, которые вам следует учитывать при написании приложений под Android. Среди них:
Производительность и скорость отклика: вы всегда должны реагировать на ввод пользователя в течение пяти секунд, в противном случае приложение выдаст ошибку ANR (ANR: application not responding - приложение не отвечает). Единственный доступный вариант - принудительно закрыть приложение.
Пользователи заметят лаги более 100 мс: как уже упоминалось выше, поток пользовательского интерфейса никогда не должен блокироваться, потому что он только один.
Ограниченные ресурсы: Wake-Lock (механизм, который заставляет устройство выполнять определенные действия, несмотря на рекомендацию менеджера батареи перевести устройство в спящий режим), следует использовать с осторожностью. Не обращайтесь лишний раз к элементам оборудования (например, GPS или акселерометру), потому что такие действия быстро разряжает аккумулятор.
Вывод
Мы с вами рассмотрели ряд простых советов для тех, кто начинает программировать под андроид. Надеемся, что данные рекомендации помогут вам создавать новые высококачественные приложения.
Если вы только раздумываете над направлением, в котором вам следует развиваться – рекомендуем посмотреть видео «Как стать Android разработчиком». В этом видео вы также сможете получить подходящие для вас ответы на вопрос «как начать программировать под Android?». Если вы уже определились с направлением развития и желаете получать знания в сфере разработки под Android - рекомендуем вам ознакомиться с нашим курсом подготовки Android Developer. Если вы уже имеете определенные навыки в разработке и ищете дополнительных знаний по отдельным технологиям - вам наверняка будет полезен курс «Автоматизация сборки проектов с помощью Apache Maven».
В свой очередь портал ITVDN.com желает вам успехов в обучении, интересных проектов и высокой зарплаты.
По материалам статьи от Эшна Верна.
Soft skills, необхідні для Senior-розробника
Автор: Редакція ITVDN
Каждый амбициозный разработчик со временем понимает, что условная «лычка» Senior — это лишь один из этапов в карьере программиста. Однако, для тех, кто планирует начать свой профессиональный путь в IT, получение данного «звания» является чуть ли не главной целью или даже мечтой. Почему?
Если кратко, то этот ранг в карьере разработчика означает следующее:
Можно смело претендовать на зарплату в 3 000 — 4 000 долларов в месяц и не соглашаться на меньшее. И вы гарантированно получите нужную вам сумму, ведь спрос на людей с вашими скилами значительно превышает предложение.
Вы больше не ищете работу – работа ищет вас. HR-менеджеры сами будут выходить с вами на связь и пытаться заманить на собеседование, предлагая максимально выгодные условия.
Вам станут поручать самые интересные и важные задачи. Ведь компании не выгодно, чтобы такой ценный специалист сидел без дела или занимался примитивной рутинной работой, с которой справится любой новичок.
Хорошо показавший себя в деле Senior легко может перейти на еще более высокую ступень в иерархии, став, к примеру тимлидом (англ. Team Lead). А это не только внушительная прибавка к зарплате, но и бесценный опыт управления командой, который впоследствии очень пригодится девелоперу, если он попробует создать собственный IT-бизнес.
Что меняется с получением звания «сеньор»
Люди, занимающие позиции Middle и Junior, являются «рядовыми бойцами». Каждого из них можно заменить новым сотрудником в любой момент без особого ущерба для общего дела. А вот Senior — это связующее звено между руководителями и командой. Руководство по умолчанию ожидает, что бывалый спец будет наставлять и контролировать новичков. Если джуны и Trainee что-то сделают не так, то за это в том числе будут спрашивать и с того, кто им не помог.
Кроме того, опытный разработчик должен отстаивать права соратников по цеху перед заказчиками и менеджментом. Начальник вынужден считаться с мнением ценного профессионала, поэтому от него в определенной степени зависит, насколько комфортно будет работаться не только ему самому, но и его коллегам.
Какими качествами обязан обладать Senior
Очевидно, что специалисту такого ранга следует не только быть крайне компетентным в своей сфере, но и обладать определенными личными качествами, наличие которых важно для любого лидера. Ведь плох тот профи, который не способен занять должность Project Manager, когда понадобится. А для этого необходимо уметь давать задания членам команды и сбалансированно распределять между ними нагрузку, быть инициативным и коммуникабельным, эффективно справляться с функциями ментора. Рассмотрим каждый из этих навыков подробнее.
Коммуникабельность
Когда ведущий специалист общается с коллегами и работниками других отделов, каждое его слово имеет значительный вес и может влиять на успех проекта. Будучи разработчиком высокой квалификации, вы не можете ограничиваться контактами с непосредственным начальником и ближайшими напарниками. Вам нужно более глубоко понимать структуру функционирования всей компании и в общих чертах быть в курсе процессов, протекающих в каждом отдельном подразделении. Поэтому надо наладить деловую коммуникацию с дизайнерами, HR, тестерами, маркетологами, сисадминами и вообще со всеми, с кем только можно.
Представим себе, что произошла критическая ситуация. И вам срочно понадобилась помощь работника другого отдела. Если ранее вы не поддерживали связь с этим человеком, то вам будет сложно донести до него, что сейчас ему непременно следует бросить текущие дела и заняться решением новой проблемы.
Главное — не переусердствовать. Для того, чтобы наладить деловое общение, не нужно вести душевные беседы и постоянно участвовать в корпоративных развлекательных мероприятиях на выходных. Достаточно быть приветливым и вежливым, знать имена всех соратников по цеху, а также иметь представление об их профессионализме и о том, над чем конкретно каждый из них работает в данный момент.
Инициативность
Современный IT-бизнес — это отнюдь не тот случай, когда инициатива наказуема. Разумеется, тут играет роль и компетентность начальства, но истинный профи по определению обязан иметь собственное мнение и предлагать оптимальные варианты достижения поставленных целей. А если менеджмент требует сделать нечто такое, что, по-вашему, является бесполезным или даже вредным, выразите несогласие — спокойно и аргументированно.
Вы видите процесс изнутри, поэтому можете заметить то, на что не обратил внимания Project Manager. Возникли рационализаторские идеи? Сообщите их руководителю. Возможно, вы сделаете работу более комфортной и интересной для всего коллектива, а также принесете компании дополнительную прибыль. Разумеется, вам придется преодолеть некоторые психологические барьеры. Но справиться с ними не так уж и сложно. Осознайте, что «лычка» присвоена вам не просто так, а именно потому, что вы обладаете экспертными знаниями. Если предложение будет отклонено менеджером, то это никак не отразится на зарплате. А вот если его примут, то вас вполне могут поощрить премией.
Менторство
Во многом именно от сеньора зависит, насколько быстро новичок адаптируется в коллективе и сумеет ли он полноценно включиться в работу. Опытный коллега должен объяснить джуниору или даже миддлу все тонкости проекта, ввести его в курс дела. Пожалуй, почти все специалисты высокого уровня понимают, что им необходимо выполнять обязанности менторов, но всячески этому противятся. С одной стороны, их можно понять, ведь хотелось бы заниматься исключительно своим фронтом работ и не отвлекаться на чужие проблемы.
Но есть смысл обратить внимание и на такие стороны менторства:
Если вам не хватает коммуникабельности или уверенности, то роль наставника — отличный способ развить в себе эти качества.
Когда человек объясняет сложные вещи простыми словами, это помогает ему самому понять для себя что-то новое, иначе увидеть проблему, прийти к неожиданным для себя выводам.
Этот опыт будет очень полезен для вас и в том случае, если вы планируете когда-нибудь начать вести научную или преподавательскую деятельность, основать собственную компанию, запустить обучающие курсы.
Вряд ли вы хотите всю жизнь оставаться на одной и той же позиции. А тем, кто не желает быть ментором, продвигаться выше по карьерной лестнице крайне сложно.
Умение четко формулировать мысли, расставлять приоритеты и распределять нагрузки
Сеньор отвечает не просто за какие-то отдельные задачи. На нем держится весь проект. От него часто требуют то, с чем нельзя справиться в одиночку. Таким образом, начальство почти всегда ставит лучших спецов в такие условия, при которых им приходится перепоручать что-то младшим сотрудникам. И здесь крайне важным становится умение точно, лаконично излагать суть того, что вы хотите получить от человека.
Для того, чтобы дать выполнимое задание, потребуется:
Определить реальную степень компетентности сотрудника. Если уровень его знаний на порядок ниже вашего, учитывайте это при формулировании требований.
Подать информацию в предельно простой форме. Разбейте таск на отдельные блоки, укажите оптимальную последовательность их выполнения.
Попытаться представить себя на месте исполнителя. Какие вопросы или сложности могут у него возникнуть? Может быть, его ожидают какие-то «подводные камни»? Разъясните эти моменты заранее.
Также надо быть готовым ответить на любые, даже самые глупые вопросы, растолковать те моменты, которые непонятны джуниору.
Что же касается распределения нагрузок, то здесь необходим индивидуальный подход. То, с чем прожженный профессионал справится за каких-нибудь 30 минут, а Middle — за 2 часа, у джуна может занять целый день, а то и больше. Со временем он научится делать это быстрее, но сейчас вам следует учитывать его работоспособность, чтобы не допустить срыва установленных сроков.
Как ITVDN может помочь вам в достижении ваших целей
Мы создали мощный обучающий ресурс, который поможет вам стать успешным программистом. На сайте https://itvdn.com собраны более 150 курсов по самым актуальным и востребованным направлениям IT обучения. Здесь вы не только найдете видеоуроки и мастер-классы от гуру программирования, но и сможете участвовать в интерактивных вебинарах, оттачивать свои навыки на онлайн-тренажерах, проходить тестирование и подтверждать свои знания сертификатами.
Изучение востребованных языков программирования, верстка сайтов, создание мобильных приложений и игр — все это и многое другое дается намного проще, когда под рукой есть доступ к базе знаний ITVDN. Не имеет значения, кто вы — джуниор, миддл, профи или же только планируете свой путь в IT. У нас обязательно найдутся для вас актуальные, полезные и интересные видео курсы.
Спеціальність – Front-end розробник
Автор: Редакція ITVDN
Давайте разберемся, кто такой Front-end разработчик и какими технологиями он должен владеть, чтобы быть востребованным специалистом.
Возможно, Вы когда-нибудь, глядя на ваш любимый веб-сайт, задавались вопросом, насколько это сложно сделать?
Веб разработка состоит из 2-х основных больших частей – клиентской и серверной, которыми занимаются – frontend и backend-разработчики.
В самом упрощенном виде: если backend создает функционал, то frontend занимается в первую очередь тем, что видит пользователь, зайдя на сайт – все эти красивые страницы, кнопочки, картинки.
Но не все так просто…
Frontend-разработчики создают те вещи, с которыми будут взаимодействовать люди. Все этапы разработки проходят вместе с frontend-разработчиком. То есть frontend-разработчик должен быть в курсе всего!
Строго говоря, Frontend — публичная часть веб-сайтов и веб-приложений, с которой непосредственно контактирует и взаимодействует пользователь. Во Frontend входят отображение пользовательского интерфейса, функционал, выполняющийся на стороне клиента, и обработка пользовательских запросов.
Знания и навыки Frontend-разработчика:
Frontend-разработчик должен разбираться в дизайне.
Если frontend-разработчик не является сам по себе дизайнером, он должен знать, насколько важен дизайн. Он должен иметь хороший вкус. Он должен знать об инструментах, участвующих непосредственно в разработке.
Frontend-разработчик должен разбираться в работе серверной части (backend).
Frontend-разработчик должен явно осознавать всю важность серверной части, понимать, с чем взаимодействует backend, что передается на сервер, а что нет, должен уметь объяснить, что должен дать вам backend и что нужно от серверной части frontend-а.
Frontend-разработчик должен разбираться в производительности.
Frontend-разработчик знает, что производительность имеет важное место в успехе проекта. Необходимо понимать, насколько быстрым должен быть backend, а также что оставшиеся 80% времени - это загрузка сайта, т.е. это frontend.
Frontend-разработчик должен разбираться в мобильном дизайне.
Frontend-разработчик должен понимать, что его сайтом могут пользоваться везде, на его сайт могут зайти с любого устройства, поэтому необходимо позаботиться заранее на этот счет. Большие экраны, маленькие, сенсорные, устаревшие устройства. Frontend-разработчик должен быть готов к неизвестному!
Это всего лишь часть того, что должен знать frontend-разработчик. Чем больше, тем лучше.
И начинать изучать лучше сразу правильно. Переучиваться – это всегда проблематично!
Какие же технологии предстоит изучить frontend-разработчику?
HTML и CSS
HTML (язык разметки гипертекста) и CSS (каскадные таблицы стилей) являются основными строительными блоками веб-кодирования. Без этих двух вещей вы не можете создать дизайн веб-сайта, и все, что у вас в итоге получится, - это неформатированный простой текст на экране. Вы даже не сможете добавлять изображения на страницу без HTML!
Прежде чем приступить к освоению карьеры в Интернете, вам нужно освоить верстку с помощью HTML и CSS.
JavaScript
JavaScript позволяет добавить к вашим веб-сайтам больше функциональности. Вы сможете создать множество базовых веб-приложений, используя не что иное, как HTML, CSS и JavaScript. На самом базовом уровне JavaScript позволяет добавлять на ваши сайты множество интерактивных элементов.
Это также самый популярный язык программирования в мире, поэтому, независимо от ваших планов карьерного роста, очень желательно иметь хотя бы базовые знания JavaScript.
JQuery
jQuery - это библиотека JavaScript: коллекция плагинов и расширений, которая позволяет быстрее и проще разрабатывать JavaScript. Вместо того, чтобы писать все с нуля, jQuery позволяет добавлять готовые элементы к вашим проектам, которые затем можно настроить по мере необходимости. Вы можете использовать jQuery для таких вещей, как таймеры обратного отсчета, автозаполнение формы поиска и даже автоматическое перераспределение и изменение размеров макетов сетки.
JavaScript фреймворки
Фреймворки JavaScript (включая Angular, Backbone, Ember и React) дают готовый дизайн вашему JavaScript-коду. Существуют различные типы фреймворков для разных потребностей. Они действительно ускоряют разработку, предоставляя вам быстрый запуск и могут использоваться с такими библиотеками, как jQuery.
Frontend Frameworks
CSS и интерфейсные рамки (самая популярная инфраструктура front-end - Bootstrap) делают для CSS то, что JS Framework делают для JavaScript: они дают вам точку перехода для более быстрого кодирования. Так как CSS начинается с точно таких же элементов из проекта в проект, фреймворк, который определяет все это для вас, является очень ценным. Как правило предполагается, что вы будете знакомы с тем, как эти структуры работают и как их использовать.
Работа с препроцессорами CSS
Препроцессоры - еще один элемент, который может ускорить кодирование CSS. Препроцессор CSS добавляет дополнительные функциональные возможности для CSS, чтобы поддерживать масштабируемость и удобство работы нашего CSS. Он обрабатывает ваш код, прежде чем публиковать его на своем веб-сайте, и превращает его в хорошо отформатированный и кросс-браузерный CSS. SASS и LESS являются двумя наиболее востребованными препроцессорами.
Практичный и мобильный дизайн
Например, когда веб-сайт посещается с настольного компьютера с большим монитором, пользователь получает несколько столбцов, большую графику и взаимодействие, созданные специально для пользователей мыши и клавиатуры. На мобильном устройстве один и тот же сайт будет отображаться как один столбец, оптимизированный для сенсорного взаимодействия, но с использованием тех же базовых файлов.
Мобильный дизайн может включать в себя гибкий дизайн, а также создание отдельных мобильных проектов. Иногда то, что пользователь видит на вашем сайте на настольном компьютере, совершенно отличается от того, что вы можете видеть при посещении его со смартфона.
Кросс-браузерная разработка
Современные браузеры очень хорошо демонстрируют веб-сайты последовательно, но по-прежнему существуют различия в том, как они интерпретируют код за кулисами. Пока все современные браузеры не будут работать с веб-стандартами, знание того, как заставить каждого из них работать так, как вы хотите, является важным навыком. Это кросс-браузерная разработка.
Системы управления контентом и платформы для электронной коммерции
Очень многие веб-сайты построены на системе управления контентом (CMS). (Платформы электронной коммерции - это особый тип CMS.) Наиболее популярной CMS во всем мире является WordPress, которая является закулисной частью миллионов веб ресурсов. Почти 60% сайтов, использующих CMS, построены на WordPress.
Тестирование и отладка
Модульное тестирование - это процесс тестирования отдельных блоков исходного кода (инструкции, которые сообщают веб-сайту о том, как он должен работать), а рамки модульного тестирования предоставляют конкретный метод и структуру для этого.
Другим распространенным типом тестирования является тестирование пользовательского интерфейса (также называемое приемочным тестированием, тестированием браузера или функциональным тестированием), где вы проверяете, что веб-сайт ведет себя так, как должен, когда пользователь действительно предпринимает действия на сайте. Отладка просто берет все “ошибки”, выявленные этими тестами.
Системы контроля версий Git и Version
Системы контроля версий позволяют отслеживать изменения, которые были внесены в код с течением времени. Они также позволяют легко вернуться к более ранней версии, если вы что-то придумаете. Итак, скажем, вы добавляете настроенный плагин jQuery и вдруг половину вашего разрыва кода. Вместо того, чтобы пытаться вручную отменить его и исправить все ошибки, вы можете вернуться к предыдущей версии, а затем повторить попытку с помощью другого решения.
Git является наиболее широко используемым из этих систем управления версиями. Знание того, как использовать Git, будет требовать практически любая работа. Это один из тех важных навыков, которые необходимы разработчикам, но об этом мало кто говорит.
С чего начать?
Если все сказанное выше звучит довольно интересно для вас, вы, вероятно, задаетесь вопросом, с чего начать. Если вы видите себя на работе в качестве Frontend-разработчика, но не знаете, где получить необходимые знания и навыки, вы находитесь в нужном месте. На ITVDN вы найдете полную подборку видео курсов по специальности Frontend разработчик. Начните с HTML и CSS!
Смотрите первый урок видео курса HTML5, CSS3 бесплатно.
Если Вы сторонник традиционных форм обучения, приглашаем Вас на курс FrontEnd Developer в CyberBionic Systematics.
Упаковка та розпакування в .NET
Автор: Редакція ITVDN
Мы уже знаем особенности работы с памятью и доступные структуры данных в .NET приложениях, в этом посте мы разберем упаковку и распаковку, а также рассмотрим, как эти две операции влияют на производительность приложения.
Что такое упаковка и распаковка?
Зачем нам задумываться об упаковке и распаковке? Разве это не обязанность .NET-среды, которая следит за управлением данных и, соответственно, сама "выбирает" наиболее оптимальный способ их хранения?
На самом деле - нет. Что очень важно знать и понимать - так это механизм перемещения данных из области стека в кучу - и наоборот.
Помните:
Когда любой значимый тип присваивается к ссылочному типу данных, значение перемещается из области стека в кучу. Эта операция называется упаковкой.
Когда любой ссылочный тип присваивается к значимому типу данных, значение перемещается из области кучи в стек. Это называется распаковкой.
К примеру, здесь мы имеем следующий пример упаковки:
А вот состояние памяти в момент произведения операции:
Чтобы сохранить значение "123" в виде объекта, в куче создается "упаковка", куда впоследствии и перемещаются данные.
Когда же производится распаковка:
Вот что происходит с памятью:
Значение "123" было изъято из упаковки и помещено назад в область стека.
Заметьте, что когда тип данных i упаковывается внутри объекта o, в стеке хранится лишь ссылка, в то время как само значение хранится в куче. Как только производиться распаковка, данные в куче обязаны быть скопированы в стек (переменная j). В обоих случаях наша цель - это работать с тем самым значением (123).
Как вы можете себе представить, сии операции могут быть достаточно ресурсоемкими.
Давайте рассмотрим IL
Когда мы производим подобный анализ производительности, часто бывает полезно заглянуть непосредственно в Intermediate Language (IL).
Мы еще не рассматривали эту концепцию, но, как вы наверняка знаете, когда мы производим компиляцию в DLL или EXE, выходной файл на самом деле содержит IL - промежуточный код, который в последствии исполняется JIT и впоследствии - виртуальной машиной. Среда выполнения .NET обязана как-то знать, нужно ли упаковывать или распаковывать определенные переменные. Поэтому для обозначения этих операций также требуются дополнительные затраты памяти.
Давайте создадим несложное .NET консольное приложение:
Теперь скомпилируем приложение и при помощи утилитки ILSpy посмотрим его код внутри EXE.
Как только EXE-файл будет открыт в ILSpy, пронавигируемся к методу Main, выбрав "IL with C#".
Заметьте, что операция box выполняется только после присвоение ссылочному типу значения значимого. И наоборот: unbox.any - только после попытки присвоить ссылочному типу данных значимой переменной.
Это де-факто способ, которым операции упаковки и распаковки представлены в IL.
Когда стоит производить упаковку и распаковку?
Код в примере выше скорее всего вам покажется наивным, и вы можете подумать: "Эй, что за вздор! Я никогда не буду такого делать". Что же, в большинстве случаев это действительно так. Но данные в нашем приложении часто упаковываются и распаковываются, когда мы об этом даже не догадываемся.
Гетерогенные коллекции
К примеру, старая школа до сих пор может похвастаться ArrayList.
Метод добавления элемента здесь, как можно отметить, принимает object-параметр.
Таким образом, и здесь производится наша излюбленная упаковка.
Впрочем, подобное кануло в лету с приходом обобщений и обобщенных коллекций.
Конкатенация строк
Другой интересный пример в виде конкатенации строк.
Эта операция требует наличия метода String.Concat, который принимает два object-параметра.
Дабы избежать подобных ситуаций, нам достаточно просто немного изменить код, используя на переменной типа int метод ToString (и здесь стоит проигнорировать сообщение ReSharper о том, что операция бессмысленна:) ).
И все! Никакой упаковки больше нет.
Вообще, это далеко не единичные примеры для демонстрации. Но цель нашей статьи - донести четкое представление о том, что такое упаковка и распаковка и когда они применяются.
Производительность
Как мы уже говорили, упаковка и распаковка требуют определенных затрат производительности. В случае с конкатенацией строк, выигрыш от применения ToString весьма незначителен. Именно потому, как я упомянул выше, даже ReSharper не советовал нам делать подобное:
В этом случае гораздо лучше сохранить читабельность кода без ToString.
Целесообразность оптимизации появляется, как правило, тогда, когда операции упаковки и распаковки предстоит производить в цикле сотни и тысячи раз. В этом случае время выполнения кода с упаковкой может составлять порядка 150 процентов от времени исполнения кода без нее (вы можете сами создать тестовое приложение и сравнить требуемый промежуток времени).
Упакованные значения могут также требовать больше памяти, чем значения в стеке. Копирование значений в/из стека также требует своих затрат. Согласно MSDN, упаковка может занимать порядка 20 раз больше времени, нежели простое присвоение. В то время как распаковка примерно в 4 раза медленней простого присвоения.
Итак... зачем же тогда вообще нужно использовать упаковку и распаковку?
Несмотря на все недостатки в плане падения производительности .NET -приложения, концепции упаковки и распаковки были внедрены в .NET не просто так. И вот причины:
.NET-стандарт обладает общей системой типов, что позволяет представлять и ссылочные. и значимые типы схожим образом - и все это благодаря упаковке.
Коллекции можно было использовать для хранения значимых типов до появления обобщений.
Упрощения кода, вроде конкатенации строк и так далее.
Упаковка и распаковка настолько распространены, что мы не может избежать их полностью. Мы должны знать принцип их работы, чтобы минимизировать их использование, но к этому нужно подходить разумно. Не тратьте свое время на постоянную оптимизацию кода, частую проверку через IL, чтобы убедиться, дабы ни одна лишняя операция упаковки не была использована. Помните, что чистота и простота чтения кода иногда значительно более важна, нежели незаметное, мельчайшее ускорение работы программы.
Подведем итоги
В сегодняшнем уроке мы рассмотрели, что такое упаковка и распаковка, как она представлена в IL-коде, и какое влияние на производительность они имеют. Искренне надеюсь, моя статья сумела прояснить некоторые общие концепции, хотя бы чуть-чуть. :)
В грядущих статьях мы рассмотрим механизм сборки мусора. Если у вас есть идеи или пожелания касательно материала новых статьей - милости просим в комментарии!
Автор перевода: Евгений Лукашук
Источник
Уразливості в .NET платформі
Автор: Редакція ITVDN
Необходимо ли вам знать об уязвимостях в .NET? Возможно, вы по-новому взглянете на код, а может, и на свою жизнь. А вдруг вам захочется сменить квалификацию и уйти в research новых видов атак? Кто знает. Но если вы задались вопросом об уязвимости, то вы наверняка интересуетесь тем, как разрабатывать надёжные и защищённые приложения.
Итак, давайте поговорим об уязвимостях, которые находят в .NET Framework.
DoS (Denial of service) – атака на ASP .NET MVC приложение. Эта атака способна серьезно нарушить работу вашего сайта, например, замедлить его, а в отдельных случаях и вовсе его заблокировать.
Как работает
На сервер производятся запросы, которые он не может обработать, в результате чего сервер не успевает обрабатывать запросы обычных посетителей и выглядит для них как неработающий.
Как исправлять
Упростить регулярное выражение
Заменить регулярное выражение на custom алгоритм
Задать timeout для обработки любого регулярного выражения, используя конструктор, или для всех регулярных выражений, используя AppDomain.
Это отличные примитивные меры, когда вам не очень хочется разбираться в том, какие у вас регулярные выражения и насколько они сложные. Тем более, что такие решения вам ничего не стоят, но дают результат.
Атака повышения привилегий (ЕоР) на SQL-сервер. В данном случае это luring атака, которая может скомпрометировать все данные на сервере.
Атака повышения привилегий (ЕоР) в ASP .NET Core 1.0. Эта уязвимость была зафиксирована как ошибка бизнес-логики, но позже выяснилось, что она может привести к раскрытию данных и повышению привилегий пользователя.
Как работает
Атакующий «заманивает» более привилегированный компонент, чтобы что-то сделать от его имени. Самый простой способ осуществить такую атаку – убедить цель запустить код атакующего в более привилегированном контексте безопасности.
Как исправлять
Единого сюжета, как избежать этих уязвимостей, к сожалению, нет. Можно дать лишь общие рекомендации. Например:
Зарегистрировать request-сервис в контексте синглтона
Дважды проверять любой код, работающий с security sensitive, c sandboxing и с модификацией данных
Runtime check – ещё один хороший способ защиты от подобных вещей
Минимум привилегий везде
Раскрытие информации ХХЕ - это уязвимость к XML-инъекциям, которая приводит к чтению произвольных файлов на атакуемом сервере.
Как работает
Чтобы понять суть ХХЕ, давайте сначала разберёмся с ХМL форматом. ХМL формат может содержать описание своей структуры в теге DOCTYPE. В этом же теге могут содержаться и некоторые Entity. Если говорить по-простому, то это аналог константы, который дальше может использоваться в ХМL коде и, при парсинге этого кода, будет автоматически раскрываться. Уже это обстоятельство может привести к DoS атаке, если вы добавите Entity, которые рекурсивно раскрываются, и не зададите timeout. Парсер начнёт их раскрывать бесконечно долго.
Классическая ХХЕ атака проходит следующим образом. Атакующий отправляет скомпрометированный ХМL файл на сервер, где этот файл парсится, после чего из сервера возвращается ответ. Ответ может быть совершенно любого типа, хотя существует вероятность, что злоумышленник получит именно тот локальный файл, который ему нужен.
Как исправлять
Запретить использование DTD файлов и External Entity
Занулить resolver
Ввести ограничение на размер и установку timeout
Одной из причин появления уязвимостей может быть десериализация бинарных данных. Десериализация данных – это обратный процесс сериализации. То есть если при сериализации мы переводим какую-либо структуру данных в последовательность битов, то при десериализации мы восстанавливаем начальное состояние этой структуры.
Как при этом процессе может возникнуть уязвимость? Допустим, у вас есть класс, реализующий команду, который запускает какой-то конвертор. Вы через конструктор передаете ему параметры этого конвертора и они валидируются в конструкторе. При этом использовать можно не каждый, а только ваш локальный конструктор.
В чём же проблема? А проблема состоит в том, что при десериализации данных конструктор не будет запущен. То есть ваши данные будут десериализованы так, как они есть, включая приватные поля.
Как исправлять
Использовать кастомный сериализатор
Использовать минимум привилегий в процессе десериализации
О возможных способах десериализации вы можете узнать больше в статье Джеймса Форшав «Are you my Type? Breaking .NET Trough Serialization».
Вывод
К всеобщему сожалению, сейчас не существует ни одного полностью защищённого веб-приложения. Поэтому проблема уязвимостей остаётся актуальной. Злоумышленники находят всё новые и новые способы взлома серверов и нанесения всяческого вреда компаниям или обычным людям. Следовательно, для гарантии безопасности вашего приложения вам необходимо знать, как устранять уязвимости и препятствовать их появлению.
Материал подготовлен на основе выступления Михаила Щербакова, Independent Consultant, на конференции по .NET-технологиям DotNext 2016 Moscow.
Паралакс для 2D гри без нервів та милиць
Автор: Дар'я Коновалова
Недавно в моей жизни начинающего разработчика игр появилась задача - сделать фон в игре, но не просто уныленький статичный бэкграунд, а параллакс. Да, эта чудо-красота применима не только в разработке сайтов, но и при создании игр. Попытка вдохновиться в гугле практически ничем не закончилась. Пришлось справляться с задачей собственными силами.
У меня получилось. Хочу поделиться с вами опытом.
Справедливо замечу, что параллакс — это не достояние веба. Еще в дремучие времена существования 8-битных игр параллакс успешно применялся для создания иллюзии объема в двухмерной игре. Коротко говоря, параллакс — это наслоение изображений, каждый слой движется со своей скоростью. Ближайший к игроку имеет самую высокую скорость, соответственно дальний (последний) — самую низкую.
Ну что, вроде, минимально в теории разобрались и даже нашли, откуда ноги растут, значит, мы готовы перейти непосредственно к практике и сотворить это чудо своими руками.
Что вам потребуется:
базовые знания Unity3D 5 (на уровне создания скриптов, добавления компонентов);
понимание С#;
3 или больше картинок в формате .png;
внимательность и желание.
Ладно, последнее не очень обязательно =)
По ссылке вы можете скачать необходимые изображения, а также уже готовый проект.
Подготовка
Запускаем Unity3D, создаем новый проект, называем его, например, Parallax2D.
Закидываем в папку Assets наши бэкграунды. Рекомендую сложить их в отдельную папку. В моем случае они лежат в Assets – StarSky.
Каждое изображение называем удобно и понятно. Я назвала их по порядку размещения (Background – задний фон, MiddleBackground – средний, TopBackground – верхний слой).
Для того, чтобы картинка перемещалась гладко, нам необходимо настроить ее в Inspector. Обратите внимание, этот этап очень важен, иначе все размажет, как звезды за иллюминатором Энтерпрайза на 3-й космической скорости.
В поле Texture Type выбираем тип Texture, во Wrap Mode отмечаем Repeat. И радостно тыкаем Apply. Без этого действия изменения не сохранятся, а потом можно долго недоумевать, почему же оно не работает. Совершаем эти телодвижения и для 2-х остальных текстур.
Подготовив картинки, переходим к этапу размещения их на сцене. Часто в этих ваших интерентах можно встретить совет - размещать их с помощью GameObject – Plane. Вы, конечно, можете потрудиться и заставить 3D объект нормально функционировать в 2D игре. Но, на самом деле, это все будет уныло, как последний эпизод «Звездных войн», а работать это чудовище будет чуть более быстро, чем аж никак. Поэтому я рекомендую долго не мучиться и использовать элемент UI – Canvas.
Canvas меняет размер фона автоматически, подстраивает его под размеры экрана гаджета, на котором запускают игру. Это избавляет нас от потребности писать скрипт, который будет отвечать за отслеживание размеров экрана и изменения размера фона.
В Hierarchy выбираем UI – Canvas. Собственно, если работать с Юнькой для вас не впервой, то вы явно знаете еще много других способов, как добавить в Hierarchy объект. Так что делайте это любым удобным способом.
Создав Canvas, добавляем чайлдами («внутрь» канвы) три Panel для 3-х наших фонов.
После добавления наша Hierarchy выглядит так:
Переименовываем Canvas и Panel, чтобы у всех были свои пароли и явки.
А теперь засучиваем рукава и беремся препарировать — подготавливать каждый компонент в Inspector.
Начнем с ParallaxBackground.
Изменяем Layer на Ignore Raycast, чтобы наш фон не реагировал на касания пальцами. Unity обязательно спросит, применить ли эти изменения ко всем «детям» — соглашаемся.
Далее переходим к компоненту Canvas.
Находим Render Mode, выбираем Screen Space – Camera. В Render Camera добавляем нашу текущую дефолтную камеру (Main Camera(Camera)).
Plane Distance ставим пока 110, но этот показатель проще отстроить во время теста. По факту — это расстояние от камеры до нашей канвы. То есть, изменяя его, мы будем получать разную глубину изображения.
Остальное не трогаем и переходим к Back.
В Rect Transform привязываем позицию к левому краю. Теперь наш фон будет всегда отстраиваться по одному краю, и мы избежим проблем с правильной позицией на разных устройствах.
Удаляем компонент Image (Script), вместо него добавляем Raw Image (Script) (напомню, Add Component – UI – Raw Image (Scriot)). В Texture добавляем картинку нашего самого последнего слоя.
Те же операции проделываем и для остальных слоев. Можно сделать немного проще, наколдовать изменения в первой Panel, дублировать (Ctrl + D), поставить в каждую свою текстуру, переименовать. Тут уже зависит от того, как вам будет удобнее.
Запускаем сцену — любуемся. Три картинки прекрасно легли друг на друга.
Немного черной магии
Теперь весь смак. Мы с вами напишем скрипт, который заставит наши картинки двигаться. Создаем новый скрипт, напомню, пишем на C#, назовем его BackgroundHelper. Он у нас будет один, поэтому нет смысла делать отдельную папку, кидаем его прямо в основную Assets. Открываем созданный скрипт и понеслась тяжкая работа на 5 строчек:
using UnityEngine;
using UnityEngine.UI; // обязательно добавляем библиотеку пользовательского интерфейса, без нее кино не будет
using System.Collections;
public class BackgroundHelper : MonoBehaviour {
public float speed = 0; //эта публичная переменная отобразится в инспекторе, там же мы ее можем и менять. Это очень удобно, чтобы настроить скорость разным слоям картинки
float pos = 0; //переменная для позиции картинки
private RawImage image; //создаем объект нашей картинки
void Start () {
image = GetComponent<RawImage>();//в старте получаем ссылку на картинку
}
void Update () {
//в апдейте прописываем как, с какой скоростью и куда мы будем двигать нашу картинку
pos += speed;
if (pos > 1.0F)
pos -= 1.0F;
image.uvRect = new Rect(pos, 0, 1, 1);
}
}
Сохраняем скрипт и добавляем его, как компонент к каждому слою.
Скорости у меня такие:
Back 0.0001
Middle 0.00015
Top 0.00024
Наслаждаемся успешной работой
Если все было сделано правильно, мы получим умопомрачительный эффект, от которого просто невозможно оторвать глаз.
У вас остались вопросы или возникли трудности? Пишите в комментариях.
Асинхронне програмування та співпрограми в Python
Автор: Олексій Орленко
Введение
В этом цикле статей будет рассмотрено асинхронное программирование при помощи сопрограмм в языке Python. В данной мы рассмотрим основные понятия и термины, которыми будем оперировать в дальнейшем, вкратце познакомимся с историей асинхронного программирования и состоянием дел в этой области на сегодняшний день. Также Вы узнаете о том, что такое сопрограммы и чем они могут быть полезны при написании кода в асинхронном стиле.
Во второй статье будет рассмотрена реализация сопрограмм при помощи расширенных возможностей генераторов в Python (PEP 342), в третьей мы рассмотрим модуль asyncio (PEP 3156), который стал частью стандартной библиотеки в Python 3.4 и доступен в виде отдельного пакета для Python 3.3, а четвёртая статья цикла будет посвящена асинхронным функциям и сопрограммам в Python 3.5 с использованием нового синтаксиса async/await (PEP 0492).
Понятие асинхронного программирования и сопрограмм
Наверное, сегодня все уже слышали о Node.js и знают причины возрастания его популярности: один язык для фронтенда и бекенда (что в рамках данной статьи нас не интересует) и то, что он является платформой для построения асинхронных неблокирующих веб-серверов.
Другой известной технологией, основанной на данной модели, является веб-сервер nginx, который часто используется на высоконагруженных проектах, занимая первое место по частоте использования среди 10000 самых посещаемых сайтов в мире (согласно данным W3Techs).
Так что же такое асинхронное программирование и почему оно становится таким популярным, особенно в highload-проектах?
На самом деле, асинхронное программирование существовало ещё на заре вычислительной техники, так как было важно максимально использовать аппаратные ресурсы машины. Но не так давно оно стало чуть ли не стандартной парадигмой программирования, настолько, что можно сказать, что большинство написанных в наши дни приложений являются асинхронными объектно-ориентированными программами.
Давайте для наглядности рассмотрим это на примере графических интерфейсов пользователя. Что происходит, когда пользователь не производит никаких действий? Ничего. Программа должна ждать, пока пользователь укажет ей, что делать.
Это ожидание можно реализовать в виде постоянных проверок: «а не нажал ли пользователь на кнопку?», «а не поставил ли пользователь курсор в поле ввода?». Таким образом, вычислительные ресурсы тратятся просто на то, чтобы проверить, не случилось ли что-нибудь. К счастью, практически все UI-фреймворки построены иначе. Они реализуют систему обработки событий. Любое действие пользователя – это событие, и разработчик может привязать к нему код – обработчик события.
Это настолько привычный паттерн, что многие разработчики даже не задумываются, как он работает, хотя следовало бы. Например, представьте, что на экране есть три кнопки и пользователь каким-то образом нажимает на них практически одновременно. Запустятся ли три разных обработчика событий?
Как правило, ответ – нет. Наиболее распространённая архитектура системы обработки событий – однопоточная, лишь с одним потоком исполнения.
Однопоточные системы обработки событий практически всегда реализуются при помощи очереди событий или сообщений. Идея состоит в том, что, когда программа ничего не делает, её поток смотрит на наличие новых событий в очереди. Когда происходит событие, оно добавляется в очередь и, если поток исполнения в это время не делает ничего другого, он забирает следующее событие из очереди и выполняет соответствующий обработчик.
Таким образом, события добавляются в очередь и UI-фреймворк предоставляет диспетчер, который выполняется в том же потоке, что и обработчики, вызывая их по мере надобности. В любой момент времени поток находится либо в каком-то обработчике события, либо в диспетчере, ожидая следующего события.
Возникает логичный вопрос: каким же образом событие попадает в очередь, если поток исполнения занят обработкой другого события? Дело в том, что у операционной системы много потоков и тот код, который действительно взаимодействует с пользователем, выполняется отдельно от нашей программы и лишь посылает ей сообщения.
Это пример асинхронной системы, так как мы не знаем, в каком порядке будет выполнятся код. Обработчики событий, с точки зрения программы, могут выполняться произвольно.
Но в данной модели обработчики событий являются неделимыми действиями. И тут возникает проблема: если обработчик событий выполняется слишком долго, интерфейс как бы «подвисает». Причина в том, что пока обработчик события не вернул управление в диспетчер, следующий обработчик не будет выполнен.
Решением является минимизировать количество работы, которое выполняет обработчик события. Но что если ему требуется совершить какие-то вычисления или загрузить данные с сервера? Очевидный ответ – выполнять обработчик этого события в отдельном потоке. Однако в JavaScript есть лишь один поток исполнения, а в Python, как известно, проблемой многопоточных приложений, которая значительно их замедляет, является Global Interpreter Lock (GIL).
Тут мы подходим к тому, что существует два вида многозадачности: вытесняющая и кооперативная.
Потоки и процессы используют вытесняющую многозадачность. Это значит, что операционная система производит квантование времени и постоянно переключается между разными потоками, сохраняя и восстанавливая их контекст выполнения.
При использовании кооперативной многозадачности ветви кода, которые исполняются параллельно, сами отдают управление в определённые моменты времени. Кооперативная многозадачность как способ одновременного выполнения отдельных программ устарела и не используется в современных операционных системах, однако, идеи, заложенные в неё, оказываются очень полезными для организации выполнения асинхронного кода и позволяют при грамотном использовании максимально использовать вычислительные ресурсы в рамках одного потока (а при комбинировании этого подхода с традиционной многопоточностью, как в async/await в C#, можно строить крайне эффективные приложения).
Можно построить обработчик события из множества асинхронных функций обратного вызова (callback-функций), которые управляются общим циклом событий, как это делается в Node.js, однако такой код сложно отлаживать и поддерживать. Значительно упрощают его паттерны Promise и Future, однако Python и некоторые другие языки программирования поддерживают механизм, который позволяет в данном случае обойтись без callback-функций – сопрограммы.
Сопрограмма (coroutine) – это компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну, как подпрограмма) и остановку и продолжение выполнения с сохранением определённого положения.
Сопрограммы в данном случае удобны тем, что позволяют писать асинхронный код в синхронном стиле. В последующих статьях мы рассмотрим механизмы их реализации в Python.
Приховування методу С#
Автор: Редакція ITVDN
Введение
В данной статье Вы узнаете, как скрывать метод и как вызывать скрытые элементы базового класса. Рассмотрим это на конкретных примерах.
Создадим в Visual Studio консольное дополнение с классом Student. У класса Student два поля: FirstName и LastName, и метод, который выводит FirstName и LastName на экране консоли.
using System;
namespace methodHidingInCSharp {
class Student {
public string FirstName;
public string LastName;
public void PrintName() {
Console.WriteLine(FirstName + " " + LastName);
}
}
class Program {
static void Main(string[] args) {
}
}
}
Создадим класс DiplomaStudent и GraduateStudent, которые унаследуют класс Student.
using System;
namespace methodHidingInCSharp {
class Student {
public string FirstName;
public string LastName;
public void PrintName() {
Console.WriteLine(FirstName + " " + LastName);
}
}
class DiplomaStudent : Student {
}
class GraduateStudent : Student {
}
class Program {
static void Main(string[] args) {
}
}
}
Если нужно создать объект класса DiplomaStudent и GraduateStudent, это можно сделать главным методом, например:
class Program {
static void Main(string[] args) {
DiplomaStudent ds = new DiplomaStudent();
ds.FirstName = "Sam";
ds.LastName = "Fisher";
ds.PrintName();
GraduateStudent gs = new GraduateStudent();
gs.FirstName = "Aiden";
gs.LastName = "Pearce";
gs.PrintName();
}
}
Так как классы DiplomaStudent и GraduateStudent наследуют класс Student, можно вызывать всех участников, кроме частных (приватных).
При запуске проекта получим следующий результат:
Результат вышел таким, как и предполагалось. Но есть небольшая проблема. Невозможно установить, какой из этих двух результатов DiplomaStudent, а какой – GraduateStudent. Для этого создадим дополнение к имени (studentType) с указанием Diploma или Graduate. Таким образом, просто смотря на имена, можно определить, какой из них DiplomaStudent, а какой – GraduateStudent.
Вернемся к проекту. Теперь стоит создать метод PrintName, который будет одинаковым для обоих производных классов, и добавить DiplomaStudent и GraduateStudent, чтоб сделать их более читабельными и понятными.
class DiplomaStudent : Student {
public void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");
}
}
class GraduateStudent : Student {
public void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");
}
}
Как известно, тот же метод используется в базовом классе.
class Student {
public string FirstName;
public string LastName;
public void PrintName() {
Console.WriteLine(FirstName + " " + LastName);
}
}
В результате дочерний класс скрывает реализацию базового класса.
При построении кода появляется зеленая волнистая линия. Если навести курсор мыши на линию, можно увидеть объяснение: если нужно специально скрыть унаследованный член, используйте новое ключевое слово.
Чтобы скрыть унаследованный от производного класса член, используйте новое ключевое слово, например:
class DiplomaStudent : Student {
public new void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");
}
}
class GraduateStudent : Student {
public new void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");
}
}
Запустите программу:
Результат оправдал ожидания. Рассмотрим, как вызвать метод назад после скрытия. Существует несколько способов.
Первый – использование основных ключевых слов.
class DiplomaStudent : Student {
public new void PrintName() {
base.PrintName();
}
}
class GraduateStudent : Student {
public new void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");
}
Базовый класс реализуется, когда мы выполним программу.
Второй способ – превратить тип переменной дочернего класса в базовый класс.
using System;
namespace methodHidingInCSharp {
class DiplomaStudent : Student {
public new void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");
}
}
class GraduateStudent : Student {
public new void PrintName() {
Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");
}
}
В двух предыдущих случаях реализовывается метод дочернего класса и используется новое ключевое слово, чтобы скрыть реализацию базового метода. То есть, когда Вы запускаете приложение, оно печатает реализацию дочернего класса.
class Program {
static void Main(string[] args) {
DiplomaStudent ds = new DiplomaStudent();
ds.FirstName = "Sam";
ds.LastName = "Fisher";
re I have type - casted the child class reference variable into a base class
((Student)ds).PrintName();
GraduateStudent gs = new GraduateStudent();
gs.FirstName = "Aiden";
gs.LastName = "Pearce";
((Student)gs).PrintName();
}
}
Запустите программу.
В предыдущем результате Вы получили базовую реализацию.
Третий способ состоит в создании переменной базового класса, которая указывает на дочерний класс.
class Program {
static void Main(string[] args) {
DiplomaStudent ds = new DiplomaStudent();
ds.FirstName = "Sam";
ds.LastName = "Fisher";
re I have type - casted the child class reference variable into a base class
((Student)ds).PrintName();
GraduateStudent gs = new GraduateStudent();
gs.FirstName = "Aiden";
gs.LastName = "Pearce";
((Student)gs).PrintName();
}
}
Результат тот же.
Источник: http://www.c-sharpcorner.com/UploadFile/219d4d/method-hiding-in-C-Sharp/
Створення Web API в MVC6
Автор: Редакція ITVDN
Введение
ASP.Net Web API – это основа создания HTTP услуг широкого спектра клиентов, таких как браузеры, мобильные телефоны, планшеты и так далее. API должна быть совместима с современными браузерами, чтобы использовать эти услуги в простой форме. Мы можем быстро и просто сбрасывать служебные данные в браузер, а также приложения.
Необходимость в Web API
Если Вы нуждаетесь в Web Service и Вам не нужно SOAP, то API ASP.Net –лучший выбор. Он строит простые HTTP сервисы, основанные на базе существующей WCF. ASP.Net Web API на основе HTTP легко определяются. У них открытый исходный код. Легкая архитектура подходит для устройств с ограниченной шириной полосы, например, смартфонов.
Создание простой Web API в ASP. NET MVC 6
Запустите Visual Studio 2015 Preview. В меню Файл выберите New > Project. В диалоговом окне New Project нажмите Tempates > Visual C# > Web и выберите ASP. NET шаблон проекта Web-приложений. Назовите проект "WebApplication1" и нажмите OK.
В диалоговом окне New ASP.NET Project выберите "ASP.NET 5.0 Empty” шаблон.
Проект включает в себя следующие файлы:
Global.json содержит настройки решения. В project.json находятся настройки проекта. Project_Readme.html – read me файл. Startup.cs содержит встроенный код конфигурации.
Откройте файл Project.json. Добавьте библиотеки классов (class libraries) в разделе зависимостей (dependencies).
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-beta1",
" "Microfost.AspNet.Diagnostics": "1.0.0-beta1"
}
Затем откройте Startup.cs с кодом, показанным ниже.
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
app.UseWelcomePage();
// app.UseMvc();
}
}
После отладки Visual Studio перейдите на http://localhost:port/ в браузере.
Создание Web API
Мы создадим Web API, чтобы упорядочить список клиентских продуктов. Сначала нужно добавить ASP.Net MVC6 в приложение.
Добавьте пакет MVC6 в список зависимостей в Project.json. Используйте код ниже.
"dependencies": {
"Microsoft.AspNet.Server.IIS": "1.0.0-beta1",
"Microsoft.AspNet.Diagnostics": "1.0.0-beta1",
"Microsoft.AspNet.Mvc": "6.0.0-beta1"
}
Затем добавьте MVC в request pipeline в Startup.cs.
Добавьте Using для Microsoft.Framework.DependencyInjection.
Добавьте следующий метод в Startup класс.
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;//add new
namespace WebApplication1
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseWelcomePage();
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
}
}
Добавьте модель
using System;
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Model
{
public class Customer
{
public int CustomerId { get; set; }
[Required]
public string Name { get; set; }
}
}
Добавьте контроллер
using Microsoft.AspNet.Mvc;
using System.Collections.Generic;
using System.Linq;
using WebApplication1.Model;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
static readonly new List<Customer> _items = new List<Customer>()
{
new Customer { CustomerId = 1, Name = "Henry" },
new Customer { CustomerId = 2, Name = "John" },
};
public IEnumerable<Customer> Get()
{
return _items;
}
public IActionResult GetById(int id)
{
var its = _items.FirstOrDefault(x => x.CustomerId == id);
if (its == null)
{
return HttpNotFound();
}
return new ObjectResult(its);
}
public void CreateCustomer([FromBody] Customer item)
{
if (!ModelState.IsValid)
{
Context.Response.StatusCode = 400;
}
else
{
item.CustomerId = 1 + _items.Max(x => (int?)x.CustomerId) ?? 0;
_items.Add(item);
string url = Url.RouteUrl("GetByIdRoute", new { id = item.CustomerId },
Request.Scheme, Request.Host.ToUriComponent());
Context.Response.StatusCode = 201;
Context.Response.Headers["Location"] = url;
}
}
public IActionResult DeleteItem(int id)
{
var item = _items.FirstOrDefault(x => x.CustomerId == id);
if (item == null)
{
return HttpNotFound();
}
_items.Remove(item);
return new HttpStatusCodeResult(204);
}
}
}
Выше описывается класс HomeController.
Маршрутизация
Атрибут маршрутизации определяет URL шаблоны контроллера.
[Route("api/[controller]")]
Методы HTTP
[HttpGet], [HttpPost] и [HttpDelete] – атрибуты, определяющие методы HTTP для контроллера.
public IEnumerable<Сustomer> Get() { } //[HttpGet]
public IActionResult GetById(int id) { } //[HttpGetbyid}
public void СreateСustomer([FromBody] Сustomer item) { } // [HttpPost]
public IActionResult DeleteItem(int id) { } //[HttpDelete]
{Customerid: int} int ограничивает переменную до соответствия целому числу, чтобы URL-адреса совпадали.
http://localhost/api/home/1
http://localhost/api/home/42
Из этой статьи Вы узнали, как создавать Web API в MVC 6, используя модели, контроллер и HTTP методы.
Источник: http://www.c-sharpcorner.com/UploadFile/85ed7a/create-web-api-in-mvc-6/