-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

Redux Business Logic

Понедельник, 03 Июля 2017 г. 00:25 + в цитатник

Как-то раз в Телеграмм-чате React_JS (кстати, русскоязычный чат, присоединяйтесь) обсуждали вопрос: "где в React-приложении должен быть расположен код, отвечающий за бизнес-логику". Вариантов несколько, мнения разделились. Ну а я решил записать подкаст (автор @PetrMyazin).


Рассмотрим частный пример с бизнес-логикой исключительно на клиенте. Приложение "кредитный калькулятор". Пользователь вводит в форму исходные данные: сумму, срок кредита, еще какие-то параметры; и на лету получает результат, например, сумму переплаты. Весь хитрый алгоритм расчёта суммы переплаты известен и уже реализован в виде JS-функции, назовём её f, которая принимает несколько параметров — те самые данные из формы, пусть будут a-b-c-d, а возвращает эта функция числовой результат (сумму переплаты, обозначим её как x) — это наша бизнес-логика.


Обратите внимание, что функция чистая. Её результат зависит только от входящих параметров. Она не производит никаких side-эффектов, не читает из глобальных переменных. Также эту функцию можно назвать независимой от фреймворка, ведь она одинаково будет работать и в React-приложении, и в Angular, и в Ember, и в jQuery, и даже на VanillaJS.


Но мы решили строить интерфейс на React+Redux, так что теперь возник вопрос: "в какой момент запускать вычисление нашей бизнес-логики". Ещё раз подчеркну, что в этом подкасте мы рассмотрим только упрощённый пример — все вычисления на клиенте, никаких запросов к серверу. В реальном приложении у нас навернка будет часть логики завязана на общение с сервером, но будет и такая часть, которая полностью может быть вычислена на клиенте по уже имеющимся данным. Так что все дальнейшие рассуждения применимы именно ко второй части — вычислениям на клиенте.


Что же, взглянем на форму. Она состоит: из пары текстовых полей, из слайдеров, выпадающих списков, чекбоксов. Введённые пользователем значения этих полей станут входящими параметрами в функцию f. Первый вопрос: "где и как будем хранить значения полей". Вариантов всего два: либо в локальном состоянии самих компонент (чекбоксов, слайдеров и тому подобных), либо в Redux-store.


Локальный state, размазанный по нескольким компонентам, в данном случае неудобен, т.к. нам в итоге нужно получить все значения одновременно, чтобы передать их параметрами a-b-c-d в функцию бизнес-логики. Нам хотелось бы хранить всё в одном месте: либо в неком общем предке (помните lifting state up), либо в Redux-store. Поскольку темой этого подкаста является обзор бизнес-логики в Redux-приложении, будем хранить в Redux.


Пройдём по шагам всю цепочку событий и попробуем прикинуть, в какой же момент лучше всего вызвать функцию бизнес-логики f.


Итак, пользователь меняет что-то на форме, вводит новую цифру в текстовое поле ввода, или двигает слайдер. Срабатывает некий обработчик handleChange — это первое место для вызова бизнес-логики. Но в обработчике handleChange мы можем прочитать значение только текущего измененного поля ввода из event.target.value, а остальные данные формы нам пока недоступны. Напомню, функция f — чистая, она не читает никаких данных из глобальных переменных. Она только принимает параметры a-b-c-d, и возвращает результат x. Значит сам по себе обработчик handleChange не подходит.


Следующий шаг — это функция action-creator. Здесь, с помощью Redux-thunk, мы можем получить весь объект состояния, т.е. получить a-b-c-d для вызова f. Action-creator — хороший претендент, запомним его.


Идём дальше. Поток выполнения переходит к запуску всех редюсер-функций. Редюсеры можно реализовать по-разному. Например, заготовим четыре отдельных функций для поля a, для поля b, для поля c и для поля d. В этом случае каждый из редюсеров имеет доступ к предыдущему состоянию своего поля ввода и к объекту action. Нам же, для запуска функции f, нужны все значения a-b-c-d одновременно. При такой организации редюсеров, единственный шанс — это если мы пробросим все четыре значения a-b-c-d внутри объекта action. По сути, мы таким образом передаем в объект action почти полную копию store. Звучит не очень, не хочется так делать.


Другой способ организации редюсеров — это единый редюсер, который будет обновлять все поля формы сразу, этакий formData-редюсер. Он принимает предыдущее состояние полей формы в виде объекта, содержащего a-b-c-d, обновляет изменившееся значение, а затем запускает функцию бизнес-логики f. Но постойте, а куда мы будем складывать результат вычислений, ту самую сумму переплаты x? Находясь внутри formData-редюсер, единственное место, куда мы можем сохранить x — в тот же самый объект, который теперь у нас будет иметь пять полей: a-b-c-d и x. Впору переименовать этот редюсер в formDataAndResult-редюсер. Он делает всё: и изменение формы запоминает, и результат вычисления бизнес-логики запоминает. Всё в одном большом объекте из пяти полей. Звучит тоже не очень. Слишком жирный редюсер, слишком много всего. Redux настраивает нас на функциональные подходы, на композицию редюсеров. А мы тут пишем одну большую функцию для управления всем сразу.


Дальше у нас запускаются селекторы в container components. Селекторы — это функции в mapStateToProps. Нас интересует селектор для компонента отображающего финальный результат х. Да, здесь можно сделать вызов функции бизнес-логики, т.к. селектор имеет доступ ко всему state, причем к уже обновленному state со свежими значениями a-b-c-d (сделаю на этом небольшой акцент); а результат вычисления функции f, вызванной внутри селектора, попадает в props компонента, отображающего сумму переплаты — вполне рабочее решение.


Если же не в селекторе, то последний шанс вызвать бизнес-логику — это вызвать функцию f непосредственно в методе render компонента x, того самого, который отображает сумму переплаты. Для этого в props придётся прокинуть все четыре требуемые значения a-b-c-d. На мой взгляд это менее изящное решение, чем предыдущее. Селекторы представляются более правильным местом, чтобы запустить вычисления, и передать уже готовый x для отображения.


Мы рассмотрели все варианты. Ну почти все. Можно было обсудить стрёмные способы, типа запуска расчётов в компонент will receive props, или ещё что-нибудь придумать. Но не будем тратить время.


Итого, у нас есть два явных кандидата на запуск бизнес-логики — это action creator и селектор компонента x.


Взглянем на action creator подробнее. Сначала ответим себе на вопрос: "у нас будет один action creator на все поля формы, или по отдельной функции на каждое из полей". Если мы сделаем отдельные функции, то получим четыре action creater-а: changeA, changeB, changeC и changeD; которые на самом деле будут похожи друг на друга, как две капли воды. Если мы хотим вызвать функцию f внутри action creator, эти вызовы придётся скопировать в код всех четырёх функций. Много копипасты. Хотя, кто плотно работает с Redux, boilerplate-кода не боится. Здесь можно организовать фабрику — create action creator, чтобы избежать копирования кода.


Но я предлагаю не углубляться в этом направлении, давайте лучше опишем одну функцию action creator, она будет принимать fieldName и newValue. fieldName — это строковая переменная, обозначающее поле ввода, в которое пользователь что-то ввёл. Используя Redux-thunk, мы можем получить доступ ко всему текущему state внутри нашего action creator-а, что нам и нужно для вызова функции f.


Но обратите внимание, что функция f должна получить самые свежие значения a-b-c-d, а тот state, который мы получим благодаря Redux-thunk, это уже устаревший state. Одно из полей имеет старое значение. Перед тем, как вызывать функцию f, нам нужно понять, в какой именно параметр подставить newValue. С точки зрения JS-синтаксиса, тут можно придумать с десяток элегантных и не очень решений. Но факт остаётся фактом, если мы хотим вызвать f внутри action creator, нам нужно взять текущий state, т.е. уже немного устаревший, и объединить его с только что пришедшим newValue. Не напоминает ли это нам редюсер-функцию, которая занимается ровно тем же самым? Получается, что нам придётся продублировать логику редюсера внутри action creater-а, чтобы сформировать самые свежие значения a-b-c-d.


Дальше-больше. Допустим мы получили результат вызова функции бизнес-логики внутри action creater-а, но теперь нам надо довести результирующее значение x до компонента, отображающего сумму переплаты. Придётся пробрасывать через Redux store, написав соответствующий редюсер и селектор. Сделав это, обратим внимание, что store теперь хранит и данные формы a-b-c-d, и одновременно результат вычисления x. Store получился избыточним. Помимо исходных данным, в нём ещё и результат производных вычислений. Это нехорошо по многим причинам. Некоторые из них мы обсуждали в предыдущих выпусках. Вывод: action creator — плохой выбор для вызова функции бизнес-логики.


Переходим к варианту с вызовом функции бизнес-логики из селектора. Его механику я уже упомянул выше. Селектор компонента, отображающего сумму переплаты, имеет доступ ко всему store. Причём к самым актуальным значениям a-b-c-d. Никакого дублирования кода редюсера. Имея a-b-c-d внутри селектора, мы вызываем f, а результат x передаём в качестве props в компонент, отображающий сумму переплаты. Просто, логично, без дублирования кода и без избыточного состояния. Селектор — отличное место для вызова функции бизнес-логики, которое представлено чистой функции от данных.


Заострю внимание на последней фразе, что мы рассматривали исключительно вычисление на клиенте. Если же ваша бизнес-логика — это не чистая функция, а целый процесс, с походом на сервер, т.е. с некими side-эффектами, то это уже тема отдельного разговора. Тут как раз надо обратить внимание и на action creator, и на middleware. Но обсудим это в другой раз.


Пишите на React, и процветайте!

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332146/


Метки:  

LibGDX + Scene2d (программируем на Kotlin). Часть 0

Воскресенье, 02 Июля 2017 г. 23:46 + в цитатник
И снова всем привет! Спешу поделиться, у меня были отличные выходные! Полтора дня я обдумывал вариант подачи материала, пилил макет и вообще всячески старался сделать хорошо. Что такое хорошо в контексте обучающего материала? На мой взгляд это «интересность», краткость, корректность и наглядность. Для меня лично написать такую статью — это подвиг. А вот серию статей — просто емкая и ответственная задача. Изучать Scene2d мы будем в процессе написания игры с нуля! Процесс нашего творчества растянется на долгие десять-двенадцать дней. Мне хочется верить что периодичность материалов будет примерно раз в день. Для меня лично это очень амбициозная задача, ведь требуется не столько запрограммировать, но и описать в статьях с детальным разбором. Я не сторонник бросаться в бушующий океан, в надежде научиться плавать. Мы прыгнем у лужу и будем последовательно ее углублять и расширять. Итак начинаем.

Разработку любой программы я настоятельно советую начинать с составления карточки продукта. Из обязательного — цели. Я составляю карточку продукта в Google Docs и вот как карточка выглядит в нашем случае docs.google.com/document/d/1m_A_Pupfk7gub732ZkGLuIYY0ezSCmSxqQffO93uXpY/edit?usp=sharing

Средневековый магнат


Цели проекта


  1. Демонстрация процесса разработки игры для сайта habrahabr.ru (общественная, информационная, краткосрочная)
  2. Создание материалов, которые впоследствие могут быть использованы как обучающие (личная, репутация, долгосрочная; общественная, информационная, долгосрочная)
  3. Создание основы для коммерческой игры (личная, краткосрочная)
  4. Привлечение скачиваний из Google Play (личная, финансовая, долгосрочная)


Первым делом стараемся максимально честно разобраться со своей мотивацией. Зачем мы вообще влезаем в это дело. Что будет служить нам путеводной звездой когда все хорошо и пинком под зад когда все плохо. Пожалуйста постарайтесь избегать альтруизма и “мира во всем мире”. Человеческая психика устроена таким образом, что личные побудительные мотивы значат больше чем общественные (визионерскую психологию тут не рассматриваю).

Игровой мир


Средние века / фэнтези
Цель игры — много денег
Сбор ресурсов
Продажа

Описание процесса игры


Так как первая и основная цель данной разработки является демонстрация работы, и игровой мир, и описание игрового процесса будут упрощены в угоду краткости и ясности подачи материала.

Игрок собирает ресурсы и продает их на местном рынке. На поиск и сбор ресурсов тратится еда/энергия. Когда энергия кончилась, ее необходимо покупать в городе.

Прототип интерфейса


пример


Несмотря на то, что в предыдущей статье я рекомендовал тетрадку и карандаш в качестве инструментов для прототипирования, этот макет сделан в Adobe Experience Design CC (Beta). На момент публикации статьи, его можно скачать бесплатно www.adobe.com/ru/products/experience-design.html На работу с ним я угрохал полтора дня но считаю это оправданным. Дело в том, что публикация на хабре является групповой работой, даже если я все делаю один. Чем более качественные опорные материалы я предоставлю, тем легче будет воспринимать информацию. Вот проектный файл Adobe Experience Design drive.google.com/file/d/0B2CQZfruKhbWcG5EOWZYenN6UUU/view?usp=sharing
Его можно скачать, запустить в режиме презентации и даже немного потыкать по кнопкам. Технически можно запилить отдельную статейку, но не знаю нужно ли это. Комментарии рассудят.

Ну и какая разработка без публичного репозитория? Вот ссылка: ptsiber@bitbucket.org/terrav/medieval-tycoon.git

Для работы нам понадобится Android Studio 3.0 (на данный момент доступна версия Canary 5), Android SKD и LibGDX libgdx.badlogicgames.com/nightlies/dist/gdx-setup.jar
Установку всех этих тряхомудрий я пропущу, тут все большие мальчики и девочки. На крайний случай есть комментарии.

Запуск мастера конфигурирования LibGDX происходит из командной строки:
java -jar gdx-setup.jar
Параметры проекта


Кто был не в курсе, LibGDX это кроссплатформенный фреймворк, позволяющий писать одновременно под PC, Android, iOS и даже HTML (для последнего используется GWT, а у нас Kotlin, так что HTML нам точно не грозит). Из расширений я выбрал два:

Freetype — позволяет генерировать растровые шрифты из ttf/otf
Tools — среди прочего позволяет генерировать атласы текстур


Коммит с получившимся проектом доступен в репозитории. Я старался крошить и именовать коммиты таким образом, чтобы было просто понять какой фрагмент за что отвечает. Так как LibGDX кроссплатформенный, я предпочитаю большую часть разработки проводить на PC и тестировать/исправлять ошибки под Android непосредственно перед релизом. Как правило на эту работу уходит не больше 2-3 часов времени.

Дальше в этой статье


  • Запуск проекта через DesktopLauncher
  • Перевод проекта на Kotlin
  • Ошибка Kotlin not configured
  • Конфигурация gradle для kotlin-desktop версии
  • Конфигурация портретной ориентации для desktop версии
  • Первое использование Scene2D. Загрузочный экран, загрузочная сцена


Запуск проекта через DesktopLauncher


конфигурация


Обратите внимание, что рабочая папка для DesktopLauncher расположена в android/assets. Запуск DesktopLauncher на коммите
initial commit after libgdx wizard


Обратите внимание, что даже только что сконфигурированный проект не запускается под android. Мы поправим это на следующем шаге.

Перевод проекта на Kotlin


LibGDX проекты сконфигурированы как мультимодульный gradle. Есть проектный build.gradle и модульные build.gradle для core, android и desktop. Почти весь код мы будем писать в core. В проекте android позже у нас будет сидеть AdMob + конфигурация immersive mode + покупки в Google Play маркете.

Для перевода проекта из java в kotlin мы меняем все apply plugin: «java» на apply plugin: «kotlin». В android/build.gradle добавляем apply plugin: 'kotlin-android'. Самые большие изменения произошли в проектном build.gradle
build.gradle
         mavenCentral()
         maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
         jcenter()
+
+        maven { url 'https://maven.google.com' }
     }
+
+    ext.kotlin_version = '1.1.3'
+
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.2.0'
-        
-
+        // uncomment for desktop version
+        // classpath 'com.android.tools.build:gradle:2.3.2'
+        classpath 'com.android.tools.build:gradle:3.0.0-alpha5'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
 
@@ -37,7 +43,7 @@
 }
 
 project(":desktop") {
-    apply plugin: "java"
+    apply plugin: "kotlin"
 
 
     dependencies {
@@ -74,13 +80,13 @@
 }
 
 project(":core") {
-    apply plugin: "java"
+    apply plugin: "kotlin"
 
 
     dependencies {
         compile "com.badlogicgames.gdx:gdx:$gdxVersion"
         compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
-        
+        compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
     }
 }



Добавился гугловый репозиторий, в buildscript.dependencies добавлен kotlin-gradle-plugin и в core проект добавлена compile-зависимость kotlin-stdlib (в нашем случае kotlin-stdlib-jre8).

Данная версия работает на android, но не работает в desktop варианте из-за ошибки Android Studio 3.0 Canary 5. Почему я считаю что это причина — запуск gradle цели desktop-run таки запускает приложение (правда требует запущенное Android device/emulator для запуска android:run). А вот запуск из Android Studio выкидывает
Exception in thread «main» java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
Если кто сможет победить запуск DesktopLauncher'a со свежей версией gradle — дайте знать пожалуйста!

Перевод java файлов в kt элементарна — выделяете файл/папку и жмете Ctrl+Alt+Shitf+K. Единственная ошибка которая возникнет у вас после данной операции заключается в требовании Kotlin'a инициализировать свойство в момент определения
java
public class MedievalTycoonGame extends ApplicationAdapter {
	SpriteBatch batch;
	Texture img;
	
	@Override
	public void create () {
		batch = new SpriteBatch();
		img = new Texture("badlogic.jpg");
	}

	@Override
	public void render () {
		Gdx.gl.glClearColor(1, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		batch.begin();
		batch.draw(img, 0, 0);
		batch.end();
	}
	
	@Override
	public void dispose () {
		batch.dispose();
		img.dispose();
	}
}



kotlin
class MedievalTycoonGame : ApplicationAdapter() {
    internal var batch: SpriteBatch // ошибка тут <- следует заменить на private lateinit var batch: SpriteBatch
    internal var img: Texture // ошибка тут <- следует заменить на private lateinit var img: Texture
    override fun create() {
        batch = SpriteBatch()
        img = Texture("badlogic.jpg")
    }

    override fun render() {
        Gdx.gl.glClearColor(1f, 0f, 0f, 1f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
        batch.begin()
        batch.draw(img, 0f, 0f)
        batch.end()
    }

    override fun dispose() {
        batch.dispose()
        img.dispose()
    }
}


internal = package видимость в java. Нам пакетная видимость не нужна (и вообще через пару коммитов мы удалим эти поля). Не во всех случаях мы можем проинициализировать поле сразу, а делать его nullable это вообще глупость (нам котлин интересен как раз из-за null-safety). Для этого в kotlin есть модификатор lateinit, который говорит компилятору, что программист зуб дает, на момент использования этого поля оно не будет равно null. Это так называемая синтаксическая соль. Вообще, если смотреть на этот код не как результат автоматической конверсии, то уместнее бы смотрелось
private val batch = SpriteBatch()
private val img = Texture("badlogic.jpg")


Ошибка Kotlin not configured


Эту ошибку вы будете видеть каждый раз при запуске Android Studio. Просто щелкните синхронизировать gradle


Конфигурация gradle для kotlin-desktop версии


Как я уже говорил, я предпочитаю разрабатывать desktop версию приложения, и изменением пары строчек мы реанимируем этот режим. Все что нужно — указать в проектном build.gradle
classpath 'com.android.tools.build:gradle:2.3.2'
а в gradle-wrapper.properties версию gradle-3.3-all.zip

Конфигурация портретной ориентации для desktop версии


В DesktopLauncher добавляем горсть параметров конфигурации. Три относятся к размеру окна и возможности изменения размеров. Четвертый параметр vSync отключен т.к. есть глюк, на некоторых видеокартах в desktop и только на config.foregroundFPS=60 (по умолчанию), грузит одно ядро процессора в 100%.
        config.width = 576
        config.height = 1024
        config.resizable = false
        config.vSyncEnabled = false


Первое использование Scene2D. Загрузочный экран, загрузочная сцена


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

Scene2D является графом (деревом) элементов и предназначен в первую очередь для создания UI. Прямо «из коробки», вы получаете возможность верстки, трансформации элементов (поворот, масштаб, сдвиг и т.д.). Огромным плюсом идет обработка касаний. Ну и вишенка на торте система действий. С непонятным определением закончили, теперь то же самое человеческим языком.

Есть сцена, она занимает весь экран. На сцене вы можете разместить таблицу, в таблице картинку, панель прокрутки, десяток кнопок и даже черта лысого (главное чтобы в душе он был Actor'ом). При помощи волшебных слов top/center/left/width и т.д. вы реализуете верстку. Пример сложнее чем hello world будет только завтра, и так статья большая получилась. Дальше на любой произвольный элемент вы вешаете обработчик касания и он работает. Вам не нужно вручную ловить координаты клика, проверять что же находится там, какой у объектов z-index и т.п. Но еще раз, про это завтра. А сегодня просто несколько фрагментов кода напоследок:

class MedievalTycoonGame : Game() {

    val viewport: FitViewport = FitViewport(AppConstants.APP_WIDTH, AppConstants.APP_HEIGHT)

    override fun create() {
        screen = LoadingScreen(viewport)
    }
}

Наш класс MedievalTycoonGame теперь наследуется от Game, вся задача которого свалить работу на Screen. Первый экран, который мы сейчас покажем пользователю будет называться LoadingScreen и будет содержать одну сцену — LoadingStage. Т.к. не предполагается разрастания этих классов, я размещу их в одном файле LoadingScreen.kt

LoadingScreen.kt
class LoadingScreen(val viewport: Viewport) : ScreenAdapter() {

    private val loadingStage = LoadingStage(viewport)

    override fun render(delta: Float) {
        Gdx.gl.glClearColor(0f, 0f, 0f, 0f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)

        loadingStage.act()
        loadingStage.draw()
    }

    override fun resize(width: Int, height: Int) {
        viewport.update(width, height)
    }
}

class LoadingStage(viewport: Viewport) : Stage(viewport) {

    init {
        val backgroundImage = Image(Texture("backgrounds/loading-logo.png"))
        addActor(backgroundImage.apply {
            setFillParent(true)
            setScaling(Scaling.fill)
        })
    }
}


Все что делает LoadingScreen — затирает экран черным цветом и вызывает методы act() и draw() у LoadingStage. На act() очень удобно вешать логику программы, работу с данными. Draw() это просто отрисовка всех элементов сцены.

Единственный момент, хочу заакцентировать как выглядит инициализация сцены java vs kotlin
    init {
        val backgroundImage = Image(Texture("backgrounds/loading-logo.png"))
        addActor(backgroundImage.apply {
            setFillParent(true)
            setScaling(Scaling.fill)
        })
    }


    public LoadingStage() {
        Image backgroundImage = new Image(new Texture("backgrounds/loading-logo.png"));
        backgroundImage.setFillParent(true);
        backgroundImage.setScaling(Scaling.fill);
        addActor(backgroundImage);
    }


В случае с kotlin у нас всегда инициализация элемента и его размещение на соседних строчках. Это достигается за счет функции расширения apply. Иерархия в kotlin автоматически создает отступы и визуально очень легко читается. В java вся верстка идет без отступов. Инициализация элемента и его размещение часто невозможно рядом. Если иерархия состоит из 3+ уровней глубины, упорядочить элементы красиво (и дешево в поддержке) в java невозможно.

На сегодня это все. Рассматривайте эту статью как вводную, собственно раскрытие Scene2D и реализация игры будет завтра и далее. Спасибо что были с нами ;) И не рекламы ради (в этом приложении надо приложить усилия чтобы увидеть рекламу), мой первый проект «Пятнашки» на Scene2D когда я еще только-только осваивал. Из достоинств — удобство управления. Существуют сотни если не тысячи версий приложения и в 90% что я видел передвижение фишек возможно только тычком в соседнюю с пустой клеткой. Соберёте котика?

В следующих статьях


  • Базовые элементы Scene2D
  • Два базовых контейнера для верстки
  • Атлас текстур
  • Шкурки
  • Интернационализация


P.S. В загрузочном экране использована работа художника Виталия Самарина aka Vitaly S Alexius
www.rom.ac
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332144/


Метки:  

Посещение конференции — чеклист

Воскресенье, 02 Июля 2017 г. 22:53 + в цитатник
Это статья появилась за распитием пива с друзьями в Академгородке — стало понятно, что лучше её перенести в текстовый вид, дабы не повторять сто раз.

Существуют легенды на тему того, что единственный смысл конференции — это найти себе работу покруче за деньги текущего работодателя. Несмотря на внешнюю неэтичность, в этом хотя бы есть смысл. В реальности бывает куда хуже: люди приходят на конфу, и потом не знают, чем заняться, кроме как хавать булочки в переходе (особенно если они бесплатные). Давайте я расскажу, что делать, чтобы не тратить свои и чужие ресурсы зазря.


0. Целеполагание


Основная проблема разработчика в том, что навыки, технологии, идеи — всё это постоянно устаревает. Когда-то на земле жили динозавры, а теперь их нет. Вы точно не хотите быть одним из этих динозавров. Для этого нужно сфокусироваться на обновлении собственных знаний — это важно и максимально приоритетно. Это настолько приоритетно, что вы готовы потратить на это свои и чужие бабло и время.

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

В книге «Moonwalking with Einstein» рассказывалось о журналисте с феноменально хорошей памятью, которому никогда не нужно было ничего записывать на бумажки и диктофоны включая длинные интервью. Эта память досталась ему благодаря тому, что его мозг считал все события вокруг одинаково важными, и одинаково достойными запоминания. Эта же особенность полностью разрушила жизнь этого человека. Он всю жизнь пытался фокусироваться на всём сразу, и ни разу не достиг ничего конкретного.

Разработчики софта обычного не такие, как этот журналист. Нам постоянно хочется вполне конкретных очевидных вещей: интересней проекты (и обычно они в точности знают, что значит «интересней», больше возможности, выше должности, больше денег – и всё это как можно быстрее. Для этого необходим конкретный фокус на своих задачах, отбросив иллюзии и мораль (в смысле, корпоративный bullshit типа «в нашей компании работать большая честь»).

Самая центральная идея в том, что совершенно тот же фокус следует сохранять и при посещении любых информационных ивентов.

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



1. План


Любое достаточно сложное, растянутое во времени действие, должно начинаться с плана.
Отсутствие плана — тоже план, только плохой. Как видим, выбора у нас немного: либо план пишем мы, либо его пишут за нас. «Дума — не место для обсуждений».

Наметим несколько опорных пунктов:
  • Предположения
  • Вехи
  • Конкретные задачи

2. Предположения


2.1 Предположение: смысл существует


Осознайте, зачем вам нужна конференция, определите смысл.
Выделите на это хотя бы 2 минуты.

Как говорил один товарищ, «каждый приезжает за своим. Кто хочет поменять работу — за оффером, кто хочет разобраться в технологии — за советами, у кого беда на продакшене — за рекомендациями лучших собаководов».

Можно воспользоваться «помощью друга» или «подсказкой зала» — организаторы конференции в рекламных материалах уж точно расскажут, зачем она нужна. Что угодно, лишь бы вы купили билетик. Но я всячески советую осознать смысл самостоятельно, потому что его может не быть.

Девопсерам повезло, у нас с поиском смысла всё просто.

Например, есть люди, которые могут целыми неделями редактировать две строчки кода, добиваясь корректности какой-то сложной формулы, или починки суперсложного бага в legacy системе. Нас же постоянно преследует удав:



Если вас постоянно преследуют страшные монстры, с этим нужно что-то делать, верно?
Каждый второй хочет быть богом Докера, легко и просто вертя тысячами контейнеров, и ты горишь, и ты в аду.

К сожалению, просто наличия смысла недостаточно.

2.2 Предположение: благородные сэры не варят кашу


(Предупреждение: Эта часть будет большой, нет времени — можно смело пропускать)

Представьте, джава-разработчик начинает готовиться к следующему собеседованию.
Как может выглядеть его чек-лист?

  • Java Core, Collections, Concurrency (14d)
  • Spring, Spring MVC (7d)
  • Hibernate, SQL (7d)
  • JS (Angular, React) (7d)
  • Testing, TDD (2d)
  • Building (Maven, Nexus) (1d)
  • VCS (Git, Mercurial) (2d)
  • Jenkins (1d)
  • GNU/Linux, Monitoring (2d)

Как видим, к «чистому программированию», посвященному алгоритмам и железу, примешивается знатная порция всякого борща! Сколько положить хлеба? Может быть, еще два компота и четыре дня на Графану?

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

Есть один отличный пример — который по дефолту, к сожалению, понятен только игрунам в современные онлайн игры. Хорошо известен термин «метагейм». Попробую объяснить в паре слов. У словно существуют core механики — собственно правила игры, и meta механики — те эффективные способы действия, которые игроки придумывают сами. Например, в футболе core механики – это собственно правила футбола, а meta — выбор оптимального состава команды, стратегий и тактик поведения на поле. В покере core — это правила покера (которых раз два и обчелся), а meta — это вся остальная игра, стратегия игрока, придержать фишки до последнего стола, не рисковать и играть тайтово, забирать блайнды на префлопе, итп.

Обычно у несбалансированных игр (например, у большинства современных moba) всегда есть «господствующая мета» — набор высоко эффективных способов игры, очень сильно зависящих от конкретных характеристик твоих персонажей, которые меняются с каждым новым обновлением. Мета, которая была популярна в прошлом году, совершенно не играет на сегодняшний день, несмотря на то что состав персонажей и их способности почти не изменились. Когда-то в Overwatch всегда играли 2-2-2, а теперь уже нет. Чтобы научиться играть в ту же игру, теми же персонажами, но с новой метой, на профессиональном уровне от игрока требуется вложить усилия, иногда — значительные. Это как если бы в футболе правила менялись каждое воскресенье.

С технологиями происходит примерно то же самое, что в moba — это сплошной метагейм поверх одних и тех же идей, а зачастую одних и тех же реализаций. У тебя есть куча технологий, связанных друг с другом в отношении «все со всеми», имеющие в меню комплексного обеда разные веса. Будучи сисадмином, твои core правила игры включают GNU/Linux и Monitoring. Джава-программисту в 2017 году точно встретятся на пути Spring и Hibernate. Хотя это будут уже совершенно не тот Linux и Spring, которые были в 2003 году (про systemd и Spring Cloud можно похоливарить в комментариях). Одни и те же идеи правильного мониторинга или организации коллекций кочуют из года в год, из технологии в технологию, всегда похожие – и всегда немножечко разные. Иногда они тебе нравятся, иногда ты с ними в чем-то несогласен — но ты знакомишься с ними, и идешь дальше.

Но адаптироваться ко всему этому весьма непросто. Пробовали подписаться на Гитхабе на все интересные проекты и следить за свежими коммитами? Это нереально. Ну разве что тебе вообще мало что интересно, либо ты упоролся и готов потратить на это всё рабочее и свободное время. То же касается мониторинга новостных сайтов, RSS-фидов и почтовых рассылок проектов, итп. Более того, для глубокого понимания метагейма недостаточно их просто читать – нужно анализировать и экспериментировать, пробовать варианты. Возможно, придется нанять нескольких архитекторов, которые будут делать это вместо тебя, и платить им за это конские зарплаты? Это была печальная часть.

Приятная часть в том, что синхронизироваться с метой можно и нужно достаточно редко. Скажем, раз в полгода.

И вот тут отлично подходят конференции. На них приезжают лучшие специалисты в своем направлении, которые специально за месяцы готовились к этому докладу, подобрали для тебя удобную форму подачи, любое блюдо будет съедено за час. Весь комплексный обед съедается за день. Меню обеда, треки конференции – составляют знатные гуру, знающие толк в вопросе. После этого у тебя есть всего полгода-год чтобы попытаться хотя бы как-то угнаться за метой. Полгода-год – потому что потом будет следующая конференция. Изящное, удобное решение. Конечно есть и минусы — придется тратить силы чтобы погрузиться в самолет и дотащить свою тушку до места, но имхо, в целом этот минус полностью покрывается плюсами.

Поэтому второй совет будет такой: воспринимайте конференцию/митап как набор задач, которые нужно выполнить в ближайшие полгода. Как своеобразный чеклист или дополнение к бэклогу. Все задачи – опциональные, но если не проработать достаточное количество тем, то можно серьезно влипнуть в будущем. Важно, что это не какие-то «готовые решения» ваших проблем (для этого есть StackOverflow), а именно список стратегических задач, трендов.



3. Вехи


Как и в любом другом проекте, мы должны иметь вполне четкую временную структуру. Пока нет плана — ты тратишь время на фигню. Все, кто недостаточно богат, чтобы спокойно тратить время на ерунду, стоит озаботиться следующим:

  1. Определить концепт
  2. Обеспечить ресурсами (билеты, время, итп)
  3. Провести информационную подготовку
  4. Поучаствовать в конференции
  5. Внедрить изученное
  6. Перейти на следующую итерацию (через полгода-год)

4. Задачи


4.1 Определение концепта


Обычно концепт делится как минимум на следующее:

  • Для себя
  • Для работодателя
  • Для друзей и прочего окружения

Очевидно, что «для себя» идея выглядит типа «послушать доклад X, лично пообщаться с Y, и нажраться на афтерпати».

Только не нужно сообщать это работодателю, потому что ему обычно не так интересно, что ты там знаешь, как то, что ты собираешься со всем этим делать в конкретном проекте. Ему можно сформулировать как-то более конкретно: «в проекте X собираюсь улучшить Y благодаря использованию A,B,C, которым посвящена конференция». Объяснение желательно придумать емкое и пафосное, сдобрив любимыми баззвордами руководителя.

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



4.2 Обеспечение ресурсами


Тут всё обыденно:

  • Если ехать на деньги компании
    • Заявить руководству свое желание присутствовать
    • По необходимости — придумать аргументацию для убеждения.

  • Если ехать за свои деньги — купить билеты:
    • Конференция
    • Самолёт
    • Гостиница/хостел


Об этой ерунде нужно побеспокоиться вовремя, ибо:

  • Некоторые конференции дают бешеные скидки на предзаказы билетов.
  • Гостиницы и самолёты, будучи заказанными заранее, могут стоить сильно дешевле.
  • В некоторых компаниях нужно заявлять бюджет на год вперед. Или на месяц.
    Если не заявить работодателю прямо сейчас — потом поезд ушел.
    Также работодатели не любят внезапного желания выделить хорошую гостиницу.
  • Некоторые компании отпускают на конференцию, только если ты не собираешься увольняться ближайшие полгода. Если ты собираешься — надо продумать тайминг.

Видел огромное количество людей, которые просохатили все полимеры просто потому, что решили оформить бумажки «завтра с утра». Начинай заполнять чертовы бумажки прямо сейчас.

4.3 Информационная подготовка


Это — самый интересный пункт программы.
Обучение лучше всего работает следующим образом:

  • Некоторое время интуитивно изучать X
  • Придумать теорию относительно Х
  • Сделать предположение, как X должно работать
  • Поставить эксперимент и узнать, как Х работает на самом деле
  • На основании разницы между предположением и реальностью скорректировать теорию
  • Пойти на следующую итерацию улучшения теории


Холиварный пример:

  • Некоторое время использовать Windows-сервер, создавая общее впечатление
  • Теория в том, что Linux значительно лучше Windows как файлопомойка
  • Предположение: сделав файлопомойку на Linux мы не получим тех проблем, что были в Windows
  • Эксперимент: делаем файлопомоку на Linux и обнаруживаем, что в ряде случаев проблемы есть, а в ряде случаев – нету
  • Корректировка: Linux лучше Windows как файлопомойка только в ряде случаев
  • Следующая итерация: определение ситуаций, когда лучше, а когда хуже


Идея кристалльно ясна со школы, наверное.

Конфа или митап сильно походит на этот план постановки эксперимента. Только экспериментируем мы не над предметами неживой природы или программами, а над докладчиками и их докладами. На приличных конференциях обязательно присутствует дискуссионная зона, поэтому можно ставить тесты напрямую на докладчике.

Теперь, что происходит, когда мы приехали на конфу, не подготовившись? Отсутствует фундаментальные два первых пункта программы: мы не знакомы с обсуждаемыми вещами, и не имеем никаких идей, что же мы собираемся проверять. Epic Fail. Остается либо дивиться на непонятные вещи, о которых вещают со сцены, либо хавать булочки в переходе.

Чтобы не опростоволоситься, перед тем как куда-то ехать, и вообще соглашаться на конфу, надо изучить её программу и составить план подготовки.

Дальше я скопипастил из недавней статьи план конференции DevOops 2017:

  • Контейнеры, Оркестрация (Docker, Kubernetes, Clusters, etc).
  • Виртуализация, Облачные технологии (AWS, Azure, Heroku и другие).
  • Мониторинг и аудит приложений (Prometeus, OkMeter, DataDog, BPF, Dynatrace, XRebel, Glimpse, Zipkin, OpenTrace и другие).
  • Continuous Delivery (Jenkins, TeamCity, Bamboo).
  • Configuration Management (puppet, chef, ansible).
  • Безопасность (Vault, etc.)
  • Разбор полетов на примерах крупных проектов, внедряющих DevOps: успешных и провальных.

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

Важно, что предварительная подготовка должна занимать какое-то достаточно небольшое время, например суммарно час в день. Иначе обычный ленивый разработчик (типа автора этой заметки) отмораживается и перестает готовиться. Простой способ – не нужно изучать вообще всё перечисленное, а только по одному слову из каждого пункта, это серьезно облегчит задачу.

Теперь классифицируем источники информации, по которым мы будем проводить предварительную подготовку.

4.3.1 Целенаправленное применение чего-то нового


Просто берете любую технологию, и начинаете её использовать.

Использовать где угодно:
  • Соорудить небольшой тестовый проект
  • Помочь коллеге или другу, который уже её использует на практике
  • Использовать на работе, если это возможно

Не нужно тратить огромное количество времени, достаточно сделать пару рабочих прототипов, чтобы зацепить основные идеи. Например, чтобы завести Ansible и что-нибудь им сделать — нужны считаные минуты, плюс это можно тут же применить на работе.
Способ хорош тем, что в отличие от всевозможных туториалов, он дает понимание о вещах, которые нужны лично тебе, а не доброму дяденьке, сочинявшему документацию на сайте.

4.3.2 Блоги


Сейчас все мы проводим в социальных сетях просто непростительное количество времени. Достаточно открыть Хабр или Фейсбук, и залипнуть на целый день. Способ использовать прокрастинацию с пользой — начать читать технические блоги по нужным темам.

Чтобы обмануть свой мозг, чтобы он всё еще считал это отдыхом и прокрастинацией, а не новым типом работы, можно поставить какой-нибудь четкий будильник: например, читаем про Энсибл не более 30 минут, а потом снова за работу!

Сейчас я не буду мусорить здесь, описывая всю первую страницу гугла по запроу «Docker Blog», они там все интересные. Если у тебя, читатель, есть какие-то любимые блоги, напиши о них в комментариях!
Возможно теме систематизации devops блогов стоит посвятить отдельный пост. Нужно ли это Напишите в комментариях.

Хочется только отметить, что лучшие материалы — это те, которые промаркированы по времени. Чтобы было четко понятно, какие именно события, и в каком порядке, произошли за последний год. Сравнить «как было» и «как стало». Потом посмотреть «как стало» уже на конференции и сравнить

4.3.3 Книги


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

Некоторые советы по использованию книг:

  • Не читайте от корки до корки. Смотрите оглавление, и читайте только то, что интересно. Время для подготовки к конфе очень ограничено, если залипнуть в одну книгу длиной 1500 страниц, времени ни на что другое не останется.
  • Не бойтесь, что текущая глава опирается на предыдущую настолько, что пропустив её, вы ничего не поймете. Обычно — поймете. Исключение составляют, например, книжки по алгоритмам и дискретке.
  • Читайте книги по фундаментальным вещам. Для всего остального есть документация в интернете и StackOverflow.
  • Если книга выглядит так, как будто автор просто перенес на бумагу документацию с официального сайта проекта — дальше можно не читать, в печь.


4.3.4 Видео


На ютубе есть огромное количество видео, которые помогут быстро настроиться на правильный лад, например DockerCon 2017. Особенно круты видеозаписи предыдущих конференций: можно посмотреть «что было» и «что стало», как менялось отношение спикеров по разным вопросам из года в год.

4.4 Участие в конференции


Хорошие рекомендации недавно были опубликованы вот в этой недавне статье на Хабре.

На этом можно бы и закончить, но всё же главное:

  • Заранее определи, что собираешься слушать.
    (Если нет ничего интересного, то на конфу можно не идти)
  • Если идете толпой от одной компании, можно разделиться по разным докладам.
    Кто куда пойдет — тоже решить заранее
  • Взять с собой блокнот или смартфон, чтобы записывать интересные моменты и мысли.
  • Обязательно поймать докладчика в дискуссионной зоне. Не бояться задавать странные и неудобные вопросы
  • Продумать вопросы — где пожрать и поспать. Не пишу здесь, не маленький уже


Отдельно по поводу записей. Если мысли тут же не записать, то под давлением мощного потока информации они быстро забываются. Диктофоны на подобных мероприятиях зачастую работают плохо из-за акустики залов и стремности диктофона (можно купить хороший диктофон). Лично для меня лучше всего работают mind maps на ноутбуке. Мне нравится платный Mindjet MindManager, но есть и куча бесплатных утилит (google: «best free mind mapping software 2017»). Если дополнить это слайдами доклада (докладчики обычно выкладывают их в интернете), получается очень хорошая база.

4.5 Презентация и внедрение


Внедрение чего угодно с конференции связано с несколькими сложностями.

4.5.1 Quickfix vs trending topic


Во-первых, материалы конференции не являются быстрофиксами для решения лично твоих проблем. Докладчики — не твои сотрудники. Просто так срисовать доклад и метнуться у себя что-то чинить — не получится. Поэтому можно относиться к полученной информации как к трендам, тенденциям, которые нужно учесть и проработать.

Чтобы было что прорабатывать, нужно иметь:
  • Память о том, что происходило на конференции
    (как минимум, не следует во время и после конференции употреблять излишне много алкоголя, иначе точно всё забудешь)
  • Записи: блокнот, диктофон, майдмапы, итп
  • Видеозаписи конференции (бывают не везде, и точно не сразу)

Дальше идет собственно проработка темы. Например, если мы на конференции впервые познакомились с Kubernetes, то не стоит бежать и внедрять его. Стоит всесторонне изучить тему и отнестись к этому максимально серьезно.

4.5.2 Презентация темы


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

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

Наиважнейшая штука в том, как именно подать внедрение новой технологии. Обычно мы, технари, просто задавливаем собеседника интеллектом. Но в случае если ты пытаешься убедить начальника провести тестирование какой-то совершенно новой штуки, того самого критического объема знаний может еще не быть. Тогда можно задавить собеседника здравым смыслом. Здравый смысл стоит начать с описания типа:

  1. Проблема.
    Нужно выдернуть какую-то реальную проблему из текущего проекта.
  2. Решение.
    Тут следует название полюбившейся тебе технологии с кратким комментарием.
    Не нужно вываливать на человека всю глубину технической мысли, для начала нужно чтобы название технологии врезалось в память и засело там навечно.
  3. Концептуальное описание пути к успеху.
    Например, рассказываешь, что при наличии дополнительных двух человек и недели времени мы внедряем технологию, и получаем такой-то профит. Тут нужно сконцентрироваться на бизнес-аспектах: деньгах, времени, наборе платных решений, итп. Впрочем, грузить конкретными цифрами тоже не нужно, только намекнуть общий смысл.
  4. Магия, благодаря которой всё это работает.
    Вот тут можно развернуться в технических описаниях, впрочем, не перегибая палку. Как только заметишь, что у начальника погасает взгляд и интерес — необходимо тут же заткнуться и переходить к другим вопросам.
  5. Конференция!
    Упомянуть что вся эта магия стала доступна благодаря участию в конференции.
    Это гарантирует тебе оплаченные билеты на следующие конференции :)
  6. Как мы это представим заказчику.
    Зачастую новая технология требует совершенно отдельного подхода к продаже её заказчику, если такой имеется. Это серьезный пункт, который надо прорабатывать отдельно.
  7. Очень круто было бы объяснить, почему того, что ты сейчас будешь внедрять, убьёт на месте всех ваших конкурентов, если таковые имеются, и ты достаточно в этом понимаешь, чтобы не сморозить чушь.
  8. На последок, можно притащить информацию о текущем статусе проекта, и о конкретных метриках (например, увеличении числа запросов к веб-сервису), которые покажут успех внедрения технологии


Еще раз подчеркну, что так как мы получили на конференции не конкретные быстрофиксы, а снимок трендов, перед тем как вступать в такой диалог, нужно основательно подготовиться.

Зачастую начальство привыкает к общению в виде просмотра слайдов. Можно считерить, и просто доработать слайды с конференции, благо на них есть публичная ссылка.
Но слайды для огромной конфы готовятся для просмотра с большого экрана.

Я обычно перерабатываю их следующим образом:
  • Инвертирую цвета: задник тёмно-серый, шрифт белый
  • На первой страничке должен быть какой-нибудь запоминающийся логотип (логотип твоего проекта, или технологии, которую ты пиаришь. Он должен прямо вжечься в мозг смотрящего)
  • Все шрифты сконвертить в sans serif или что-то такое серьезное. Никаких комиксансов.
  • Поменьше длинных списков. Спикеры пишу длинные списки всего на свете, а ты с начальнкиом сможешь обсудить от силы 3-5 пунктов. По той же причине не должно быть многоуровневых списков и прочего треша и угара.
  • Выбросить картинки – мемасики и смехучеки с Боромиром
  • Графики и пайчарты — оставить. Если их вообще нет — добавить.
  • Особый понт и шик — сделать печатную версию слайдов на хорошей бумаге и действительно распечатать их. Принтер в соседнем книжном магазине, скорей всего, не умеет печатать белым по темной бумаге, поэтому придется нагуглить подходящую типографию.


5. Заключение


Это был короткий чеклист, о чем стоит подумать перед посещением конференции. Если ты

  • поставил четкую цель,
  • составил план мероприятия,
  • определил концепцию того как это мероприятие укладывается в повседневную жизнь коллектива,
  • вовремя купил билеты на конфу-самолет-гостиницу (вовремя — в смысле, сейчас, потом будет поздно),
  • основательно подгтовился к конференции, используя видео, блоги, книги и другое,
  • внимательно слушал доклады и майндмапил важные мысли,
  • после конференции проработал эти заметки и отсмотрел видео,
  • по полученному сформировал картину трендов и задач по развитию твоего проекта,
  • и грамотно презентовал начальству


То конференция, возможно, прошла незря.

А если ты собираешься всё-таки, ни к чему не готовиться, и тупо жрать булки в коридоре в ожидании конца доклада, то можешь здорово сэкономить, поменяв конфу на посещение Бургеркинга.

Всем добра.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332126/


Метки:  

Дайджест свежих материалов из мира фронтенда за последнюю неделю №269 (26 июня — 2 июля 2017)

Воскресенье, 02 Июля 2017 г. 22:49 + в цитатник
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.

Веб-разработка
CSS
Javascript
Занимательное

Веб Разработка



CSS


https://habrahabr.ru/post/332138/


Продажи через App Store. Неутешительные выводы по-прошествии 4-х лет

Воскресенье, 02 Июля 2017 г. 22:27 + в цитатник
В который раз уже убеждаюсь в том, что никому нельзя доверять, особенно в денежных вопросах и особенно известным корпорациям с безупречной репутацией. Конечно у меня нет прямых доказательств, чтобы обвинять кого-либо в мошенничестве, но у меня пока еще есть здравый смысл и логика, чтобы определить это с высокой долей вероятности.
Для начинающих молодых и не очень iOs разработчиков, вкладывающих свои идеи, силы и средства в создание своих интересных и не очень приложений с целью заработка на их продаже или продаже рекламы в них, коих здесь много. Хочу вас предупредить: скорее всего вы не получите ничего, либо такой мизер, что и говорить не о чем. И дело вовсе не в качестве вашего приложения, не в том, что ваша идея никому не интересна, не в том, что о ней мало кто знает, нет, конечно и в этом тоже, но по большей части в той бизнес схеме, по которой работает App Store и которая делает корпорацию Apple богатейшей в мире, во многом и за ваш счет.

Более четырех лет назад я сделал несколько приложений под iOS для изучения английского языка, о которых писал в этой статье. Делал я их для себя, по ним учился я и моя семья. В дальнейшем, чтобы не пропадать добру я их выложил в App Store, до сих пор они там и имеют хорошие отзывы.
У каждого приложения есть две версии: бесплатная, ознакомительная с одним уроком и платная расширенная. Из бесплатной версии можно по ссылке скачать платную. Еще я создал сайт, где рассказал нашу историю освоения английского языка и там же раздавал (и раздаю) промо-коды всем желающим.
Приложения приносили мне более чем скромные деньги, но я и не рассчитывал на многое, понемногу совершенствовал их, исправлял ошибки. Затем, поддавшись естественному желанию получить больше денег, я решил заняться их продвижением.
Я сразу решил, что никаких массовых рассылок, спама — только органический трафик и только вручную. Я давал рекламу в поисковиках, в соцсетях, общался с живыми людьми, заинтересованными и не очень. Результат, как правило, это большое количество установок бесплатных версий, доходивших до нескольких сотен в день и неизменное количество покупок платных. Несмотря на очевидность происходящего я в течение четырех лет пытался как-то объяснить это математическое чудо, чем угодно, только не мошенничеством Apple, пока не перепробовал всё. Сегодня я уверен, что Apple платит рабам разработчикам ровно столько, сколько посчитает нужным, а имеет с них по полной. Возможно кто-нибудь сможет переубедить меня в этом? И объяснит мне как такое возможно (цифры условные, но полностью отражают суть):
100 установок бесплатных версий в неделю — 3-5 покупок платных версий
1000 установок в неделю — 3-5 покупок
3000 установок в неделю — 3-5 покупок
100 установок в неделю — 3-5 покупок

1 год прошел

100 установок в неделю — 3-5 покупок
1000 установок в неделю — 3-5 покупок
3000 установок в неделю — 3-5 покупок
100 установок в неделю — 3-5 покупок

2 года прошло

100 установок в неделю — 3-5 покупок
1000 установок в неделю — 3-5 покупок
3000 установок в неделю — 3-5 покупок
100 установок в неделю — 3-5 покупок

4 года прошло.
Повторюсь, что установки не покупные, трафик из Яндекс Директ, вКонтакте и из Facebook. Размещал публикации в группах, общался в комментариях с живыми людьми.
Вообщем делайте выводы, господа. Я свой уже сделал. Возможно кому-то эта информация будет полезной.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332136/


Метки:  

Один бит сломал, другой потерял: задачка по передаче данных

Воскресенье, 02 Июля 2017 г. 22:19 + в цитатник
Здравствуй, Хабр!

imageКартинка отсюда

Предлагаю в качестве тренировки для мозга следующую задачку:
Общаются между собой две машины. Шлют друг другу цифровые данные, натурально нули и единицы. Только канал между ними не очень: биты регулярно то искажаются, то пропадают вовсе. Допустим, наш канал из 20 бит в среднем один бит ломает, другой теряет. А теперь пишем алгоритм, наиболее оптимально эти данные передающий.



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

Вы скажете, серьезные ребята из IEEE и смежных организаций уже всё давно придумали, и будете правы. Но когда это мешало переизобретать, just for lulz? И вылезти ненадолго из зоны комфорта надежных и простых сокетов (Не подсматривая какие-нибудь RFC)? Тем более, делать это будем на JavaScript, в браузере, без сторонних библиотек, еще желательно чтобы на один экран влезло:

Вот прямо тут

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

Весь код исполняется локально. Подключен CodeMirror для редактора кода.
Пишем содержимое двух функций, периодически вызываемых на передающей (Sender \ Source) и (Receiver \ Destination) машинах.

В нашем распоряжении контекст this, имеющий аж 5 методов:

var runs = this.counter();

Счетчик того, сколько раз была вызвана основая функция. Помогает ориентироваться во времени, для отсчета таймуатов например.

var frame = this.getPayload(n);

Доступен на передающей машине. Считывает и возвращает следующие n бит полезной нагрузки.

this.write(frame);

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

var frame = this.read(n);

Считывает из входящего сетевого буфера до n бит. Если ничего нет, вернет пустой массив.

this.acceptPayload(frame);

Доступен на принимающей машине. Помещает frame в результирующий массив.

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

Я добавил несколько примеров исходного кода:
  • Tutorial — чуть более подробное описание возможностей передающих и принимающих машин.
  • No Corrections — простейший алгоритм, не следящей за целостностью передаваемых данных. Оверхед отсутствует, но целостность оставляет желать лучшего.
  • Naive 900% Overhead — думаю, понятно из названия. Шлем не торопясь по одному биту, продублированному десять раз. Работает более-менее стабильно (хотя изредка целостность нарушалась), но оверхед по нагрузке сети 900%.


Между первой идеей и последней точкой этой статьи пока что еще прошло меньше 12 часов, так что не обессудьте, если что пропустил. Пишите, поправлю как можно оперативней.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332134/


Метки:  

Метод оптимизации Нелдера — Мида. Пример реализации на Python

Воскресенье, 02 Июля 2017 г. 20:58 + в цитатник

Метки:  

[Из песочницы] Практическое использование в Go: организация доступа к базам данных

Воскресенье, 02 Июля 2017 г. 20:02 + в цитатник

Несколько недель назад кто-то создал топик на Reddit с просьбой:


Что бы Вы использовали в качестве лучшей практики Go для доступа к базе данных в (HTTP или других) обработчиках, в контексте веб-приложения?

Ответы, которые он получил, были разнообразными и интересными. Некоторые люди посоветовали использовать внедрение зависимостей, некоторые поддержали идею использования простых глобальных переменных, другие предложили поместить указатель пула соединений в x/net/context.


Что касается меня? Думаю что правильный ответ зависит от проекта.


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


В этом посте рассматрим четыре разных подхода к организации вашего кода и структурирование доступа к пулу соединений к базе данных.


Данный пост является вольным переводом оригинальной статьи. Автор статьи предлагает четыре подхода по организации доступа к БД в приложении написанном на golang


Глобальные переменные


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


Что бы код выглядел красивым и соответствовал принципу DRY (Don't Repeat Yourself — рус. не повторяйся), вы можете использовать функцию инициализации, которая будет устанавливать глобальный пул соединений из других пакетов и тестов.


Мне нравятся конкретные примеры, давайте продолжим работу с базой данных онлайн магазина и кодом из моего предыдущего поста. Мы рассмотрим создание простых приложений с MVC (Model View Controller) подобной структурой — с обработчиками HTTP в основном приложении и отдельным пакетом моделей содержащей глобальные переменные для БД, функцию InitDB(), и нашу логику по базе данных.


bookstore
+-- main.go
+-- models
    +-- books.go
    +-- db.go

Код

File: main.go


package main

import (
    "bookstore/models"
    "fmt"
    "net/http"
)

func main() {
    models.InitDB("postgres://user:pass@localhost/bookstore")

    http.HandleFunc("/books", booksIndex)
    http.ListenAndServe(":3000", nil)
}

func booksIndex(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), 405)
        return
    }
    bks, err := models.AllBooks()
    if err != nil {
        http.Error(w, http.StatusText(500), 500)
        return
    }
    for _, bk := range bks {
        fmt.Fprintf(w, "%s, %s, %s, lb%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
    }
}

File: models/db.go


package models

import (
    "database/sql"
    _ "github.com/lib/pq"
    "log"
)

var db *sql.DB

func InitDB(dataSourceName string) {
    var err error
    db, err = sql.Open("postgres", dataSourceName)
    if err != nil {
        log.Panic(err)
    }

    if err = db.Ping(); err != nil {
        log.Panic(err)
    }
}

File: models/books.go


package models

type Book struct {
    Isbn   string
    Title  string
    Author string
    Price  float32
}

func AllBooks() ([]*Book, error) {
    rows, err := db.Query("SELECT * FROM books")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    bks := make([]*Book, 0)
    for rows.Next() {
        bk := new(Book)
        err := rows.Scan(&bk.Isbn, &bk.Title, &bk.Author, &bk.Price)
        if err != nil {
            return nil, err
        }
        bks = append(bks, bk)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return bks, nil
}

Если вы запустите приложение и выполните запрос на /books вы должны получить ответ похожий на:


$ curl -i localhost:3000/books
HTTP/1.1 200 OK
Content-Length: 205
Content-Type: text/plain; charset=utf-8

978-1503261969, Emma, Jayne Austen, lb9.44
978-1505255607, The Time Machine, H. G. Wells, lb5.99
978-1503379640, The Prince, Niccol`o Machiavelli, lb6.99

Использование глобальных переменных потенциально подходит, если:


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

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


Один из вариантов это вызывать InitDB несколько раз, но такой подход быстро может стать неуклюжим и это выглядит немного странным (легко забыть инициализировать пул коннектов и получить панику вызова пустого указателя во время выполнения). Второй вариант это создание отдельного конфигурационного пакета с экспортируемой переменной БД и импортировать "yourproject/config" в каждый файл, где это необходимо. Если не понятно о чем идет речь, Вы можете посмотреть пример.


Внедрение зависимости


Во втором подходе мы рассмотрим внедрение зависимости. В нашем примере, мы явно хотим передать указатель в пул соединений, и в наши обработчики HTTP и затем в нашу логику базы данных.


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


Для проектов, в которых все ваши обработчики находятся в одном пакете, аккуратный подход состоит в том, чтобы все элементы находились в пользовательском типе Env:


type Env struct {
    db *sql.DB
    logger *log.Logger
    templates *template.Template
}

… и затем определить ваши обработчики и методы там же, где и Env. Это обеспечивает чистый и характерный способ для создания пула соединений (и для других элементов) для ваших обработчиков.


Полный пример:


Код

File: main.go


package main

import (
    "bookstore/models"
    "database/sql"
    "fmt"
    "log"
    "net/http"
)

type Env struct {
    db *sql.DB
}

func main() {
    db, err := models.NewDB("postgres://user:pass@localhost/bookstore")
    if err != nil {
        log.Panic(err)
    }
    env := &Env{db: db}

    http.HandleFunc("/books", env.booksIndex)
    http.ListenAndServe(":3000", nil)
}

func (env *Env) booksIndex(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), 405)
        return
    }
    bks, err := models.AllBooks(env.db)
    if err != nil {
        http.Error(w, http.StatusText(500), 500)
        return
    }
    for _, bk := range bks {
        fmt.Fprintf(w, "%s, %s, %s, lb%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
    }
}

File: models/db.go


package models

import (
    "database/sql"
    _ "github.com/lib/pq"
)

func NewDB(dataSourceName string) (*sql.DB, error) {
    db, err := sql.Open("postgres", dataSourceName)
    if err != nil {
        return nil, err
    }
    if err = db.Ping(); err != nil {
        return nil, err
    }
    return db, nil
}

File: models/books.go


package models

import "database/sql"

type Book struct {
    Isbn   string
    Title  string
    Author string
    Price  float32
}

func AllBooks(db *sql.DB) ([]*Book, error) {
    rows, err := db.Query("SELECT * FROM books")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    bks := make([]*Book, 0)
    for rows.Next() {
        bk := new(Book)
        err := rows.Scan(&bk.Isbn, &bk.Title, &bk.Author, &bk.Price)
        if err != nil {
            return nil, err
        }
        bks = append(bks, bk)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return bks, nil
}

Или использовать замыкания...


Если вы не хотите определять ваши обработчики и методы в Env, альтернативным подходом будет использование логики обработчиков в замыкании и закрытие переменной Env следующим образом:


Код

File: main.go


package main

import (
    "bookstore/models"
    "database/sql"
    "fmt"
    "log"
    "net/http"
)

type Env struct {
    db *sql.DB
}

func main() {
    db, err := models.NewDB("postgres://user:pass@localhost/bookstore")
    if err != nil {
        log.Panic(err)
    }
    env := &Env{db: db}

    http.Handle("/books", booksIndex(env))
    http.ListenAndServe(":3000", nil)
}

func booksIndex(env *Env) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "GET" {
            http.Error(w, http.StatusText(405), 405)
            return
        }
        bks, err := models.AllBooks(env.db)
        if err != nil {
            http.Error(w, http.StatusText(500), 500)
            return
        }
        for _, bk := range bks {
            fmt.Fprintf(w, "%s, %s, %s, lb%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
        }
    })
}

Внедрение зависимостей является хорошим подходом, когда:


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

Еще раз, вы можете использовать этот подход, если ваши обработчики и логика базы данных будут распределены по нескольким пакетам. Один из способов добиться этого — создать отдельный конфигурационный пакет, экспортируемый тип Env. Один из способов использования Env в примере выше. А так же простой пример.


Использование интерфейсов


Мы будем использовать пример внедрения зависимостей немного позже. Давайте изменим пакет моделей так, что бы он возвращал пользовательский тип БД (который включает sql.DB) и внедрим логику базы данных в виде типа DB.


Получаем двойное преимущество: сначала мы получаем чистую структуру, но что еще важнее он открывает потенциал для тестирования нашей базы данных в виде юнит тестов.


Давайте изменим пример и включим новый интерфейс Datastore, который реализовывает некоторые методы, в нашем новом типе DB.


type Datastore interface {
    AllBooks() ([]*Book, error)
}

Мы можем использовать данный интерфейс во всем нашем приложении. Обновленный пример.


Код

File: main.go


package main

import (
    "fmt"
    "log"
    "net/http"
    "bookstore/models"
)

type Env struct {
    db models.Datastore
}

func main() {
    db, err := models.NewDB("postgres://user:pass@localhost/bookstore")
    if err != nil {
        log.Panic(err)
    }

    env := &Env{db}

    http.HandleFunc("/books", env.booksIndex)
    http.ListenAndServe(":3000", nil)
}

func (env *Env) booksIndex(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, http.StatusText(405), 405)
        return
    }
    bks, err := env.db.AllBooks()
    if err != nil {
        http.Error(w, http.StatusText(500), 500)
        return
    }
    for _, bk := range bks {
        fmt.Fprintf(w, "%s, %s, %s, lb%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
    }
}

File: models/db.go


package models

import (
    _ "github.com/lib/pq"
    "database/sql"
)

type Datastore interface {
    AllBooks() ([]*Book, error)
}

type DB struct {
    *sql.DB
}

func NewDB(dataSourceName string) (*DB, error) {
    db, err := sql.Open("postgres", dataSourceName)
    if err != nil {
        return nil, err
    }
    if err = db.Ping(); err != nil {
        return nil, err
    }
    return &DB{db}, nil
}

File: models/books.go


package models

type Book struct {
    Isbn   string
    Title  string
    Author string
    Price  float32
}

func (db *DB) AllBooks() ([]*Book, error) {
    rows, err := db.Query("SELECT * FROM books")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    bks := make([]*Book, 0)
    for rows.Next() {
        bk := new(Book)
        err := rows.Scan(&bk.Isbn, &bk.Title, &bk.Author, &bk.Price)
        if err != nil {
            return nil, err
        }
        bks = append(bks, bk)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return bks, nil
}

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


Код
package main

import (
    "bookstore/models"
    "net/http"
    "net/http/httptest"
    "testing"
)

type mockDB struct{}

func (mdb *mockDB) AllBooks() ([]*models.Book, error) {
    bks := make([]*models.Book, 0)
    bks = append(bks, &models.Book{"978-1503261969", "Emma", "Jayne Austen", 9.44})
    bks = append(bks, &models.Book{"978-1505255607", "The Time Machine", "H. G. Wells", 5.99})
    return bks, nil
}

func TestBooksIndex(t *testing.T) {
    rec := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/books", nil)

    env := Env{db: &mockDB{}}
    http.HandlerFunc(env.booksIndex).ServeHTTP(rec, req)

    expected := "978-1503261969, Emma, Jayne Austen, lb9.44\n978-1505255607, The Time Machine, H. G. Wells, lb5.99\n"
    if expected != rec.Body.String() {
        t.Errorf("\n...expected = %v\n...obtained = %v", expected, rec.Body.String())
    }
}

Контекст в области видимости запроса (Request-scoped context)


Наконец-то давайте посмотрим на использование контекста в области видимости запроса и передачи пула подключений к базе данных. В частности мы будем использовать пакет x/net/context.


Лично я не фанат переменных уровня приложения в контексте области видимости запроса — он выглядит неуклюжим и обременительным для меня. Документация пакета x/net/context так же советует:


Используйте значения контекста только для области видимости данных внутри запроса, которые передают процессы и точки входа API, а не для передачи необязательных параметров функции.

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


Давайте адаптируем пример книжного магазина в последний раз, передавая контекст для наших обработчиков, используя шаблон, предложенный в замечательной статье от Joe Shaw


Код

File: main.go


package main

import (
  "bookstore/models"
  "fmt"
  "golang.org/x/net/context"
  "log"
  "net/http"
)

type ContextHandler interface {
  ServeHTTPContext(context.Context, http.ResponseWriter, *http.Request)
}

type ContextHandlerFunc func(context.Context, http.ResponseWriter, *http.Request)

func (h ContextHandlerFunc) ServeHTTPContext(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
  h(ctx, rw, req)
}

type ContextAdapter struct {
  ctx     context.Context
  handler ContextHandler
}

func (ca *ContextAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  ca.handler.ServeHTTPContext(ca.ctx, rw, req)
}

func main() {
  db, err := models.NewDB("postgres://user:pass@localhost/bookstore")
  if err != nil {
    log.Panic(err)
  }
  ctx := context.WithValue(context.Background(), "db", db)

  http.Handle("/books", &ContextAdapter{ctx, ContextHandlerFunc(booksIndex)})
  http.ListenAndServe(":3000", nil)
}

func booksIndex(ctx context.Context, w http.ResponseWriter, r *http.Request) {
  if r.Method != "GET" {
    http.Error(w, http.StatusText(405), 405)
    return
  }
  bks, err := models.AllBooks(ctx)
  if err != nil {
    http.Error(w, http.StatusText(500), 500)
    return
  }
  for _, bk := range bks {
    fmt.Fprintf(w, "%s, %s, %s, lb%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
  }
}

File: models/db.go


package models

import (
    "database/sql"
    _ "github.com/lib/pq"
)

func NewDB(dataSourceName string) (*sql.DB, error) {
    db, err := sql.Open("postgres", dataSourceName)
    if err != nil {
        return nil, err
    }
    if err = db.Ping(); err != nil {
        return nil, err
    }
    return db, nil
}

File: models/books.go


package models

import (
    "database/sql"
    "errors"
    "golang.org/x/net/context"
)

type Book struct {
    Isbn   string
    Title  string
    Author string
    Price  float32
}

func AllBooks(ctx context.Context) ([]*Book, error) {
    db, ok := ctx.Value("db").(*sql.DB)
    if !ok {
        return nil, errors.New("models: could not get database connection pool from context")
    }

    rows, err := db.Query("SELECT * FROM books")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    bks := make([]*Book, 0)
    for rows.Next() {
        bk := new(Book)
        err := rows.Scan(&bk.Isbn, &bk.Title, &bk.Author, &bk.Price)
        if err != nil {
            return nil, err
        }
        bks = append(bks, bk)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return bks, nil
}

P.S. Автор перевода будет благодарен за указанные ошибки и неточности перевода.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332122/


Метки:  

Квантовый компьютер: большая игра на повышение. Лекция в Яндексе

Воскресенье, 02 Июля 2017 г. 19:05 + в цитатник
Мы то и дело слышим, что не за горами эпоха активного использования квантовых вычислений, что такие системы уже скоро станут доступны специалистам, включая аналитиков данных. Но сколько осталось ждать на самом деле? Научный сотрудник Российского квантового центра Алексей Фёдоров вводит в курс дела и рассказывает, как идут дела с разработкой квантовых компьютеров.




Под катом — расшифровка и часть слайдов Алексея.



Всем добрый день. Я хочу поблагодарить организаторов. Раз квантовые технологии стали темой обсуждения в таком формате, значит эта тема воспринимается, начинает звучать на достаточно высоком уровне. Такие компании, как Яндекс, — лидеры в IT-индустрии, и очень здорово, что в их повестке дня и в их сфере интересов появляется квантовая технология. Это некий мировой тренд. Я очень рад, что мы сегодня выступаем здесь.

Я работаю в Российском квантовом центре и представляю команду, которая в рамках Российского квантового центра занимается разработкой IT-продуктов. Квантовый центр стартовал как фундаментальный научный институт, но очень быстро в процессе развития стал заниматься прикладными исследованиями в сфере квантовых технологий, одно из них — квантовая криптография, проект, которым мы занимаемся. И наши спикеры — я, Евгений, Максим, Николай — мы все представляем эту команду, поэтому будем рады не просто формально поговорить о заявленной теме, но и повзаимодействовать с той точки зрения, что наши интересы довольно сильно перекрываются. Будем рады любым вопросам в сфере квантовых технологий, не ограничиваясь квантовыми компьютерами.



На свой центральный слайд я поместил эту картину. Знаете ли вы, что это за символ?

Это статуя, которая располагается рядом с Франкфуртской фондовой биржей и означает две стратегии того, как можно играть на рынке: на повышение и на понижение. Квантовый компьютер является краеугольной квантовой технологией, и сейчас к его развитию подключается не только государство как основной инвестор фундаментальных исследований, но и IT-компании, крупные производители информационных технологий. Они играют в этом смысле на повышение, вкладывают большие средства и усилия в разработку квантового компьютера, потому что видят в нем некую новую возможность для революции в информационных технологиях. И основной месседж моего доклада — то, почему квантовые технологии и квантовый компьютер — это очень круто, и почему он не просто является интересной и красивой идеей, а действительно предоставляет огромные возможности для развития всей индустрии IT.

Что я как человек, большую часть времени занимающийся физикой, слышал от специалистов по программированию и IT? Я слышал о следующих трендах.



Мы часто слышим о большом количестве новых стартапов или таких проектах уже существующих компаний, как интернет вещей, машинное обучение, big data и информационная безопасность. Эти тренды звучат везде, задают информационные поводы, однако за ними, кроме красоты алгоритмических решений, красоты математики, красоты программирования, стоит реальное железо. И это железо уже квантовое.

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



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

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



И все это лежит в основе таких применений, как GPS, будущие медицинские сенсоры, новые материалы, которые и востребованы в трендах информационных технологий.



Все технологически успешные страны в данный момент активно занимаются развитием квантовых технологий. В эти исследования вкладывается огромное количество средств, создаются специальные программы поддержки квантовых технологий. Если вернуться в историю — все мы помним космическую гонку между СССР и США.



В квантовой гонке участвуют не только государства, но и частные компании. Суммарно Google, IBM, Intel и Microsoft вложили около 0,5 млрд долларов в развитие квантовых компьютеров за последнее время, создали крупные лаборатории и исследовательские центры.

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



Здесь представлена карта квантовых центров мира, которые являются точками компетенции и точками роста. В 2011 году на этой карте появился Российский квантовый центр, который я представляю. Это фундаментальный научный институт, который превратился в экосистему, где фундаментальные исследования сосуществуют и стимулируют развитие прикладных технологий.



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

Здесь представлен всем известный закон Мура, который выражает некий тренд роста производительности уже существующих компьютеров. Мы знаем, что год от года компьютеры становятся мощнее, однако за этим стоит уменьшение элементной базы, ее миниатюризация. Благодаря нашему прогрессу в создании транзисторов мы можем создавать их всё меньше, и располагать всё плотнее на элементарную единицу площади.

Но, конечно, у этого тренда есть фундаментальный предел. Он обусловлен физикой.

Вряд ли мы сможем создать транзистор размером с один атом. Для этого требуется хотя бы несколько атомов. Насколько я знаю, самый миниатюрный в мире транзистор состоит из семи атомов. Однако если закон Мура будет продолжаться так, как он продолжается, то в 2020 году нам потребуется создавать компьютеры с транзистором в один атом. И это кажется невозможным.

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



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

Первая — задача оптимизации поиска. Она достаточно плохо решается на классических компьютерах.

Вторая, наиболее близкая мне и важная — моделирование сложных физических систем. Например, используя моделирование из первых принципов, очень тяжело промоделировать какую-то достаточно сложную физическую систему. Это требует огромного количества ресурсов.

Есть разные прикладные задачи, которые для широкой аудитории кажутся достаточно отдаленными от практики, такие как разложение на простые множители. Однако всем известна их фундаментальная важность для различных технологий.

И даже если мы будем подбираться близко к пределу закона Мура, то такие задачи решить мы не сможем.

Конечно, сможем. Но для этого будет требоваться много времени.

Хотелось бы получить физическую систему, которая позволит решать эти задачи более оптимальным образом. Оказывается, она существует, и возникает в контексте квантового компьютера.

Я подсмотрел в нобелевских лекциях Андрея Гейма, одного из создателей графена, такую интересную идею. Он рассказывал, как он пришел к идее заняться исследованиями в области графена, пользуясь мысленными облачками. Он о чем-то слышал, это как-то обрабатывалось у него в голове, и когда он накопил достаточно мысленных облачков, у него создалась концепция, что заниматься двумерным углеродом и графеном очень интересно.



Я считаю, что к концепции квантового компьютера привели три мысленных облачка.

Первое. Непонятно, как быть с вычислительно сложными задачами. Неужели нам всегда будет действительно трудно промоделировать какую-то физическую систему? Или действительно ли задача разложения на простые множители не имеет достаточно эффективного классического алгоритма?

Второй момент более физичный. Он связан с исследованием вопроса о том, что за ограничения квантовая физика как одна из наиболее точных физических теорий накладывает на процесс вычислений. Например, Ричардом Фейнманом и Чарльзом Беннеттом исследовался вопрос, какое минимальное количество энергии или теплоты выделяется при совершении одной элементарной операции. Можно ли создать компьютер, наиболее продуктивный и наиболее экономный с точки зрения выделения энергии? Как создать самый миниатюрный компьютер?

Третий вопрос больше к математике. Он связан с исследованием вопроса о том, какие интересные свойства приобретает теория информации, если перейти от описания классических объектов в классической теории вероятности к описанию квантовых объектов, к квантовой теории информации. Изображенная здесь банкнота иллюстрирует идею квантовых денег. Это одна из первых концепций, которая подразумевает использование каких-то практических информационных технологий. Дело в том, что если создать банкноты, в которых подлинность будет обеспечиваться созданием специальных квантовых состояний, то за счет специфических свойств квантовых систем такие банкноты нельзя будет подделать.

Второй портрет здесь — портрет нашего соотечественника Александра Холево. Он известен во всем мире как создатель одной из фундаментальных теорем — квантовой теоремы информации или теоремы Холево. Здесь, в основании квантовой теории информации, нам есть чем гордиться. Российские исследователи сохраняют хорошую традицию интересных публикаций, интересных результатов в этой области.



Что дает квантовая физика? Какие интересные следствия и фишки у нее есть, которые могли бы быть полезны с точки зрения теории информации или будущих вычислений?

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

Второй свойство — свойство квантовой запутанности. В квантовом мире частицы могут проявлять очень сильные корреляции, то есть их свойства могут быть очень сильно завязаны между собой, даже если эти частицы достаточно сильно пространственно удалены. Объясняя концепцию квантовой запутанности, часто приводят пример мысленного эксперимента, когда у нас была какая-то частица со спином 0, она распадается на две частицы, одну мы оставляем у себя в лаборатории, вторую отправляем в Туманность Андромеды, и, измеряя спин первой частицы, мы точно узнаем спин частицы в Туманности Андромеды. Это и есть в некотором смысле проявление свойств квантовой запутанности, сильных квантовых корреляций, которые могут быть полезны с точки зрения вычислений.

Третья вещь — хрупкость. Квантовые состояния по сравнению с классическими являются достаточно хрупкими. И процесс измерения как раз и является процессом возмущения. Вопрос в том, детерменированно ли этот процесс возмущения происходит. Это один из ключевых челленджей на пути к созданию квантовых технологий. Очень сложно создать большую квантовую систему, элементы которой будут, с одной стороны, достаточно хорошо взаимодействовать между собой и при этом будут достаточно хорошо защищены от окружения, которое может их разрушить.

Еще один интересный аспект — теорема о запрете клонирования. Это запрещающая теорема. Если в квантовом мире есть произвольное квантовое состояние, заранее неизвестное, то его нельзя скопировать, в отличие от классической информации. Если есть классический сигнал, его всегда можно скопировать. В квантовом мире произвольное квантовое состояние скопировать нельзя. И это радикальное отличие квантовой информации от классической.



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

Система битов, которая нам привычна в классических компьютерах, заменяется на систему кубитов. Это двухуровневая квантовая система или система типа орел-решка, когда есть два возможных состояни, и до момента измерения система находится в суперпозиции: одновременно и в этом состоянии, и в этом с какой-то вероятностью.

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

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

Это то, из чего могут состоять кубиты. Дело в том, что кубиты могут состоять из огромного многообразия различных физических систем. Мы привыкли, что бит кодируется за счет уровня напряжения. В квантовой физике есть многообразие физических систем. Это и частица света, и частица материи, и ядерные спины, и твердотельные системы, которые могут находиться в таком интересном состоянии суперпозиции. Они обладают различными преимуществами и различными недостатками. Вот ключевой на сегодняшний день факт: никто до конца не понимает, на какой физической, элементной базе будет построен квантовый компьютер в итоге. Есть уверенность в том, что так называемые твердотельные системы или системы со сверхпроводниковыми кубитами являются одними из лидеров, однако около месяца назад проводилось соревнование между двумя квантовыми компьютерами, построенными на разных физических принципах, и оно не выявило какого-то радикального преимущества одной системы над другой. Одни системы лучше масштабируются, другие легче контролировать, третьи лучше защищены от декогеренции, от процесса взаимодействия с окружением.



Давайте в рамках лекции ограничимся двумя возможными картинами. Первая — поляризация света. Это очень просто себе представлять, частица света — очень простая двухуровневая физическая модель, и она очень популярна в другом приложении квантовых технологий: в так называемых квантовых коммуникациях. Вторая картина — спин. Спин может быть вверх или вниз, это и есть кубит. До тех пор, пока мы не измерили, это состояние суперпозиции. Плюс-минус одна вторая.

То, о чем я уже сказал, — интересный процесс, который возникает в результате квантовых вычислений. Возникает новое квантовое состояние.

На вход подали некоторую суперпозицию нуля и единицы, провели с ней многократные процедуры. На выходе есть суперпозиция нуля и единицы.

Чтобы получить ответ, нам нужно провести измерение, и его нужно проводить многократно. Квантовые вычисления подразумевают, что многократно на вход подается некоторое заранее известное квантовое состояние. Над ним производится процедура преобразования и потом измерения. И измерение в итоге дает ответ.

Эта процедура квантовых измерений достаточно сложная. Это холиварная тема даже для специалистов в области квантовой физики. Обращаю ваше внимание, что у нас есть регулярная рубрика на сайте N+1, которая называется «Квантовая азбука», и мы там обсуждаем такие тонкие вопросы, в том числе вопросы квантовых вычислений.

Зачем нам все это нужно? Что поможет нам сделать квантовый компьютер, какие интересные фишки он нам даст? Для чего вся эта свистопляска с заменой битов на кубиты, заменой логических операций и итоговых результатов вычислений? Что это дает?

Это дает преимущества в целом классе задач. Самая интересная для меня задача, которую квантовый компьютер решает хорошо, — задача факторизации дискретного логарифмирования, поскольку она лежит в основе систем с ассиметричной криптографией. И каждый раз, когда мы покупаем что-то в интернете, наши данные кредитной карты шифруются при помощи алгоритмов ассиметричной криптографии, криптографии с открытым ключом. Они, в свою очередь, базируются на таких задачах, в частности, как дискретное логарифмирование и разложение на простые множители. И в этом смысле квантовый алгоритм Шора — самый главный и самый интересный пример квантового алгоритма, потому что он решает практическую задачу экспоненциально быстрее, чем классический компьютер. В этом смысле квантовый компьютер — угроза существующей инфраструктуре информационной безопасности.

Как только он появится, любую систему открытого распределения ключа, которая базируется на таких задачах, можно будет взломать. С одной стороны, это плохо, это может привести к революции, и часто квантовый компьютер называют информационной бомбой XXI века. Но с другой стороны, на данный момент беспокоиться очень сильно не нужно, потому что алгоритм Шора требует универсального квантового компьютера, который может решать любую алгоритмически формулируемую задачу, и это очень сложно. Да, это дает колоссальные преимущества, но именно из-за факта, что квантовые системы хрупкие к внешнему воздействию, очень тяжело создать систему из достаточно большого количества кубит, чтобы над ними проводить все операции и потом измерения.

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

Сегодня об универсальном квантовом компьютере вам расскажет Евгений Киктенко именно в контексте алгоритмов. Какие алгоритмы интересны для квантового компьютера с точки зрения приложения? А Максим Ануфриев — с точки зрения решения задачи машинного обучения на универсальном квантовом компьютере.

Универсальный квантовый компьютер — сложная задача, и это очень важно понять. Нет даже однозначной временной оценки, когда он появится. В нынешнем году вокруг этой темы много хайпа, даже первый выпуск журнала Nature вышел, одна из статей — «Квантовые компьютеры выпрыгивают из лаборатории» с прогнозом, что уже в 2017 году может появиться какой-то коммерческий квантовый компьютер, способный решать действительно полезные задачи. Причем прогноз настолько оптимистичный, что якобы появится полноценный универсальный коммерческий квантовый компьютер для решения практических задач.

Хотя это очень спорный вопрос. Оценки экспертов варьируются от 5 до 25 лет. И в этом смысле очень сложно сказать, когда квантовый компьютер реально возникнет.

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

Есть еще одна разновидность квантовых компьютеров, которая называется квантовым симулятором. Он предназначен для решения еще более специфического класса задач — именно для моделирования других типов физических систем. Здесь достаточно большие перспективы — например, для поиска высокотемпературных сверхпроводников. Это материалы, которые могут проводить электрический ток без потерь при комнатной температуре. Пока теория для таких систем известна только для очень низких температур. Есть экспериментальные исследования, которые вдруг обнаруживают какие-то материалы, проводящие ток при разных температурах, например при –100 градусах Цельсия. Однако полноценной теории высокотемпературных сверхпроводников нет, потому что это вычислительно сложная задача. Квантовые симуляторы призваны воспроизводить некоторые свойства таких физических систем и давать некоторую подсказку, в каком направлении такие материалы можно искать. Но симуляторы также не являются универсальными квантовыми компьютерами.

Как я уже говорил, в исследования в области квантовых компьютеров уже вкладывают IT-компании. Очень классный пример — компания Google, которая просто переманила к себе Джона Мартиниса, одного из ведущих специалистов в области квантовых вычислений на сверхпроводящих кубитах. Джон Мартинис одновременно руководит несколькими направлениями. Одно из них — создание полноценного универсального квантового компьютера. Другое — исследование существующего квантового компьютера компании D-Wave и поиск тех задач, в которых он дает преимущество.

Кстати, Джон Мартинис будет в этом году на конференции Российского квантового центра. Там будет открытая лекция, где он о квантовом компьютере, который разрабатывается в Google, может рассказать просто всё.

Другой большой игрок — компания IBM. Она недавно анонсировала свою открытую онлайн-платформу для работы с их пятикубитным квантовым компьютером. В этом смысле программы для квантового компьютера может писать уже кто угодно, но, конечно, его потенциал ограничен тем, что кубитов достаточно мало.

Другое игроки на этом рынке — Microsoft и Intel, и у них выстрел с наиболее длинным прицелом — создание антопологического квантового компьютера. Из-за декогеренции, из-за того, что окружение вносит ошибки в процесс квантовых вычислений, часть ресурсов квантового компьютера нужно тратить на их исправление. Топологические квантовые системы позволяют избежать этого. На данный момент это очень фундаментально сложная научная концепция, за которой стоит достаточно много интересной математики. Это очень сильно отдалено на данный момент от какой-либо практической реализации. В частности, предсказываемые топологические состояния материи, которые интересны с точки зрения вычислений, еще не были обнаружены в эксперименте. Однако за базовые исследования в области следствий топологий для физики в 2016 году была присуждена Нобелевская премия.

Очень интересен следующий факт: мы привыкли, что классический компьютер — фактически одна и та же система, твердое тело, отвечающее за всю его функциональность. Мы и память, и вычисление проводим, фактически, на одной элементной базе. Однако квантовый компьютер позволяет нам сделать так называемую гибридную систему, которая будет брать от природы, от различных форм квантовой материи самое лучшее. Например, известно, что процессоры на данный момент, кажется, лучше всего строить с помощью сверхпроводниковых кубитов. Все интерфейсы, все то, благодаря чему квантовые компьютеры и их различные элементы будут общаться между собой, можно строить при помощи фотонов — частиц света. Это наилучший агент для передачи информации.

Конечно, в процессе вычисления иногда нужно хранить промежуточные результаты, потом проводить с ними дальнейшие операции. Лучшие показатели с точки зрения хранения квантовых состояний показывают атомные системы. Поэтому квантовый компьютер — большая и интересная фундаментальная задача, которая позволит скомбинировать, взять от природы лучшее и построить наиболее привлекательную, производительную и интересную гибридную систему.

В данный момент наша основная деятельность — работа не над квантовым компьютером, а над системой квантовых коммуникаций в рамках проекта QRate. И о квантовых коммуникациях мы еще будем рады поговорить. Квантовые коммуникации — щит, позволяющий нам защититься от меча квантового компьютера, создать систему распределения ключей, стойкость которых не зависит от вычислительных ресурсов злоумышленника. Спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332106/


Метки:  

[Из песочницы] Mocking в swift при помощи Sourcery

Воскресенье, 02 Июля 2017 г. 18:53 + в цитатник

Предисловие


В ходе разработки ios-приложения, перед разработчиком может встать задача unit-тестирования кода. Именно с такой задачей столкнулся я.


Задача


Допустим, у нас есть приложение с аутентификацией. За аутентификацию, в нём отвечает сервис аутентификации — AuthenticationService. Для примера, у него будут два метода, оба аутентифицируют пользователя, но один синхронный, а другой асинхронный:


protocol AuthenticationService {

    typealias Login = String
    typealias Password = String
    typealias isSucces = Bool

    /// Функция аутентификации пользователя
    ///
    /// - Parameters:
    ///   - login: Учётная запись
    ///   - password: Пароль
    /// - Returns: Успешность аутентификации
    func authenticate(with login: Login, and password: Password) -> isSucces

    /// Асинхронная функция аутентификации пользователя
    ///
    /// - Parameters:
    ///   - login: Учётная запись
    ///   - password: Пароль
    ///   - authenticationHandler: Callback(completionHandler) аутентификации
    func asyncAuthenticate(with login: Login, and password: Password, authenticationHandler: @escaping (isSucces) -> Void)

}

Имеется viewController, который будет использовать этот сервис:


class ViewController: UIViewController {

    var authenticationService: AuthenticationService!
    var login = "Login"
    var password = "Password"

    /// Обработчик аутентификации, используется для асинхронной аутентификации
    var aunthenticationHandler: ((Bool) -> Void) = { (isAuthenticated) in
        print("\nРезультат асинхронной функции:")
        isAuthenticated ? print("Добро пожаловать") : print("В доступе отказано")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        authenticationService = AuthenticationServiceImplementation() // Какая-то реализация сервиса аутентификации, нам не важно, т.к. тестировать мы будем viewController
        performAuthentication()
        performAsyncAuthentication()
    }

    func performAuthentication() {
        let isAuthenticated = authenticationService.authenticate(with: login, and: password)
        print("Результат синхронной функции:")
        isAuthenticated ? print("Добро пожаловать") : print("В доступе отказано")
    }

    func performAsyncAuthentication() {
        authenticationService.asyncAuthenticate(with: login, and: password, and: aunthenticationHandler)
    }

}

Нам нужно протестировать viewController.


Решение


Т.к. мы не хотим, чтобы наши тесты зависели от каки-либо ещё объектов, кроме класса нашего viewController'a, мы будем мокировать все его зависимости. Для этого сделаем заглушку сервиса аутентификации. Выглядела бы она примерно вот так:


class MockAuthenticationService: AuthenticationService {

    var emulatedResult: Bool? // То, что вернёт синхронная функция аутентификации
    var receivedLogin: AuthenticationService.Login? // Поле для проверки полученния логина
    var receivedPassword: AuthenticationService.Password? // Поле для проверки полученния пароля
    var receivedAuthenticationHandler: ((AuthenticationService.isSucces) -> Void)? // Обработчик, с помощью которого будем управлять возвращаемым значением при тестировании функции асинхронной аутентификации

    func authenticate(with login: AuthenticationService.Login,
                      and password: AuthenticationService.Password) -> AuthenticationService.isSucces {
        receivedLogin = login
        receivedPassword = password
        return emulatedResult ?? false
    }

    func asyncAuthenticate(with login: AuthenticationService.Login,
                           and password: AuthenticationService.Password,
                           and authenticationHandler: @escaping (AuthenticationService.isSucces) -> Void) {
        receivedLogin = login
        receivedPassword = password
        receivedAuthenticationHandler = authenticationHandler
    }

}

В ручную писать столько кода для каждой зависимости, очень не приятное занятие (особенно приятно переписывать их, когда у зависимостей меняется протокол). Я начал искать решение данной проблемы. Думал найти аналог mockito(подсмотрел у коллег занимающихся android-разработкой). В ходе поиска узнал, что swift поддерживает read-only рефлексию (в рантайме, мы можем только узнавать информацию об объектах, менять поведение объекта, мы не можем). Поэтому подобной библиотеки нет. Отчаявшись, я задал вопрос на тостере. Решение подсказали: Вячеслав Бельтюков и Человек с медведем (ManWithBear).


Мы будем генерировать моки при помощи Sourcery. Sourcery использует шаблоны для генерации кода. Имеются несколько стандартных, для наших целей подходит AutoMockable.


Приступим к делу:


1) Добавляем в наш проект pod 'Sourcery'.
2) Настраиваем RunScript для нашего проекта.


$PODS_ROOT/Sourcery/bin/sourcery --sources . --templates ./Pods/Sourcery/Templates/AutoMockable.stencil --output ./SwiftMocking

Где:


"$PODS_ROOT/Sourcery/bin/sourcery" — путь к исполняемому файлу Sourcery.
"--sources ." — Указание, что анализировать для кодогенерации (точка указывает на текущую папку проекта, то есть мы будем смотреть нужно ли сгенерировать моки для каждого файла нашего проекта).
"--templates ./Pods/Sourcery/Templates/AutoMockable.stencil" — путь к шаблону кодогенерации.
"--output ./SwiftMocking" — место, где будет хранится результат кодогенерации (наш проект называется SwiftMocking).


3) Добавлям файл AutoMockable.swift в наш проект:


/// Базовый протокол для протоколов, которые мы хотим мокировать
protocol AutoMockable {}

4) Протоколы, которые мы хотим мокировать, должны наследоваться от AutoMockable. В нашем случае наследуемся AuthenticationService'ом:


protocol AuthenticationService: AutoMockable {

5) Билдим проект. В папке путь к которой мы указали как параметр --ouput, сгенерируется файл AutoMockable.generated.swift, в котором будут лежать сгенерированные моки. Все последующие моки будут складываться в этот файл.


6) Добавляем этот файл в наш проект. Теперь мы можем использовать наши заглушки.


Давайте посмотрим, что сгенерировалось для протокола сервиса аутентификации.


class AuthenticationServiceMock: AuthenticationService {

    //MARK: - authenticate

    var authenticateCalled = false
    var authenticateReceivedArguments: (login: Login, password: Password)?
    var authenticateReturnValue: isSucces!

    func authenticate(with login: Login, and password: Password) -> isSucces {
        authenticateCalled = true
        authenticateReceivedArguments = (login: login, password: password)
        return authenticateReturnValue
    }
    //MARK: - asyncAuthenticate

    var asyncAuthenticateCalled = false
    var asyncAuthenticateReceivedArguments: (login: Login, password: Password, authenticationHandler: (isSucces) -> Void)?

    func asyncAuthenticate(with login: Login, and password: Password, and authenticationHandler: @escaping (isSucces) -> Void) {
        asyncAuthenticateCalled = true
        asyncAuthenticateReceivedArguments = (login: login, password: password, authenticationHandler: authenticationHandler)
    }
}

Прекрасно. Теперь мы можем использовать заглушки в наших тестах:


import XCTest
@testable import SwiftMocking

class SwiftMockingTests: XCTestCase {

    var viewController: ViewController!
    var authenticationService: AuthenticationServiceMock!

    override func setUp() {
        super.setUp()
        authenticationService = AuthenticationServiceMock()
        viewController = ViewController()
        viewController.authenticationService = authenticationService
        viewController.login = "Test login"
        viewController.password = "Test password"
    }

    func testPerformAuthentication() {
        // given
        authenticationService.authenticateReturnValue = true

        // when
        viewController.performAuthentication()

        // then
        XCTAssert(authenticationService.authenticateReceivedArguments?.login == viewController.login, "Логин не был передан в функцию аутентификации")
        XCTAssert(authenticationService.authenticateReceivedArguments?.password == viewController.password, "Пароль не был передан в функцию аутентификации")
        XCTAssert(authenticationService.authenticateCalled, "Не произошёл вызова функции аутентификации")
    }

    func testPerformAsyncAuthentication() {
        // given
        var isAuthenticated = false
        viewController.aunthenticationHandler = { isAuthenticated = $0 }

        // when
        viewController.performAsyncAuthentication()
        authenticationService.asyncAuthenticateReceivedArguments?.authenticationHandler(true)

        // then
        XCTAssert(authenticationService.asyncAuthenticateCalled, "Не произошёл вызов асинхронной функции аутентификации")
        XCTAssert(authenticationService.asyncAuthenticateReceivedArguments?.login == viewController.login, "Логин не был передан в асинхронную функцию аутентификации")
        XCTAssert(authenticationService.asyncAuthenticateReceivedArguments?.password == viewController.password, "Пароль не был передан в асинхронную функцию аутентификации")
        XCTAssert(isAuthenticated, "Контроллер не обрабтывает результат аутентификации")
    }

}

Заключение


Sourcery пишет за нас заглушки, экономя тем самым наше время. У этой утилиты имеются и другие применения: генерация Equatable расширений для структур в наших проектах (чтобы мы могли сравнивать объекты этих структур).


Полезные ссылки


-> Проект
-> Sourcery на github
-> Документация sourcery

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332120/


Метки:  

Практика применения технологий виртуальной и дополненной реальности

Воскресенье, 02 Июля 2017 г. 18:37 + в цитатник
В этой статье мы рассмотрим сложные и важные вопросы о том, существует ли индустрия виртуальной и дополненной реальности; кому это нужно; что делать, когда вы сделали очередное приложение по демонстрации недвижимости в виртуальной реальности, а его никто не покупает или пошли в индустрию развлечений, а там настолько большая конкуренция, что протолкнуться невозможно.
Материал подготовлен на базе лекции Василия Рыжонкова, которая проходила на конференции VR-Today в рамках нашей образовательной программы «Менеджмент игровых проектов» в Высшей школе бизнес-информатики НИУ ВШЭ. Конспект и видео и под катом.



Василий является основателем компании ARenaLab, которая разрабатывает различные продукты и решения в области виртуальной и дополненной реальности. В 2015 году Василий решил сделать систему полного погружения, но теперь этот проект как бизнес не существует. Сейчас компания работает в b2b (360-маркетинг, недвижимость, индустриальная ниша и рынок развлечений) и b2c рынках. Второе направление работы Василия — развитие сети VR-парков в России в рамках компании ARena Space.

Что определяет индустрию? Деньги и спрос. В 1992 году была третья VR-волна, вышел Nintendo Virtual Boy, который продался в мире в количестве 300 000 — 400 000, этот эксперимент был признан неудавшимся. Сегодня продано около 700 000 HTC Vive и Oculus. Проблема в том, что шлемов было продано много, а потом индустрия свернулась из-за того, что индустрия не оправдала ожиданий потребителя. Пока это не станет удобным, это не будет массовым рынком.



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

Зачем ставить дорогие системы, связанные с виртуальной реальностью в недвижимости, если можно ограничиться простым Samsung Gear VR?
Нужно ли демонстрировать вашу квартиру в VR, если можно просто показать клиенту буклет?
Важно понимать, даст ли это экономический эффект.

2017 год — это проверка гипотез. Очень много идей уже отметается. Важно решать реальные проблемы.

VR LA, CEO Unity:
“Вызовы, которые стоят перед разработчиками и теми, кто применяет VR в индустрии и в бизнесе, заключаются не в том, что вы создадите какое-то решение, а в том, насколько это решение реально оправдано и дает экономическую эффективность конкретному заказчику”. Экономическая эффективность складывается из трех факторов: улучшение процессов, увеличение выручки заказчика, снизились ли косты.



Рынок дополненной и виртуальной реальности сложится. Но огромное количество компаний умрут, потому что не успеют выйти на рынок. Очень важно быть умнее чем коллеги. Мы находимся в России, где 70% экономики — государственные. Поэтому успешными будут те компании, которые так или иначе связаны с государством. Например, Vizerra.
Или возьмем рынок индустрии развлечений. В России он составляет приблизительно 50 миллиардов рублей. При этом в одних только парках Диснейленда 17 миллионов посетителей в год. Средний чек, который там оставляет семья — 220-250 долларов без проживания в отеле.

9 рынков, в которых так или иначе разрабатываются различные решения в VR и AR:
  • Маркетинг
  • Недвижимость
  • Промышленность
  • Развлечения
  • Спорт
  • Публицистика
  • Культура и туризм
  • Образование
  • Медицина




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

Выбирая, на какой рынок идти, нужно подумать:
Об объеме этого рынка в географии (Россия — мир)
Как быстро вы сможете представить какое-то решение для этого рынка и внедрить его
Достаточна ли емкость рынка


Маркетинг — это самый очевидный кейс в VR, потому что с этого все начинали. Презентовали автомобили, 360-видео и так далее.

Самый известный кейс в маркетинге — это McDonald’s. Они делают активации клиентов, чтобы они возвращались, через VR. Вы смотрите ролик-игру, зарабатываете баллы, которые идут на ваш личный счет и получаете скидку на продукты.

Marriott. Индустрия отелей, которая привлекает дополнительных клиентов. Они продают свой отель в местах, где много траффика. Они поставили специальные будки, в которых транслировали рекламу отелей.

Volvo. Презентации о новых автомобилях. Маркетинговые активации — новый способ подарить эмоции людям, но есть ли в этом экономический смысл, непонятно. Насколько вложенный бюджет окупился и увеличилась ли конверсия в продажи — неизвестно.

IBM In-Store AR Shopping App. Сразу 2 направления VR и AR. Самый большой рынок в мире — это торговля. Мы постоянно покупаем товары. Туда идут очень многие компании.

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

В 70-е и 80-е годы появляются первые шлемы для пилотов авиации, которые дополняют информацию в режиме реального времени. Это дорогие шлемы, они существуют до сих пор. Цена шлемов, которые закупаются для US Army может варьироваться от 20 до 50 тысяч долларов.

Сейчас в промышленности очень часто используются дополненная реальность (в первую очередь) и виртуальная реальность (во вторую). Часто это делается для того, чтобы сотрудники прошли какие-то необходимые им навыки в VR в тех случаях, когда для этого требуется дорогостоящее оборудование или когда за этим стоят какие-то риски.

Существует доказанный факт эффективности обучения в VR. В Китае было проведено исследование с двумя группами школьников. Одна из них училась по учебникам, вторая — в VR. Успеваемость второй группы сразу после прохождения материала была на 20 % выше, а спустя 2 недели — на 15 %.


И в качестве завершения три основных тезиса:
  • Тезис номер один. Рынок виртуальной и дополненной реальности — это next big thing.
  • Тезис номер два — не скоро.
  • И третье, вам, как компаниям, которые разрабатывают что-то или применяют что-то в первую нужно думать, есть ли проблема, можно ли эту проблему решить по другому, планировать свои ресурсы так, чтобы успеть пройти от фазы идеи до продукта, который понравится клиенту. И самое главное, очень вам желаем успехов, работайте, развивайте рынок дополненной и виртуальной реальности, он невероятно интересный, и это один из рынков роста в IT-индустрии.


Посмотреть полную версию выступления Василия на VR-Today и ответы на вопросы зала по ее итогам вы можете в видеозаписи лекции:





Часть 2






Осенью у нас начинается обучение на нашей образовательной программе «Менеджмент игровых проектов», приглашаем приходить на наши открытые мероприятия: 4 августа лекция на тему "Игровая индустрия: менеджмент", 9 августа день открытых дверей, 11 августа лекция на тему "Игровая индустрия: художник в компьютерных играх", 18 августа лекция на тему "Игровая индустрия: маркетинг", 23 августа лекция "Геймдизайн: игровые механики".
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332118/


Метки:  

[recovery mode] Установка 3CX на Debian Linux 9 Stretch, обновление Session Border Controller и Call Flow Designer

Воскресенье, 02 Июля 2017 г. 17:25 + в цитатник

Установка 3CX на Debian Linux 9 Stretch


Возможно, вы уже слышали о выходе новой версии популярной Linux сборки Debian 9 Stretch. В данный момент, если вы попытаетесь установить Linux версию 3CX на эту сборку, то получите ошибку зависимостей модулей. Дело в том, что текущая версия 3CX создавалась под систему Debian 8, поэтому они использует зависимости (необходимые вспомогательные файлы) Jessie, которые отсутствуют в репозитории Stretch.

Но сейчас мы расскажем, как все таки установить 3CX для Debian 9 Stretch!

Прежде всего хотим предупредить, что текущая версия 3CX не до конца протестирована с Debian 9, поэтому мы настоятельно не рекомендуем устанавливать такую конфигурацию в рабочем окружении. Описываемый метод позволяет обойти ошибки зависимостей Linux, но не гарантирует стопроцентной работы системы после установки. С другой стороны, ваши отзывы очень помогут нам быстрее выпустить готовую версию 3CX для Debian 9!

Также хотим вас попросить не обновлять систему Jessie на Stretch! Несмотря на то, что технически это возможно и часто рекомендуется, мы еще не добавили корректные зависимости в пакет установки 3CX. Обновление появится в ближайшие недели, а тем временем установка 3CX возможна только на новую инсталляцию Debian 9 Stretch.

Установка 3CX на Debian Linux 9


Для начала установки скачайте ISO образ Debian 9 Stretch и установите его как обычно.

Далее необходимо добавить репозиторий Debian 8 Jessie в список доступных репозиториев Debian 9 Stretch и установить из него необходимые пакету 3CX зависимости libicu52, libssl1.0.0 и libcurl3=7.38.0-4+deb8u5. Именно они вызывают появление ошибки при стандартной установке 3CX. Выполним команду:

echo 'deb http://ftp.de.debian.org/debian/ jessie main' | tee -a /etc/apt/sources.list
apt-get update
apt-get install libicu52 libssl1.0.0 libcurl3=7.38.0-4+deb8u5

Чтобы в будущем не возникло путаницы, можно удалить репозиторий Jessie из списка доступных в Stretch.

Затем устанавливаем 3CX на Linux набором стандартных команд, как описано в документации.

wget -O- http://downloads.3cx.com/downloads/3cxpbx/public.key | apt-key add -
echo "deb http://downloads.3cx.com/downloads/3cxpbx/ /" | tee /etc/apt/sources.list.d/3cxpbx.list
apt-get update
apt-get install 3cxpbx



Если вы увидели сообщение, как на скриншоте выше, значит 3CX установлена успешно. Можно переходить к Мастеру настройки АТС и тестировать систему.

Обновление 3CX Session Border Controller


К большому сожалению, наши клиенты, использующие бесплатную утилиту 3CX Session Border Controller (которая обычно используется для подключения офиса клиента к облачной инсталляции 3CX) с понедельника не могли подключиться к серверам 3CX.

Проблема возникала, если на SBС было включено шифрование трафика.

image

Проблема с подключением была обнаружена и устранена менее чем за сутки. Поэтому мы просим вас как можно скорее обновить свои инсталляции 3CX SBC, если вы еще этого не сделали (речь идет о системах 3CX v15 и 15.5)

3CX SBC для Windows


  1. Скачайте 3CX SBC и установите его по инструкции.
  2. Шифрование трафика может быть включено.

3CX SBC для Debian и Raspberry Pi


Подключитесь к системе по ssh и выполните команды:

sudo apt-get update
sudo apt-get install 3cxsbc

После установки перегрузите все IP телефоны в сети, либо подождите примерно 10 минут, чтобы телефоны обновили регистрацию на сервере.

Мы приносим свои извинения, если данная проблема доставила вам неудобства!

Выпущен 3CX Call Flow Designer Release Candidate


Как вы знаете, вместе с 3CX v15.5 мы представили и новую среду разработки голосовых приложений 3CX Call Flow Designer. Некоторое время назад была выпущена beta-версия продукта, а сейчас мы представляем RC-версию.

Основные улучшения в 3CX CFD RC


  • Компонент Email Sender корректно проверяет необходимые поля и корректно устанавливает адрес отправителя в поле “From”
  • Также компонент Email Sender теперь автоматически получает конфигурацию почтового сервера из настроек 3CX
  • Переменные приложения (Call flow) и отдельных компонентов могут инициализироваться с помощью переменных сессии (session variables)
  • Редактор выражений определял неверное количество параметров, если в текстовой константе встречалась запятая
  • После завершения компиляции приложения в Предупреждениями, окно Ошибок оставалось открытым
  • Добавлен новый компонент Logger, который позволяет сохранять текст в лог-файлах. Это весьма удобно для отладки голосовых приложений.

Загрузки и документация


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332116/


[Из песочницы] Доступ к ClickHouse с помоьщью JDBC

Воскресенье, 02 Июля 2017 г. 15:17 + в цитатник
Привет Хабр! Не так давно я имел удовольствие посетить встречу PyData Moscow на площадке Яндекса. Я не могу назвать себя python разрабочиком, но имею интересы в области аналитики и анализа данных. Посетив данное мероприятие, я узнал о существовании СУБД ClickHouse, разработанной в Яндексе и выложенной на GitHub под открытой лицензией. Колоночная SQL СУБД с отечественными корнями пробудила во мне интерес. В этой статье я поделюсь опытом установки и настройки ClickHouse, а также попыткой доступа к ней из Spring приложения с помощью Hibernate.

Сборка и установка


Знакомство с СУБД я начал со страницы документации.

Весьма удивили рекомендации по использованию оперативной памяти. Разработки советуют использовать столько же памяти, сколько у вас данных (во всяком случае пока объем данных меньше 200 Гб). Не совсем ясно, идет ли речь о суммарной памяти всех машин в кластере или о памяти каждой машины. В любом случае, я смог довольствоваться лишь 8 гб памяти своей виртуальной машины. При этом размер тестового набора данных составлял больше 60 ГБ в несжатом виде.

Первая сложность, с которой мне пришлось стокнуться, – это рекомнедация использовать Ubuntu и остуствие rpm пакетов (UPD: позже я обнаружил вот этот репозиторий). Я предпочитаю rpm системы и принял решения собрать СУБД из исходных кодов под Centos 7.

Ребята из Яндекса приготовили нам инструкции для сборки. Здесь есть несколько любопытных моментов:

  • Нам понадобиться gcc аж 6 версии, когда в centos из коробки идет только 4.8. Любопытно, с чем связано это ограничение? Я не тратил время на изучение кода clickHouse, возможно используется новый стандарт C++, который не поддерживается в 4ой версии. Так или иначе, GCC 6 пришлось тоже собирать из исходных кодов. В инструкции по сборке clickhouse описан процесс сборки gcc, однако он опять-таки применим прежде всего к Ubuntu. Помогла следующая статья. Сборка компилятора заняла у меня 1.5 часа.

  • Второй интересной особенностью сборки оказалось зависимость clickhouse от libmysqlclient. Интересно, как и зачем в clikchouse используется функции клиента MySQL?

Сборка самой СУБД заняла порядка 40 минут. Настройка clickhouse в моем случае заключалась в правке конфигурационного /usr/local/etc/clickhouse-server/config.xml, так как я решил хранить базу данных на отдельном разделе. Запусить сервер я смог с помощью sudo /usr/local/bin/clickhouse-server --config /usr/local/etc/clickhouse-server/config.xml

Проверить работу СУБД я решил на пример из этого поста, загрузив данные об авиаперелётах в США с 1987 по 2015 год. Процесс загрузки и примеры запросов приведены выше. Загрузка у меня заняла 24 минуты, размер базы данных на диске составил 14 Гб при объеме загруженных данных в 61.6 Гб.
Время выполнения тестовых запросов из статьи выше на моей машине составило:
Запрос Время, с
какие направления были самыми популярными в 2015 году 2.067
из каких городов отправляется больше рейсов 3.744
из каких городов можно улететь по максимальному количеству направлений 7.012
как зависит задержка вылета рейсов от дня недели 3.195
из каких городов, самолёты чаще задерживаются с вылетом более чем на час 3.392
какие наиболее длинные рейсы 12.466
распределение времени задержки прилёта, по авиакомпаниям 4.596
какие авиакомпании прекратили перелёты 1.653
в какие города стали больше летать в 2015 году 0.380
перелёты в какие города больше зависят от сезонности 8.806

Еще раз отмечу, что объем памяти моей машины составлял всего 8 ГБ, что почти в 8 раз меньше, чем рекомендуется разработчиками. В связи с этим цифры выше – не более чем очень грубая прикидка того, как clickhouse будет себя вести на ноутбуке разработчика, нежели в production. В целом СУБД работала стабильно и, на мой взгляд, достаточно быстро. В анализируемой таблице было 166 миллионов записей.

Дружим ClickHouse и Java


Следующий шаг в моем изучении clickhouse – попытка доступа к нему из Java. Ребята из Яндекса выложили jdbc драйвер.

Подключить его к maven проекту очень просто:

        
            ru.yandex.clickhouse
            clickhouse-jdbc
            ${clickhouse-jdbc-version}
        

При использовании есть всего несколько моментов, которые по незнанию могут вызвать сложности. Прежде всего, clikchouse поддерживает несколько протоколов. По-умолчанию родной бинарный протокол использует порт 9000, а http протокол – 8123. JDBC драйвер умеет работать только с http, поэтому указываем порт 8123. Название БД по-умолчиню – default. Имя пользователя и пароль по-умлочанию задавать не нужно. Чтобы clickhouse разрешал подключения с удаленных машин, в конфигурационном файле следует добавить строку ::.

Далее все как в обычном JDBC:

   private static final String DB_URL = "jdbc:clickhouse://localhost:8123/default";

    private final Connection conn;

    /**
     * Creates new instance
     * @throws SQLException in case of connection issue
     */
    public ClickHouseJDBCDemo() throws SQLException {
        conn = DriverManager.getConnection(DB_URL);
    }

    /**
     * Queries db to get most popular flight route for ths given year
     * @param year year to query
     * @throws SQLException in case of query issue
     */
    public void popularYearRoutes(int year) throws SQLException {
        String query = "SELECT " +
                "    OriginCityName, " +
                "    DestCityName, " +
                "    count(*) AS flights, " +
                "    bar(flights, 0, 20000, 40) AS bar " +
                "FROM ontime WHERE Year = ? GROUP BY OriginCityName, DestCityName ORDER BY flights DESC LIMIT 20";
        long time = System.currentTimeMillis();
        try (PreparedStatement statement = conn.prepareStatement(query)) {
            statement.setInt(1, year);
            try (ResultSet rs = statement.executeQuery()) {
                Util.printRs(rs);
            }
        }
        System.out.println("Time: " + (System.currentTimeMillis() - time) +" ms");
    }

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

А что с Hibernate?


Следующим моим шагом стала попытка подключить ORM. Наивно полагая, что раз есть JDBC, то будет и ORM, я потянулся к Spring Data JPA. Но, к сожалению, я столкнулся здесь с рядом проблем. Прежде всего, при подключении к БД необходимо указать SQL Dialect. Реализованного диалекта для ClickHouse мне найти не удалось.

В результате я просто создал потомок стандартного org.hibernate.dialect.Dialect:

public class ClickHouseDialect extends Dialect {
    
}

Теперь подключится к базе данных становится возможным. Проблема начинается при выполнении запросов. Дело в том, что clickhouse не поддерживает alias для имен таблиц. Другими словами, запрос вида:

SELECT alias.column from tablename alias

для clickHouse не является допустимым.

Проблема в том, что судя по всем hibernate не позволяет отключить генерацию alias для таблиц путем определения SQL Dialect. Во всяком случае мне не удалось найти, как этого добиться.

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

Итог


Исследовать clickhouse было увлекательным занятием. СУБД показала себя с хорошей стороны. Удалось собрать ее из исходных кодов, выполнить запросы с помощью консольного клиента и jdbc. К сожалению, неполная поддержка конструкций ANSI SQL не позволила легко подключить Hibernate. Впрочем, следует учитывать и назначение СУБД. Главная ниша clickhouse — сложные аналитические запросы, большинство из которых все равно придется полностью или частично писать руками. Хотя иметь возможность простого просмотра и выборки данных «из коробки» при помощи ORM было бы не лишним.

Полагаю, будет крайне интересно наблюдать за развитием проекта, особенно учитывая растущее внимание к clikchouse в сообществе последнее время и активную поддержку проекта со стороны Яндекса.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332112/


Метки:  

Дайджест интересных материалов для мобильного разработчика #210 (13 июня — 18 июня)

Воскресенье, 02 Июля 2017 г. 14:18 + в цитатник
В этом выпуске постмортем Ubuntu, приложение из сериала, AR и VR, пуши, тимлиды и разработчики. А еще 6 триллионов долларов для индустрии приложений к 2021 году!



Как стать тимлидом и не взорваться

Если вы стали лидом и первоначальная эйфория сменилась небольшим горением и унынием, то пара советов не будет лишней.

Как HBO делала приложение Not Hotdog для сериала «Кремниевая долина»

Чтобы добиться этого, мы разработали специальную нейронную архитектуру, которая работает непосредственно на вашем телефоне, и обучили её с помощью TensorFlow, Keras и Nvidia GPU.

Ubuntu для мобильных устройств: посмертный анализ

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

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

iOS


Android


Разработка


Аналитика, маркетинг и монетизация



Устройства, IoT, AI


< Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку — пришлите, пожалуйста, в почту.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332110/


Как защитить корпоративное хранилище от вирусов-шифровальщиков снэпшотами

Воскресенье, 02 Июля 2017 г. 12:44 + в цитатник

Путешествие в Японию самостоятельно. Стоит или нет?

Воскресенье, 02 Июля 2017 г. 12:09 + в цитатник
Путешествие в Японию самостоятельно. Стоит или нет?

Свершилось то, чего так долго ждали самостоятельные путешественники — Япония облегчила визовый режим для граждан России. Теперь не нужно приглашение, которое вынуждало туристов обращаться к услугами японской туристической компании для того, чтобы получить визу. Теперь достаточно забронировать билеты, гостиницу, заполнить анкету, предоставить справку с места работы о доходах, и Япония уже готова стать ещё одним значимым направлением на карте ваших путешествий.

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

Прожив в Японии почти 12 лет, в туризм я пришёл только через 2 года после приезда в эту страну. Первые два года после окончания университета я работал в торговой компании и издательском доме, но скоро понял, что профессия востоковеда очень быстро будет похоронена в рутине серых стен японского офиса.

Первый опыт в туризме я получил после просьбы знакомого «поводить туристов» по Токио. Подготовившись за пару недель, я видел, что клиенты были, в целом довольны, но чего-то не хватало. Понятно, что опыта было недостаточно, но меня не покидало чувство, что кроме «туристических маршрутов», приправленных «историями из реальной жизни» мне рассказать было нечего. Клиенты были молодыми, историей Японии особо не интересовались, и хотели «чего-то такого… необычного… Япония же всё-таки». И я чувствовал, что не смог дать им именно той изюминки, которая бы сделала их поездку уникальной. Я понял, что прожив в Японии более двух лет, зная японский язык и свободно ориентируясь на местности, я практически ничего не знал о самой стране. Опыт с несколькими последующими группами лишь усилил чувство пресности моей работы. Безусловно, прочные академические знания имелись, но их использование на практике с клиентами было похоже на нескладную “игру по нотам” выпускника музыкального училища. Первое впечатление о туризме было серым, отдавало рутиной с привкусом совкового краеведческого музея. Я же мечтал о шедевральной композиции, которая вдохновляет людей, собирая толпы и овации.

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

Перечислять все открытия — трудоёмкая задача, которая сделает статью бесконечно длинной, но спустя 10 лет, наблюдая за работой некоторых коллег, которые работают в туризме лишь с целью заработка, я понял, что у них лишь больше «историй из жизни» и шире карта «туристических маршрутов». Мне повезло — я попал в компанию энтузиастов, которые действительно видят Японию под другим углом, не ограниченным коммерческой оптимизацией и наличием «обязательных достопримечательностей». Они не изучали Японию как полигон для испытания туристических маршрутов. Конечно, сейчас мы не можем обойтись без технологий туризма, но первичная мотивация для нас всех — дать клиентам нечто уникальное, что поможет им полюбить Японию и даст им повод посетить её снова.

Отставив философский аспект, давайте поговорим о том, стоит ли посещать Японию самостоятельно? Выгодно ли это и чего можно ожидать от страны, которую придётся покорять самим?

Пожалуй, основной причиной, по которой люди хотят посетить Японию самостоятельно, является желание сэкономить. Если нет возможности купить готовый тур, поиск альтернатив совершенно понятен. Япония — дорогая страна. Можно сказать, что одна из самых дорогих в мире. При средней зарплате в 3000 долларов в месяц человеческий труд составляет крупную статью расходов тура. Работа профессионального гида, который может раскрыть Японию, стоит от 300 долларов в день. Можно найти и дешевле среди работающих гидами по совместительству, но разница в качестве огромна.

Следующим пунктом идёт размещение. Онлайн-сервисы бронирования открывают мир без посредников, но в этом мире есть свои подводные камни. Во-первых, при более сложном составе группы и бронировании номеров в высокие сезоны на популярных направлениях онлайн-сервисы упорно отказываются помочь, а цены начинают кусаться. Во-вторых, сама гостиница. Огромные скидки до 90% при “бронировании сейчас”, и наличие “других 35 посетителей сайта, которые одновременно с вами бронируют эту же гостиницу” давят временем, не давая возможности сделать обдуманный выбор. Часто, забронированные по скидкам гостиницы, оказываются не там, где должен проходить маршрут, но об этом турист узнает после того, как оказывается в Японии. Нельзя сказать, что онлайн-сервисы бронирования — это абсолютное зло, но это инструмент, которым нужно уметь пользоваться, и в этом нужно отдавать себе отчёт. В нашей компании мы тоже иногда пользуемся подобными сервисами, но чаще всего работаем с внутренними партнёрскими сетями, дающими нам куда больше прозрачности и гибкости в подборе гостиниц, которые не только красиво выглядят на сайте, но и подходят для маршрута тура, соответствуя требованиям самого клиента (даже если он сам изначально об этом не догадывается).

Далее — транспорт. Такси из аэропорта в Токио стоит 400 долларов, в Таиланде — 400 рублей, а в Китае или Турции предоставляется вообще бесплатно (правда, с небольшим шопинг-детуром). Тур на персональном транспорте — роскошь, и это факт. Использование общественного транспорта, а также различных проездных билетов (“JR Pass” и других) может существенно снизить расходы, но вы оказываетесь в рамках стандартных маршрутов, которые всё больше становятся избитыми даже в “уникальной традиционной Японии”. Можно попробовать сделать тур более оригинальным и составить маршрут по глубинке самостоятельно, но нужна длительная подготовка и определённый опыт в составлении индивидуальных программ. В нашей компании индивидуальный маршрут готовится, в среднем, два месяца командой из 4-5 человек и, несмотря на наш опыт, иногда случаются неожиданности, которые требуют оперативного вмешательства по ходу проведения тура. В качестве примеров можно привести длительные пробки в период местных каникул, природные катаклизмы (да, это правда, что Япония — страна стихийных бедствий, которые не случаются по предварительному согласованию с туристами), проблемы с оплатой международными кредитными картами, внезапное ухудшение самочувствия участника группы и проблемы со связью. Конечно, многих это не останавливает, потому что не хочется верить, что неприятность случится именно с ними, но по статистике это происходит намного чаще, чем кажется.

Итак, кому же подходит самостоятельное путешествие по Японии? Обобщая вышесказанное, можно выделить несколько основных категорий туристов. Самостоятельная поездка в Японию подойдёт:

1. Молодым и активным туристам, которые имеют мало средств, много времени на подготовку, а также “продвинуты” технологически (карты “Google”, приложения для перевода и другие гаджеты, которые облегчают решение проблем в другой стране).

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

3. Профессионалам (историки, искусствоведы, лингвисты, которые хорошо знают Японию, и посещают её с определённой целью: повышение квалификации, углублённое изучение темы, поиск творческих решений).

Кому стоит избегать посещения самостоятельного посещения Японии?

1. Состоятельным путешественникам с ограниченным количеством времени.

Высокая занятость в целом может привести к серьезным ошибкам при самостоятельном бронировании гостиниц и составлении маршрута тура. Выбор услуг гидов на туристических сайтах приводит к неслаженной работе (повторяющаяся часть рассказа о Японии, однообразные рестораны, а также нестыковки в маршруте). Сэкономить, возможно, получится, но путешествие может выйти скомканным, оставив негативное впечатление о стране в целом. Как правило, состоятельные туристы ждут от Японии самого лучшего за самый короткий срок. Как и в любом деле, пренебрежение услугами профессионалов, в итоге, создает больше проблем, чем решает. Если у вас есть негативный опыт пользования услугами туристических компаний, не торопитесь ставить крест на всех. Агентство с красивой вывеской, красочной брошюрой и симпатичной девушкой-менеджером — это одно. Местный туроператор, которые знает свою страну и обладает широким практическим опытом — это совсем другое.

2. Семьям с пожилыми людьми и / или детьми.

Даже 4 участника в группе — это существенная экономия на общий расходах (транспорт и гид). При заблаговременном обращении общая стоимость готового индивидуального тура может оказаться даже ниже самостоятельно составленной программы. Даже если тур выйдет несколько дороже, на выходе вы получите гарантию качественного отдыха без неприятных сюрпризов. В вашем распоряжении будут не только идеально подобранные номера и удобный персональный транспорт, но и опытный гид, который сможет найти подход к каждому участнику группы. Команда специалистов, координирующих маршрут, будет решать проблемы до того, как вы их заметите.

Хотелось бы немного рассказать о других способах экономии.

1. Групповой тур.

Присоединение к групповому туру, предлагаемому туркомпанией в вашей стране — хорошее решение, особенно если вы планируете посетить Японию в одиночку, либо если состав вашей группы относительно простой. Лучше не покупать пустой тур, так как Япония не является страной “гостиничного туризма”. Также избегайте “туров для всех”. Сделайте выбор в пользу тура с несколькими экскурсиями, который имеет концепцию и логику. Если вы видите тур в духе “Неделя в Японии для всех”, то будьте уверены, что экскурсии подобраны наобум.

2. Готовый оптимизированный тур.

Многие совершают ошибку, стараясь сделать тур слишком оригинальным. Отправиться в горы Японии, жить в монастырях и исследовать отдалённые уголки традиционных деревень — это, безусловно, романтично, но для первого посещения Японии не рекомендуется. Перед тем, как брать такую высоту, необходимо получить основное представление о стране, проследовав классическим маршрутом. Также избегайте составления тура в стиле “всё и сразу”. Хаотические передвижения между городами, слишком длинный маршрут и много городов в одном туре — это не только дорого, но и бессмысленно. Ответьте для себя на вопрос, зачем вам посещать тот или иной город в принципе. Если не знаете точно, то лучше не посещать вовсе. Для первого раза вполне достаточно недельной слаженной программы по основным городам, которые раскрывают Японию в определённой логической и тематической последовательности. Успех в мелочах — удачно подобранные гостиницы, достопримечательности и рестораны — кардинально улучшает даже короткий тур, поэтому распыляться между направлениями, стараясь охватить всё и сразу — не самое лучшее решение.

3. Низкий сезон.

Япония — страна, которая ассоциируется с двумя туристическими сезонами — цветение сакуры (начало апреля) и сезон красных клёнов (ноябрь). Страна действительно красива в этот период, но красота природы и приятная погода несут в себе значительные неприятности в виде огромных толп туристов в популярных местах, заоблачных цен даже на бюджетные гостиницы (500 долларов за ночь в гостинице класса “2 звезды” в разгар сезона цветения сакуры в центре Киото — обыденная реальность). Многие считают, что в другие сезоны Япония ничего не может предложить. Это большое заблуждение, порождённое отсутствием информации о стране и интересных предложений. Например, самые интересные фестивали проходят летом. Погода в декабре может быть намного приятнее ноября, а июнь может быть не самым дождливым месяцем года. Кроме того, сакура может цвести и в феврале, а клёны можно увидеть в конце сентября. Цены на туры в низкий сезон могут падать до 50% при более высоком качестве программы, когда страна и лучшие гиды полностью в вашем распоряжении.

4. Помощь специалистов.

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

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

Автор: Алексей Филатов, профессиональный востоковед-японист.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332104/


Метки:  

[Из песочницы] Советы по использованию FactoryGirl без ORM

Воскресенье, 02 Июля 2017 г. 11:03 + в цитатник
FactoryGirl это один из моих любимых инструментов для тестирования. Это один из первых инструментов, который я выбираю при работе вне фреймворков Ruby.

В последнее время я работаю над Rails-проектами, которые не используют базы данных
и поэтому не использует ORM такие как ActiveRecord. Вместо этого используется JSON API и преобразовывают в значения для обычных Ruby объектов.

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

Но к счастью FactoryGirl может быть использован и в обычных Ruby объектах,
со всеми полезными функциями.

Небольшое вступление:


Примечание: В примерах используются однострочные классы и Factory для краткости.

Давайте начнем с простой Factory для обычного Ruby объекта.

it "supports building a PORO object" do
  class User
    attr_accessor :name
  end

  FactoryGirl.define do
    factory :user do
      name "Amy"
    end
  end

  user = FactoryGirl.build(:user)

  expect(user.name).to eq "Amy"
end

Примечательным здесь является использование build стратегии для создания объекта. При стратегии create мы получим ошибку:

NoMethodError:
  undefined method `save!' for #

Как понятно из описания ошибки: она появляется потому — что у нас отсутствует метод #save!.. Если бы мы использовали ORM на подобие ActiveRecord то при наследовании от
ActiveRecord::Persistence он (данный метод) был бы реализован.

Неизменяемость.


Теперь давайте добавим кое что в нашу модель User, а именно:

  1. Сделаем ее не изменяемой заменив attr_accessor на attr_reader.
  2. Сделаем объект класса с конструктором — заполняемым хэшом пришедшими из JSON.

Реализовав это — мы получим что то вроде:

class User
  attr_reader :name

  def initialize(data = {})
    @name = data[:name]
  end
end

И при вызове без изменений в Factory мы получим:

NoMethodError:
  undefined method `name=' for #

Произошло это потому — что по умолчания FactoryGirl использует метод #new для инициализации обьекта, а затем присваивает значения атрибутом объекта. Это можно переопределять при помощи метода метода initialize_with, в описании Factory:

t "supports custom initialization" do
  class User
    attr_reader :name

    def initialize(data)
      @name = data[:name]
    end
  end

  FactoryGirl.define do
    factory :user do
      name "Amy"

      initialize_with { new(attributes) }
    end
  end

  user = FactoryGirl.build(:user)

  expect(user.name).to eq "Amy"
end

Распознание вложенных ресурсов


Давайте представим некий JSON объекта с вложенными ресурсами, например:

{
  "name": "Bob",
  "location": {
    "city": "New York"
  }
}

Давайте добавим описание класса для нашего вложенного объекта Location:

class Location
  attr_reader :city

  def initialize(data)
    @city = data[:city]
  end
end

class User
  attr_reader :name, :location

  def initialize(data)
    @name = data[:name]
    @location = Location.new(data[:location])
  end
end

Теперь нужно довить к нашей User Factory еще одну: Location:

it "supports constructing nested models" do
  class Location
    attr_reader :city

    def initialize(data)
      @city = data[:city]
    end
  end

  class User
    attr_reader :name, :location

    def initialize(data)
      @name = data[:name]
      @location = Location.new(data[:location])
    end
  end

  FactoryGirl.define do
    factory :location do
      city "London"

      initialize_with { new(attributes) }
    end

    factory :user do
      name "Amy"

      location { attributes_for(:location) }

      initialize_with { new(attributes) }
    end
  end

  user = FactoryGirl.build(:user)

  expect(user.name).to eq "Amy"
  expect(user.location.city).to eq "London"
end

А теперь — давайте проверим что получилось:

puts FactoryGirl.attributes_for(:user)
# => {:name=>"Amy", :location=>{:city=>"London"}}

Структура имитирующая вложенный обьект передается хешом в метод initialize класса User в нашем initialize_with блоке.

Time to lint


Последняя часть использования FactoryGirl с «чистыми» Ruby объектами. Обычно Linting вызывается до начала тестирования, для того что бы избежать кучи ошибок в не правильно описанной Factory.

После использования метода FactoryGirl.lint из Rake задачи мы получим следующее:

FactoryGirl::InvalidFactoryError: The following factories are invalid:
* user - undefined method `save!' for # (NoMethodError)

Не найден метод #save!, потому — что метод #lint из коробки использует стратегию с созданием и сохранением объекта. Для того что бы изменить это — у нас есть 2 варианта:

Вариант первый: метод #skip_create

Давайте добавим метод #skip_create в описание наших Factory:

 FactoryGirl.define do
  factory :location do
    city "London"

    skip_create
    initialize_with { new(attributes) }
  end

  factory :user do
    name "Amy"

    location { attributes_for(:location) }

    skip_create
    initialize_with { new(attributes) }
  end
end 

Теперь наши Factory заработают. Метод #skip_create также позволяет вызывать метод create в тестах:

FactoryGirl.create(:user)

Вариант второй: Lint by building


Мы можем добавить Rake задачу, для проверки наших factory:

namespace :factory_girl do
  desc "Lint factories by building"
  task lint_by_build: :environment do
    if Rails.env.production?
      abort "Can't lint factories in production"
    end

    FactoryGirl.factories.each do |factory|
      lint_factory(factory.name)
    end
  end

  private

  def lint_factory(factory_name)
    FactoryGirl.build(factory_name)
  rescue StandardError
    puts "Error building factory: #{factory_name}"
    raise
  end
end

Пример не правильной Factory:

class User
  attr_reader :name

  def initialize(data)
    @name = data.fetch(:name) # <- Имя обязательно
  end
end

FactoryGirl.define do
  factory :user do
    # name "Amy" <- Закомментируем

    initialize_with { new(attributes) }
  end
end

Теперь после вызова factory_girl:lint_by_build задачи Rake мы получим:

Error building factory: user
rake aborted!
KeyError: key not found: :name
/path/models.rb:5:in `fetch'
/path/models.rb:5:in `initialize'
/path/factories.rb:8:in `block (3 levels) in '
/path/Rakefile:15:in `lint_factory'

Итого:


Для использования FactoryGirl + «чистый» Ruby:

  • Используйте FactoryGirl.build вместо FactoryGirl.create.
  • Используйте initialize_with для изменения инициализации объекта.
  • Внешние Factory могут использовать attributes_for для построения вложенных ресурсов.
  • Для переопределения стратегии создания объекта:
    Либо добавление метода #skip_create
    Либо используйте кастомные linting для проверки Factory

P.S. Данный перевод является экспериментальным и не претендует на профессиональную полноту.
Оригинальная статья
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332100/


Метки:  

[Из песочницы] ReactJS — мое понимание тестирования

Суббота, 01 Июля 2017 г. 23:11 + в цитатник
Как мог бы сказать мой босс, всем рок. Поскольку я ничего умнее не придумал, на этом и остановимся.

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

С реактом я работаю пару месяцев как, в основном мой бекграунд это бэк, а тут вроде как ликвидация безграмотности. Redux и прочие вспомогательные концепции в уравнение пока не введены.

Возникла задача попробовать таки сделанное небольшое приложение протестировать. Ну, всякие сервисы вполне в привычном стиле можно тестировать каким-нибудь jasmine. С компонентами сложнее: по идее тестировать принято контракты, а не реализацию, то есть тесты должны иметь вид «ткнули кнопку — приложение попыталось сделать то-то».

Ну все, завязываю со вступлением.

1.

Реакция компонента на действия пользователя (или таймеры, или еще что-то) может быть двоякой: он может произвести какие-то изменения внутри себя, а может изменить что-то еще (перейти на другую страницу или другую часть SPA, выполнить скачивание файла...). В случае ReactJS «внутри себя» правильно реализуется через либо изменение состояния компонента, либо уведомление родительских компонентов о наступлении какого-то события (так, что родитель может перерендерить компонент с другими props). А изменения «вне себя» тоже, будем считать, реализуются вызовом некой функции, которую компоненту спускает родитель: это может быть в классическом понимании обработчик события, а может — «делегат» для выполнения действия (ухода на другую страницу, например). У меня создается пока впечатление, что примерно так это все под ReactJS делается обычно.

Выходит, тестирование реакций сводится к «имитировал действие пользователя, проверил, какие методы (из инжектированных в него) и с какими параметрами вызвал компонент». Тут правда есть такой момент, что setState мы в компонент не инжектируем; то есть либо нужно придумать, как перехватить setState (в общем-то мне кажется что тот же самый jasmine с этим справится), либо вместо setState дать компоненту какой-то иной способ менять свое состояние. К этому мы еще вернемся чуть ниже — там станет понятно, для чего.

2.

Еще остается вопрос, а как, собственно, имитировать действия пользователя. Я немножко почитал интернеты и нашел 1) вот — там предлагают в публичный апи компонента выводить методы вроде increment и их вызывать через component.getInstance(), 2) вот — а там ищут в построенном дереве элементы управления по каким-то критериям и их жмут. Второй способ плох тем что тест привязывается к разметке там, где это вообще-то не нужно для логики теста (и создает лишнюю зависимость от разметки таким образом, и отвлекает от сути теста), а еще тем, что это не вполне корректно (реально действия пользователя часто вызывают сразу несколько событий, и даже если компоненту из них интересно только одно, делать «неполную имитацию» как-то некрасиво). Первый же плох тем, что во-первых нет оснований выводить increment в публичное апи (компоненту вообще не обязательно иметь какое либо апи кроме того которое нужно реакту, в том числе для инжектирования пропсов), а во-вторых, если в onClick сидит что-то более сложное чем {increment} — например {() => if (this.state.count > 0) decrement();} — то вот эту дополнительную обвязку мы так не протестируем.

Пока мне кажется, что для получения разумного ответа тут нужно выбрать правильную точку зрения. От нетривиальных обработчиков внутри маркапа — следует отказаться; они заманчивы с точки зрения лаконичности, позволяя сразу на месте оттранслировать «внутреннюю» интерпретацию события (клик по кнопке +) в интерпретацию в терминах назначения компонента (вызов увеличения счетчика), не городя для этого отдельный метод, но это ударяет по тестируемости. В примере с increment неправильно имитировать действия пользователя вызовом increment, поскольку increment — это действие пользователя, уже выраженное в терминах предназначения компонента, а контракты на компоненты (которые мы и проверяем) обычно в техзаданиях имеют вид «при нажатии такой-то кнопки происходит то-то»; поэтому частью контракта является именно событие «нажатие кнопки +», а не «команда на увеличение счетчика».

А раз события мы признаем частью контракта, то внезапно у них появляется право быть публичными. То есть фактически компонент распадается на разметку и контроллер, и мы их тестируем отдельно; и потому у контроллера есть свой апи, который должен быть видим из разметки и потому публичен. И если рассматривать класс (на основе которого создается компонент) именно как задание контроллера, то именно этот класс и может это апи публиковать; то есть вполне резонно вызывать эти «управляющие сигналы» контроллера через «getInstance().onPlusButtonClick()». Правда, в общем случае тогда нужно создавать объект event (и из сображений перфекционизма — более-менее корректный), который будет подан на вход. Но во многих случаях и этого можно избежать: пусть «перевод» событий прямо в разметке писать и не следует, но такие штуки как (event) => onTextChange(event.value) выглядят, возможно, достаточно безобидно, чтобы их не тестировать, а на вход сигнала тогда можно подавать не event а прямо текст.

Но возможно это все витание в облаках, и если ваши компоненты невелики и просты, проще не париться и писать прямо в маркапе что угодно, а потом находить кнопки и в них тыкать. Вроде бы то, что я выше предложил, ощутимого дискомфорта приносить не должно, но в сущности кроме как на тестах, ни на чем принимаемое тут решение не отразится, а красивость тестов, наверное, не так уж важна — можно пойти и путем «поменьше ограничений свободы разработчиков». Посмотрим, что напишет прогрессивная общественность :)

3.

Но сгенерированный маркап ведь тоже является частью контракта компонента =) Но тут опять вопрос — в какой мере? Отчасти маркап это лишь реализация. Мне пока неясно как отделить в маркапе важное от неважного (ну помимо вынесения конкретики оформления в CSS). В принципе, если весь маркап считать контрактом, тогда jest предлагает регрессионное тестирование сравнением с эталоном; но если мы знаем, какие части маркапа для нас важны, мы можем проверить именно их анализом сгенерированной DOM. Вот только очень уж многословный анализ получится. Пока я все же склоняюсь к сравнению с эталоном, хотя это и не очень чисто.

Выработка способа анализа маркапа — это не единственная задача, которую нужно решить для тестирования маркапа. Мы ведь тестируем, как выглядит компонент в некий момент работы — после каких-то действий. А сами действия запускать в ходе этого же теста не очень правильно (как минимум — даже если понятно как это делать). Мне представляется, что поскольку маркап есть продукт вычисления функции состояния и пропсов, то следует просто подать ему на вход такое состояние и пропсы, которое имитирует выполнение этих предыдущих действий; то есть тест формулируется как «проверить, как выглядит маркап в состоянии, когда выбрана вторая закладка в приложении и на ней в таблице показана вторая страница эталонного набора данных». И вот тут возникает вопрос, а как такое состояние описать в тесте: 1) откуда тест знает структуру состояния компонента, ведь это часть реализации, а не контракта, 2) как он должен сформировать правильное состояние (должно ли это быть бесконтрольное создание объекта простым перечислением свойств и значений, или должен предоставляться — не для тестов даже, а для реальной жизни, — какой-то билдер, которым компонент гарантирует корректность формируемого состояния).

Насчет приватности опять встает вопрос точки зрения. Если состояние компонента это черный ящик, тогда действительно родитель либо вообще не занимается состоянием дочерних компонентов, либо он предоставляет дочерним доступ к какой-то функции, позволяющей читать или изменять состояние, но при этом сам опять-таки о составе состояния не знает. Но возможен и другой подход, аналогичный тому, который в .Net применяется в парадигме MVVM: состояние в этом случае представляет собой некую модель ViewModel, описывающую view, а компоненты этого view привязываются к интересующим их частям этой модели. Тогда структура ViewModel самоценна: управляя ею, мы управляем состоянием компонентов, читая ее — читаем сохраненное контролами состояние. И тогда естественно становится делать свойства ViewModel публичными — не в том смысле, что все дочерние контролы свободно к модели обращаются и откуда угодно читают и куда угодно пишут, а в том, что на каком-то верхнем уровне, где ViewModel хранится, мы знаем, как в ней выглядит (какими свойствами описывается и в каком формате) состояние каждого компонента, и можем в том числе в тесте задать такое состояние, в котором хотим проверить, как отрендерится компонент.

Выше в конце ч.1 я писал о варианте, когда вместо setState компонент применяет какой-то иной механизм, и вот описанная модель как раз неплохой пример такого подхода. Где-то хранится ViewModel, дочерним компонентам отдаются части ее в props, а чтобы наоборот компонент мог воздействовать на какое-то свойство X из ViewModel, ему можно передать в props под названием setX некую f(x), которая по существу делает viewModel.prop1 := x. Конечно, на самом деле f(x) должен быть хитрее — не просто синхронно менять состояние и все, а действовать как-то аналогично setState. Как один из вариантов, наверное, можно иметь настоящий state на верхнем уровне компонентов, а детям спускать аксессоры, которые будут через setState этого верхнего компонента реализовываться. Другой вариант это какой-то известный механизм внешнего хранения вроде Redux.

А вот как сформировать гарантированно корректное состояние — это я пока не продумывал. Если бы речь шла только о тестировании, бог бы с ним. Но раз уж ViewModel имеет публично известную структуру и допускает внесение изменений в него извне вьюшки (в нашем случае — из тестов), то с формальной точки зрения неплохо было бы предусмотреть какие-то методы манипулирования состоянием такие, чтобы они получали не больше параметров, чем нужно, и гарантированно ставили непротиворечивое состояние. Что-то вроде gotoFirstPage(), который сам понимает, что номер текущей страницы должен стать 1, а еще знает, что «номер предыдущей страницы» надо установить в этом случае в null (просто придуманный пример).
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332090/


Метки:  

Никогда не пишите длинных if-ов

Суббота, 01 Июля 2017 г. 22:38 + в цитатник
Ошибок в условиях допускается великое множество. Можно взять для примера любой пост из блога PVS-studio, в каждом есть ошибки, связанные с невнимательным обращением с условиями. И правда, нелегко разглядеть ошибку в условии, если код выглядит так (пример из этого поста):

static int ParseNumber(const char* tx)
{
  ....
  else if (strlen(tx) >= 4 && (strncmp(tx, "%eps", 4) == 0
    || strncmp(tx, "+%pi", 4) == 0 || strncmp(tx, "-%pi", 4) == 0
    || strncmp(tx, "+Inf", 4) == 0 || strncmp(tx, "-Inf", 4) == 0
    || strncmp(tx, "+Nan", 4) == 0 || strncmp(tx, "-Nan", 4) == 0
    || strncmp(tx, "%nan", 4) == 0 || strncmp(tx, "%inf", 4) == 0
          ))
  {
      return 4;
  }
  else if (strlen(tx) >= 3
    && (strncmp(tx, "+%e", 3) == 0
     || strncmp(tx, "-%e", 3) == 0
     || strncmp(tx, "%pi", 3) == 0   // <=
     || strncmp(tx, "Nan", 3) == 0
     || strncmp(tx, "Inf", 3) == 0
     || strncmp(tx, "%pi", 3) == 0)) // <=
  {
      return 3;
  }
  ....
}


Такие условия явление повсеместное, я сталкивался с ними абсолютно во всех проектах с которыми я имел дело. Вот этот пост на хабре отлично иллюстрирует сложившийся в программистском мире зоопарк того, как «можно» писать условия для if-else и каждый подход аргументирован и по своему крут, вообще пробелы рулят, табуляция отстой и все такое. Самое смешное, что он начинается словами «Условный оператор в обычной своей форме источником проблем является сравнительно редко», за подтверждением обратного отсылаю вас, опять же, к блогу PVS-studio и вашему собственному горькому опыту.

Я заметил, что в последнее время при написании if-else блоков начал повсеместно использовать подход, который формализовал только сейчас и захотел с вами поделиться. В общем и целом он совпадает с описанным в первом же комментарии к вышеупомянутому посту, но радикальней и с четкой идеей. Я использую такую технику вообще во всех условиях, не важно, сложные они или простые.

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

По правде, второе даже важнее. Если по поводу условия есть, что сказать, кроме того, что явно написано в нем самом, нужно подумать, можно ли это выразить названием переменной. Сравните два псевдокода:
if (model.user && model.user.id) {
    doSomethingWithUserId(model.user.id);
    ...
}

и
let userExistsAndValid = model.user && model.user.id;
if (userExistsAndValid) {
    doSomethingWithUser(model.user);
    ...
}

В первом случае у нас чисто формальная проверка значений, нам нужен user.id и мы проверяем, есть ли он, все можно оставить как есть. Во втором случае нам нужна уже валидная модель и мы объясняем это в имени переменной через термины бизнес-логики.

Такой подход имеет преимущества и при рефакторинге: если проверка поеадобится дальше в методе еще раз и/или расширятся условия валидности (нужно будет, чтобы у юзера еще и емейл был обязательно) — изменения будут минимальны.

Возьмем пример тоже про пользователя, но посложнее, с которым я столкнулся буквально несколько дней назад и отрефакторил в этом стиле. Было:
if (this.profile.firstName && this.profile.lastName &&
    (this.password ||
     !!_.find(this.serviceAccounts, a => a.provider === 'slack' && a.accountId)) {
   ....
}

Стало:
const hasSlack = !!_.find(this.serviceAccounts, a => a.provider === 'slack' && a.accountId);
const hasSomeLoginCredentials = this.password || hasSlack;
const hasPersonalData = this.profile.firstName && this.profile.lastName;
if (hasPersonalData && hasSomeLoginCredentials) {
   ....
}

Конечно есть случаи, когда какие-то элементы условия надо выполнять только, если предыдущие были успешны и другие исключения, но все же: если в условии присутствует больше одного логического оператора, нужно задуматься о его рефакторинге. Большинство таких моментов можно разрешить, кстати, тернарными выражениями или локальными функциями, но насколько эти инструменты более читабельны, чем длинные if-ы, каждый решает сам.

Для меня в списке исключений стоят еще шорткаты, очевидно, если надо просто выразить что-то вроде
if (err || !result || !result.length === 0) return callback(err);

нет смысла вводить переменные.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332060/


Метки:  

Задачи с собеседований. Три адекватные задачки на «подумать»

Суббота, 01 Июля 2017 г. 21:37 + в цитатник
И снова про собеседования. Некоторые простые задачи порой вызывают затруднение. В этом посте я хочу рассмотреть три задачки с собеседований, которые мне понравились, потому что к их решению можно прийти самим, но чуток подумать все равно придется.




Задача 1. Проверьте, насколько вы избалованный программист


Дана упорядоченная последовательность чисел от 1 до N. Из нее удалили одно число, а оставшиеся перемешали. Найти удаленное число.

С толку сбивает только одна фраза «упорядоченная последовательность», она-то и может натолкнуть на использование сортировки для решения данной задачи. Программисты довольно часто пользуются готовыми библиотеками и фреймворками, поэтому при решении задач автоматом обдумываешь, что будешь использовать из библиотеки. Для многих программистов единственным очевидным решением является сортировка полученной последовательности и далее поэлементное сравнение исходной и отсортированной последовательностей до первого несовпадения. Можно подсчитать сложность такого решения: логарифмическая сложность сортировки плюс линейная сложность поиска. Хм, может подойти к решению как-то иначе?

Есть более простое решение
Давайте забудем о том, что последовательность упорядочена. Обе последовательности различаются всего одним числом, а значит, чтобы его найти нужно из суммы элементов исходной последовательности вычесть сумму полученной. И кстати, если все элементы уникальны, то в исходном массиве у нас арифметическая прогрессия и первую сумму можно вычислить как $(a_1+a_n)n/2$.


Задача 2. Жонглирование числами


У вас есть пятилитровый и трехлитровый кувшины и неограниченное количество воды. Как отмерить ровно 4 литра воды? Кувшины имеют неправильную форму, поэтому точно отмерить половину кувшина не получится.

Это моя любимая задачка из разряда «головоломок». С одной стороны нужно немного подумать, а с другой – она действительно проста и адекватна.

Решение
Здесь придется немного пожонглировать с простыми числами 5 и 3.

1. Заполняем трехлитровый кувшин. Переливаем эти 3 литра в пятилитровый кувшин.
2. Снова заполняем трехлитровый кувшин и переливаем из него в пятилитровый. Помним, что в пятилитровом кувшине сейчас 3 литра, до полного его заполнения из другого кувшина выливается 2 литра. В трехлитровом кувшине остался один литр.
3. Опустошаем пятилитровый кувшин. Переливаем в него отмеренный один литр. Снова заполняем трехлитровый кувшин и переливаем из него в пятилитровый. Теперь в большом кувшине у нас 4 литра воды.


Задача 3. Без посредников


Имеется два числа. Можно ли поменять их местами без использования дополнительной переменной?

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

Решить задачу можно, используя арифметические или побитовые операции. Поскольку арифметические показались проще, то я решила использовать их.
Решение
Пусть у нас есть A и B.

A = A + B
B = A – B // После этого B становится A, т.к. в действительности получаем (A + B) – B = A
A = A – B

В этом решении есть большой минус: возможность переполнения. Поэтому лучше использовать поразрядную операцию XOR.

A = A ^ B
B = A ^ B
A = A ^ B

Как это работает: в первой строке мы получаем маску на различающиеся биты, в этих разрядах будут стоять единички. Далее производится сброс и выставление нужных бит для обмена значений.
На примере будет наглядней. Рассмотрим обмен чисел 5 и 9.

A = 0101 ^ 1001 = 1100
B = 1100 ^ 1001 = 0101
A = 1100 ^ 0101 = 1001


Осталось только пожелать всем успешных собеседований!
А в комментариях можете написать, какие задачи встречались вам.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332088/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1033 1032 [1031] 1030 1029 ..
.. 1 Календарь