Уязвимость графического пароля, сеанс разоблачения

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

История о том, как мой одиннадцати летний сын взломал графический ключ на планшете.

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

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

Отсюда начинается наша детективная история с погонями и перестрелками. Передаю слово моему сыну:

— После пяти неправильных попыток планшет попросил ввести адрес электронной почты. Я ввел свой адрес на gmail (адрес с которым настроена синхронизация планшета прим. папы). После этого планшет просто предложил сменить пароль.

Вот такой у меня сын.

ссылка на оригинал статьи http://habrahabr.ru/post/174797/

Программы для дубляжа или озвучки для начинающих

В этой статье ты, хабр, не найдёшь советов о том, какой микрофон лучше выбрать. С этим вопросом я рекомендую прочесть посты Микрофоны, микрофоны и еще раз микрофоны. Часть #1, Микрофоны, взгляд подкастера #2 и Микрофоны, 3й взгляд подкастера.
Ещё ты здесь не найдёшь советов о качестве роликов. Если тебе нужно качество, рекомендую почитать годную статью Попытка перевода и озвучки видео в домашних условиях.

Бывают ситуации, когда тебе очень нравится то или иное видео, но ты не можешь показать его своим друзьям, потому что они до сих пор не знают английского/испанского/немецкого/любого другого языка. Но ведь ты его знаешь, и как минимум можешь сделать субтитры для просмотра видео, а может быть даже замахнёшься на озвучку дубляжа, без надобности тратить 100 американских рублей на микрофон, и ещё больше тех-же американский рублей на софт обработки звука.
Я расскажу о том, как сделать субтитры или дубляж на том, что есть почти в каждом доме.

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

  • программа для создания субтитров: image Aegisub (бесплатная)
  • программа-диктофон image audacity (бесплатная)
  • любая программа извлечения аудиодорожки из видео (к примеру, avitomp3, бесплатная)
  • VirtualDubMod — модификация всем известного ВиртуалДаба, где, среди прочего, есть возможность простой замены звуковой дорожки
  • микрофон, который позволит достаточно внятно записать речь (я использую обычный микрофон для караоке, который сейчас идёт в комплекте практически с любым DVD-проигрывателем)

Создание субтитров.

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

Перво-наперво нужно открыть видео-файл, для которого мы будем создавать субтитры. Выбираем в меню пункт «Video», а в нём «Open Video…», и в открывшемся диалоге открываем наш файл.
После выбора он появляется в левой верхней области окна. Но это ещё не всё. Если сейчас нажать на «Play» (т.е. включить видео), то оно будет без звука. Это не удобно для нас, поэтому нужно добавить звук. Сделать это можно через пункт меню «Audio» -> «Open Audio from Video».
Таким образом, мы получим следующее окно:

  • красное — время начала текущего текста
  • зелёное — время окончания текущего текста
  • жёлтое — отображаемый текст
  • синее — список всех текстов, с указанием времени действия

Итак, начинаем создавать субтитры. Слева, над синей областью есть три клавиши. Нажимаем на первую из них, «Play». Как только мы слышим текст, нажимаем на паузу. Обычно речь начинается не с первой секунды видео — сначала идёт заставка, или просто музыка. Поэтому мы начинаем создавать субтитры не с первой секунды.
Итак, мы нажали на паузу, и видим, с какой именно секунды начинается речь. Указываем это время в красном поле. После этого можно продолжить игру видео, и записать текст, который должен отображаться в субтитрах в жёлтую область. Когда фраза будет логически завершена, указываем в красном поле время, когда фразу следует скрыть с экрана.
Теперь можно переходить ко второй фразе. В синей области экрана нажимаем правой клавишей мышки на текущей строчке субтитров, и выбираем там пункт «Insert (after)». Появится вторая строка, у которой время начала будет равно времени окончания предыдущей строки.
Повторяем ввод текста и добавление строк до тех пор, пока не введём все необходимые субтитры.
Приведу скриншот проекта, который я делал последним.
image

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

Сохраняя проект в формате этой программы, ты не сможешь подключить этот файл ко всем проигрывателям, и не сможешь загрузить его на YouToube. Для этого субтитры нужно экспортировать. Нажимаем в меню «File» -> «Export Subtitles…», откроется окно, в котором тоже нажимаем клавишу «Export», и указываем файл для субтитров (обязательно указываем в имени файла расширение, я использую субтитры в формате srt, они нормально загружаются на YouToube).
Пожалуй это всё, что я могу рассказать о создании субтитров.

Создание звука.

Свой голос я записываю с помощью программы-диктофона image audacity. Это очень простой и довольно мощный звуковой редактор. Перечислю функции, которые я использую при создании своих записей:

  • удаление шума
  • нормализация
  • изменение громкости
  • автоприглушение

Но, обо всём по порядку.
Прежде всего, нужно освободить комнату от всех посторонних друзей/родственников. Первые 5-10 дублей, вероятно, вы будете смеяться вместе с ними, но затем это очень раздражает, мешает сосредоточиться, и нормально записать свой голос.
Далее, нажимаем на красную кнопку на верхней панели программы — запись началась. После этого переключаемся на программу субтитров, и там тоже запускаем воспроизведение видео. Текущая строка субтитров подсвечивается жетловатым цветом, и я всегда знаю, где именно я нахожусь в тексте, что было перед этим и что будет дальше. Это очень важный момент, поверьте.
Конечно, ты заметил, что между включением записи и стартом видео произошла заминка на время переключения между программами. Это совершенно не важно, и в дальнейшем я покажу, как избавиться от этого промежутка пустоты.
Итак, два скриншота. Первый показывает положение окон на рабочем столе перед стартом записи.

И второй, показывает мой экран сразу после начала записи.
image

Пока что нас не очень волнует то, что происходит в диктафоне. Пускай потихоньку пишет. Если твой голос будет звучать слишком тихо — не вздумай ничего сейчас менять. Обрабатывать звук можно будет только после того, как вся звуковая дорожка будет подготовлена, иначе разрывы между дублями будут слишком заметны на слух.
Итак, мы записали около 30 секунд текста, и голос начал «проседать» — заикаться, путать буквы и тому подобное. Совсем не обязательно начинать всё с начала, можно просто продолжить запись.
Для этого:

  1. определим, на каком моменте у нас есть завершённая фраза. К примеру, это будет 27-я секунда нашего видео.
  2. Находим в диктофоне момент перед началом фразы «Пожалуй, я начну с вопроса…», которая звучит с 27-й секунды. На моём скриншоте это отметка времени 31,836 секунды
  3. Указываем внизу в длительности значение побольше, чтоб выделение захватило весь оставшийся звук
    image
  4. Выбираем в меню «Правка» -> «Remove Audio or Labels» -> «Удалить»

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

Однако, нам нужно работать с ними как с одной, поэтому выбираем в меню «Дорожки» -> «Свести в последнюю дорожку».

Обработка звука

Итак, у нас есть единая звуковая дорожка. Начинаем её причёсывать.
Прежде всего нужно нормализовать звук.
«Эффекты» -> «Нормировка сигнала». Все параметры оставляем как есть, просто нажимаем «Ок».
Бывает, после нормализации остаются «петухи», из за которых весь звук слышится достаточно тихо. На скриншоте пример такого «петуха»:
image

Для борьбы с такими моментами можно использовать инструмент из меню «Эффекты» -> «Усиление сигнала» (обязательно нужно выделить этот фрагмент аудио, чтобы эффект применялся только к нему).
Я указал значение "-3,8", после чего убрал выделение и опять применил нормализацию.
Получил такую картинку:
image

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

  • выделяем фрагмент дорожки, где нет нашего голоса (только посторонние шумы)
  • выбираем в меню «Эффекты» -> «Удаление шума»
  • в открывшимся окне нажимаем кнопку «Создать модель шума»
  • снимаем выделения
  • опять выбираем в меню «Эффекты» -> «Удаление шума»
  • нажимаем «ОК»

В результате получим такую чистую дорожку:
image

Согласись, теперь звук намного приятнее, да и выглядит получше =)
Однако, ещё остались случайные щелчки, шумы (а у меня перед каждой фразой слышится ещё и придыхание, как я набираю воздух в лёгкие).
Избавиться от этого проще простого. Выделяем фрагмент, который нужно убрать, и выбираем в меню «Создание» -> «Тишина…» -> «ОК».
К примеру, я убрал в начале звук клавиатуры от переключения между окнами и нажатия кнопки «Play» на видео.
image

Сохраняем проект в диктофоне, пока что мы с ним закончили.

Получение аудио дорожки из видео

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

Я использую программу avitomp3 для получения файла mp3 из видео-файла. Уверен, в этой части многие могут пойти своим путём, главное чтобы в результате мы получили аудио-дорожку в mp3 файле.
Пользоваться программой просто — нажимаем кнопку «Add», выбираем наш файл, указываем параметры аудио-файла (рекомендую сразу сохранять в моно-канале, для домашнего видео будет вполне нормально), папку сохранения и нажимаем на большую круглую кнопку со стрелками справа внизу.
image

Создаём микс из двух дорожек

Итак, возвращаемся к нашей записи. На скриншоте я вместо примера взял свою реальную работу по озвучиванию. Необходимо добавить к уже существующей дорожке ту запись, которую мы только что извлекли из видео-файла.
Сделать это можно пунктом меню «Файл» -> «Импортировать» -> «Звуковой файл…»
Если помнишь, я просил не заострять внимание на том моменте, что запись твоего голоса начинается раньше начала воспроизведения видео?
Пришла пора с этим разобраться.
Обычно, видя две дорожки сразу, удаётся прямо на глаз определить, где идёт отставание нашей дорожки от видео.
Верхняя дорожка из видео файла, нижняя — та, что я записал на микрофон.
image

Я выделил на нижней дорожке время отставания моего голоса от того момента, когда начинается речь на оригинальном видео. Внизу можно увидеть, что выделение начинается с позиции 6 секунд и 321 миллисекунд, и этот кусок составляет 950 миллисекунд.
Теперь нам нужно эти 950 миллисекунд вырезать из начала файла. Для этого просто записываем вместо цифры 6,321 нули, и наше выделение перемещается в начало файла.
Теперь, для удаления этого отрезка, достаточно применить уже знакомую нам операцию «Правка» -> «Remove Audio or Labels» -> «Удалить».

Дорожку из видео файла нужно переместить выше нашей звуковой дорожки, и выделить её целиком. После этого применяем «Эффекты» -> «Автоприглушение…»
Настройки указываем такие, как у меня на скрине:
image

И получаем вот такие изменения в дорожке из видео:
image

Это последний этап работы с нашей звуковой дорожкой. Теперь можно сохранить сам файл проекта (если в будущем ты планируешь что-то в нём изменять), и экспортировать эти две дорожки в mp3-файл.
Экспортировать можно соответственно через меню «Файл» -> «Экспортировать».

Замена дорожки в видео-файле

Наконец пришла пора последнего приложения в моём списке. Открываем VirtualDubMod.
Открываем наш видео-файл. «File» -> «Open video file…».
Указываем, что с видео нам ничего делать не нужно. Делаем это, выбирая в меню «Video» -> «Direct stream copy».
И теперь начинаем работу с аудио-дорожками. Заходим в меню «Streams» -> «Stream list».
Сейчас у нас там одна дорожка, из видео-файла. Нажимаем справа внизу кнопку «Disable» для того, чтобы отключить её.
И добавляем наш файл mp3, используя кнопку «Add».
image

Осталось только сохранить новое видео «File» -> «Save as…».
Всё, наш файл готов.

При желании можно его загружать на YouToube или любой другой хостинг. Для субтитров указываем файл с расширением *.srt, который мы создавали в самом начале.
Я могу дать ссылку на свой файл, который я делал таким образом. Если не обращать внимание на акцент и на мой нудный голос, то, мне кажется, получилось весьма неплохо.

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

ссылка на оригинал статьи http://habrahabr.ru/post/174759/

Что делать с плохим кодом

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

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

«Как можно быть таким идиотом?»
«Кто виноват в том, что мой мозг взорвался от всей этой бессвязности и богохульства?»
«Кто оскорбляет <Название Компании>!?»

Это неправильно. Не надо начинать с этого. Прежде, чем найти беднягу-автора кода и обрушить на него свой гнев, лучше поймите сам код.

Понимайте код

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

Действительно понимайте код

Хотите понять, откуда эти необузданные GOTO, такие ветвистые switch, тупые названия, уродливое форматирование?
Понять очень просто. Представьте, что код — ваш, и вы изобретете оправдания собственной глупости. В голову немедленно придут железные аргументы: «меня торопили», «я не понимал API», «у меня не было возможности полностью разобраться в системе»… Глазом моргнуть не успеете, как у вас будет целая батарея отмазок, и лишь немногие из них будут иметь отношение к делу. Вы написали код плохо. Вы не выше этого.

Изучите исходную проблему, проведите расследование

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

Учитесь

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

Не дайте своему эго водить вас на поводу. Не отправляйте письмо всей команде. Не кричите с крыши. Не пишите очередной пост в блоге. Главная ошибка — вера в то, что:

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

И если все-таки приходится писать всем. Если приходится кричать с крыши. Если вы должны написать пост о том, что вас так раздражает, не забудьте использовать басни и намеки, маскируя намерения вашего эго. Название все-таки придется дать серьезное, чтобы соблазнить упрямого программиста прочитать его. Что-нибудь типа «Что делать с плохим кодом» будет в самый раз.

Серьезно, вы тоже отстой

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

Итак, к чаше унижения…

ссылка на оригинал статьи http://habrahabr.ru/post/174795/

Языки 5го поколения. Разве они существуют?

Java, как и C#, по-прежнему занимают львиную долю рынка корпоративных приложений, сделанных на заказ. Java на этом рынке уже около 20 лет, C# около 10 лет, при этом они относится к языкам программирования 3го поколения. В то время, как языки 4го поколения предлагают более эффективный способ создания бизнес приложений, но всё равно Java и C# вне конкуренции. Что же говорить про языки программирования 5го поколения, если языки программирования 4го поколения, предлагая увеличение производительности программиста на порядок, не смогли потеснить лидеров из 3го поколения. Разве языки 5го поколения вообще существуют?

Поколения языков программирования

  • 1е поколение

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

  • 2е поколение

    В отличие от языков программирования первого поколения, программы можно было задавать с помощью символов, используя мнемонические сокращения английских слов. Например, MOV — означал «move» — передвинуть. JMP — означал «jump» — перепрыгнуть. Такие программы впоследствии конвертировались в машинный код программой ассемблером. Ассемблерные языки были также специфическими для компьютера и процессора, но при этом были намного более понятны человеку, чем языки 1го поколения. Их также можно классифицировать, как языки уровня процессора, но с более дружелюбным синтаксисом.

  • 3е поколение

    Эта группа языков представляет собой значительные улучшения по сравнению со вторым поколением языков. Эти языки отличаются наличием составных типов данных, именованными переменными и возможностью определять участки кода, как подпрограммы. Первыми представителями этих языков были Fortran, ALGOL и COBOL. Языки программирования общего назначения такие, как C, С++, Pascal, Java, C# тоже являются языками 3го поколения.

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

  • 4е поколение

    Языки этого поколения создавались для решения задач в строго определённой предметной области, и их синтаксис наиболее приближен к языку понятному человеку. Примером этих языком могут послужить SQL, XUL, FoxPro, PowerBuilder и др. Языки четвёртого поколения часто сравнивают с предметно-ориентированными языками программирования (domain-specific language, DSL). Эти языки значительно упрощают разработку приложений в определённой предметной области, но по-прежнему программы выполняются в рамках определённой компьютерной системы. Поэтому их можно классифицировать, как языки уровня компьютерной системы, но с более дружелюбным синтаксисом.

  • 5е поколение

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

Если проанализировать классификации поколений языков, приведённых выше, то можно заметить, что 1е и 2е поколения работали на уровне процессоров. 3е и 4е поколения — на уровне компьютерных систем, будь то просто компьютер или кластер компьютеров. А что же с 5м поколением?

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

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

Предвестниками языков 5го поколения можно считать веб фреймворки. Вернее, ту часть веб фреймворка, которая отвечает за соответствие запроса пользователя и выполняемой команды со стороны сервера. В одних фреймворках — это называется маппинг запросов на действия (actions), в других роутинг (routing) запросов.

Пример маппинга в Spring MVC:

@Controller public class HelloWorldController {       @RequestMapping("/helloWorld")     public String helloWorld(Model model) {         model.addAttribute("message", "Hello World!");         return "helloWorld";     } } 

Фреймворк, обрабатывая URL запрос "/helloWorld", вызывает соответствующий метод helloWorld(…). Метод возвращает строчку «helloWorld», что указывает фреймворку передать управление соответствующему представлению, например «hellowWorld.jsp».

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

Представителями языков 5го поколения можно считать разнообразные workflows, так как workflow задаёт, какие действия, в какой последовательности и при каких условиях должны выполнить программа и человек, участвующий в этом процессе. Существуют некоторые веб фреймворки, у которых маппинг задаётся посредством определённого Workflow.

В Apache Struts 1, маппинг выглядит примерно так:

<struts-config>       <action-mappings>            <action path="/submitDetailForm"                  type="mybank.example.CustomerAction"                  name="CustomerForm"                  scope="request"                  validate="true"                  input="/CustomerDetailForm.jsp">                    <forward name="success"                          path="/ThankYou.jsp"                          redirect=”true” />                   <forward name="failure"                          path="/Failure.jsp" />          </action>            <action path=”/logoff” parameter=”/logoff.jsp”                  type=”org.apache.struts.action.ForwardAction” />        </action-mappings>   </struts-config> 

По событию со страницы CustomerDetailForm.jsp форма отправляется на action — "/submitDetailForm". Фреймворк вызывает соответствующий обработчик в классе «mybank.example.CustomerAction», который возвращает результат в виде строки «success» или «failure». По результату возвращает пользователю либо «ThankYou.jsp», либо «Failure.jsp».

В Spirng Web Flow маппинг будет выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?> <flow ...>       <input name="id" />       <action-state id="edit">         <evaluate expression="personService.findById(id)"             result="flowScope.person" />         <transition to="personForm" />     </action-state>       <view-state id="personForm" model="person" view="/person/form">         <transition on="save" to="save">             <evaluate expression="personService.save(person)" />               <evaluate expression="personService.find()"                       result="flowScope.persons" />         </transition>         <transition on="cancel" to="cancel" bind="false">             <evaluate expression="personService.find()"                       result="flowScope.persons" />         </transition>     </view-state>       <end-state id="save"/>       <end-state id="cancel"/>   </flow> 

Фреймоворк, выполняя запрос пользователя переходит в «action-state» — edit. Где находит Person объект по Id, пришедшему из URL. Кладёт его во flowScope. Дальше переходит на «view-state» — personForm, где передаёт управление "/person/form.jsp" представлению. После того, как пользователь обновит форму, он может сохранить изменения или же отменить их. В этих случаях происходят соответствующие события «save» или «cancel», которые ведут на соответствующие end-states. При этом флоу заканчиват свою работу.

Также workflows могут задаваться не только на уровне приложения, но и на уровне интеграций приложений (business process management — BPM), обеспечивая взаимодействие между собой относительно независимых приложений. Достаточно известным примером из BPM может послужить Activiti. Пример его XML представления бизнес-процесса:

<definitions id="definitions"   targetNamespace="http://activiti.org/bpmn20"    xmlns:activiti="http://activiti.org/bpmn"   xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">                  <process id="financialReport" name="Monthly financial report reminder process">                      <startEvent id="theStart" />                        <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' />                        <userTask id="writeReportTask" name="Write monthly financial report" >             <documentation>               Write monthly financial report for publication to shareholders.             </documentation>             <potentialOwner>               <resourceAssignmentExpression>                 <formalExpression>accountancy</formalExpression>               </resourceAssignmentExpression>             </potentialOwner>           </userTask>                        <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' />                          <userTask id="verifyReportTask" name="Verify monthly financial report" >             <documentation>               Verify monthly financial report composed by the accountancy department.               This financial report is going to be sent to all the company shareholders.               </documentation>             <potentialOwner>               <resourceAssignmentExpression>                 <formalExpression>management</formalExpression>               </resourceAssignmentExpression>             </potentialOwner>           </userTask>                        <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' />                          <endEvent id="theEnd" />                        </process>          </definitions> 

Процесс описывает создание некой финансовой отчётности. По «theStart» событию процесс переходит в состояние пользовательского задания — writeReportTask. На этом этапе некий бухгалтер готовит отчёт. После того, как он его закончил, он его сохраняет в систему и процесс переходит в состояние «verifyReportTask», где менеджер проверяет отчёт. После проверки, процесс заканчивается и переходит в «theEnd» состояние.

Workflows можно сравнить с чисто процедурными языками 3го поколения (Fortran, ALGOL, COBOL, С, Pascal), где workflow процессы соответствуют процедурам. В то время, как процедуры работают с определёнными структурами в памяти, то workflow процессы работают на уровне всего приложения или нескольких приложений. Главное отличие workflows от языков 3го поколения, что они являются асинхронными, их процессы выполнения растянуты во времени, и они описывают взаимодействие человека и компьютера на уровне процесса.

Из-за того, что workflows используют процедурный стиль программирования, их использование несколько ограничено, что позволяет создавать приложения только до определённого уровня сложности. Это одна из причин, почему Apache Struts 1 потерял свою привлекательность. Так как системы, написанные на нём, упирались в возможности, заложенные на уровне конфигурационных файлов (workflows).

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

Lexaden Web Flow представляет эту возможность, и даже более того, он позволяет группировать разные workflows в модули, модули в пакеты, пакеты в профайлы, профайлы в приложение. Весь этот функционал открыл возможность создавать динамические модели приложения под определённые роли в системе. Каждая роль получает своё уникальное приложение, строящееся из общих для всех компонентов. Структуру приложения на Lexaden Web Flow можно сравнить с HTML, который интерпретируется на сервере, а не в браузере, где в роли CSS выступают контроллеры, написанные на Java.

Как это всё работает… Всё начинается с определения состояния «application» и входящих в него «profiles».

  <application id="application" ...>             …              <profile id="admin" ... />             <profile id="manager" ... />             <profile id="employee" ... />             <profile id="customer" ... />             …   </application> 

Профайл — это часть приложения, привязанная к определённой роли в системе. Профайл администратора имеет доступ ко всему функционалу в системе, когда customer имеет доступ только к ограниченному функционалу, например, сделать заказ, посмотреть статус заказа.

Профайлы описывают набор модулей доступных определённой роли.

   <profile id="customer" ...>           …         <module id="orders"  … >               <on event="go_account" to="account"/>               <on event="go_addresses" to="addresses"/>         </module>         <module id="account" … >         </module>         <module id="addresses" … >         </module>         …    </profile>  

Модули независимы между собой и обмениваются информацией только посредством событий. Например, из модуля «orders», можно перейти в модуль «addresses» по событию «go_addresses».

Модули в бизнес-приложениях можно представлять, как CRUD’ы, состоящие из контроллеров, например:

<module id="addresses" initial="list">    ...     <controller id="list" … >          <on event="go_create" to="create"/>          <on event="go_read" to="read"/>          <on event="go_update" to="update"/>          <on event="go_delete" to="delete"/>    </controller>    <controller id="create" … >         …    </controller>    <controller id="read" … >          …    </controller>    <controller id="update" … >          …    </controller>    <controller id="delete" … >          …    </controller>    ... </module> 

Заходя в модуль «addresses», приложение переходит на «list» контроллер. К «list» контроллеру в Java коде привязывается, AddressesListController примерно таким кодом.

    flowControllerContext.bindController("addresses/list", new AddressesListController ()); 

С контроллером ассоциировано определённое представление (AddressesListView), которое становится активным, когда приложение переходит в состояние «addresses/list». Таким же образом привязываются другие контроллеры со своими представлениями к «create», «read», «update», «delete» состояниям. По событиям из контроллера, привязанного к состоянию «list», приложение переходит в соответствующие состояния «create», «read», «update» или «delete» и отображает соответствующие представления пользователю.

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

<flow>     ...     <profile id="t_admin" ...>         …          <module id="addresses" extends="t_addresses">                <controller id="list" extends="addresses/list">                       <on event="go_export" to="export"/>                </controller>                <controller id="export" >                       ...                </controller>          </module>         …     </profile>       <profile id="t_customer" ...>         …          <module id="addresses" extends="t_addresses"/>         …      </profile>       <module id="t_addresses" initial="list">        ...     </module>     ... </flow> 

Профайл «t_admin» и профайл «t_customer» получают копии модуля «t_addresses», определённого на верхнем уровне.
Также, при помощи наследования в профайле «t_admin», модуль «address» расширяет возможности «list» контроллера, добавляя событие «go_export», по которому приложение перейдет в состояние «admin/addresses/export». Получается наследование состояний с полиморфизмом, что предоставляет широкие возможности по настройке динамической модели приложения.

Дальше профайлы при помощи того же наследования включаются в «application».

     <application id="application" ...>             …              <profile id="admin"  extends="t_admin"/>             <profile id="manager"   extends="t_manager" />             <profile id="team_leader"   extends="t_manager" >                   <on event="go_team" to="team"/>                   <module id="team"…> … </module>              </profile>             <profile id="employee" extends="t_employee"/>             <profile id="customer"  extends="t_customer"/>             …     </application>       <profile id="t_admin" ...> ... </profile>     <profile id="t_manager" ...>... </profile>     <profile id="t_employee" ...>... </profile>     <profile id="t_customer" ...>... </profile> 

Профайлы, как и другие состояния application, profile, controller…, также поддерживают наследование и полиморфизм, позволяя повторно использовать конфигурации, внося в них лишь незначительные коррективы.

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

В будущем, благодаря такой гибкой архитектуре, можно будет связывать несколько распределённых приложений в одно целостное, предоставляя возможность задавать бизнес-процессы (workflows), используя те же технологии, но уже на уровне интегрированных приложений. Это могло бы выглядеть примерно так:

<profile id="customer" ...>           …         <module id="orders" refers="lwf://server1/profile/customer/orders"  … >               <on event="go_account" to="account"/>               <on event="go_addresses" to="addresses"/>         </module>         <module id="account" refers="lwf://server1/profile/customer/account" … >         </module>         <module id="addresses" refers="lwf://server2/profile/common/addresses" … >         </module>         …  </profile> 

Модули «orders» и «account» ссылаются на модули в приложении на сервере «server1», когда как модуль адрес ссылается на модуль в приложении на сервере «server2». При этом модули будут оставаться независимыми друг от друга и общаться только посредством событий. К контроллерам в модулях будут привязываться свои Java контроллеры, которые будут обеспечивать работу интеграционного решения. Но это всё только в будущем, надеюсь, не в таком далёком. 😉

Подводя итог, можно сказать, что языки программирования 5го поколения уже используются программистами. Только их эволюция ещё не закончена. Они находятся на стадии развития языков 3го поколения конца 50х. Их не так легко обнаружить и классифицировать. Думаю, что пройдут годы, прежде чем они наберут силу и займут своё почётное место наряду с популярными ныне языками 3го поколения — C++, Ruby, Python, Java, C#, и т.д.

P.S. Lexaden Web Flow – это язык программирования нового поколения с открытым исходным кодом (распространяется под Apache 2.0 лицензией).

ссылка на оригинал статьи http://habrahabr.ru/post/174791/

Тестирование тривиального кода

Даже если код тривиален, вы всё равно должны его тестировать.
Пару дней назад, Роберт Мартин опубликовал пост «Прагматичность TDD», (здесь лежит переводприм.переводчика) где он рассказал о том, что не тестируют абсолютно весь код. Среди исключительных ситуаций, когда не стоит применять TDD, дядя Боб упоминает написание GUI-кода, и я вижу смысл в таких утверждениях, но среди исключений есть парочка, на мой взгляд, нелогичных.
Роберт Мартин утверждает, что не разрабатывает через тестирование:

• геттеры и сеттеры;
• однострочные функции;
• абсолютно тривиальные функции.
По сути, эти утверждения сводятся к единственному правилу о том, чтобы не разрабатывать через тестирование «тривиальный» код, такой как геттеры и сеттеры (свойства для программистов .NET).
Есть несколько проблем, связанных с этим правилом применения TDD, которые я хотел бы обсудить:

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

Причинно-следственная связь

Весь смысл TDD заключается в том, что тесты направляют реализацию. Тест – это причина, а реализация – это следствие. Если вы следуете этому принципу, то каким образом на планете Земля, вы можете решить не писать тест из-за того, что ожидаемая реализация будет тривиальной сложности? Вы ещё этого даже не знаете. Это логически невозможно.
Роберт Мартин сам предложил условие очерёдности преобразований (Transformation Priority Premise — TPP), и одна из мыслей заключается в том, что когда вы начинаете направлять разработку нового кода через тесты, вы должны стараться делать это мелкими формализованными шажками. Первый шажок, скорее всего, приведёт вас к тривиальной реализации, такой как возврат константы.
С точки зрения TPP, единственная разница между тривиальным и нетривиальным кодом это то, как далеко вы зашли в процесс направления реализации через тестирование. Таким образом, если «тривиальность» это всё, что вам требуется, единственно верным путём является написание единственного теста, который будет демонстрировать, что тривиальное поведение работает так, как и ожидается. Таким образом, согласно TPP, вы разделаетесь с задачей.

Инкапсуляция.

Исключение Роберта Мартина насчёт геттеров и сеттеров ставит в тупик в особенности. Если вы считаете тот или иной геттер\сеттер (или .NET свойство) тривиальным, зачем вы вообще его имеете? Почему бы в таком случае не реализовать открытое поле в классе?
Существуют отличные причины для того, чтобы избегать реализации открытых полей класса, и все они связаны с инкапсуляцией. Данные должны быть реализованы через геттеры и сеттеры, потому что это даст возможность изменения их реализации в будущем.
Что именно мы называем процессом изменения реализации без изменения поведения?
Мы это называем рефакторингом. Откуда мы можем знать, что, изменяя код реализации, мы не изменим поведение?
Как утверждает Мартин Фаулер в книге «Рефакторинг», вы должны иметь хороший набор тестов в качестве сетки безопасности, иначе вы не будете знать, сломали ли вы что-то или нет.
Хороший код живёт и развивается долгое время. То, что в начале было тривиальным может измениться за долгий промежуток времени и вы не можете предсказать будут ли оставаться тривиальные члены тривиальными в течении нескольких лет. Важно быть уверенным в том, что тривиальное поведение остаётся корректным с добавлением к нему сложности. Регрессионный набор тестов призван решить эту проблему, но только в том случае, если вы реально пишете тесты на тривиальные фичи.
Роберт Мартин приводит в качестве аргумента то, что геттеры и сеттеры тестируются косвенно через другие тесты, но, несмотря на то, что это может быть верно на этапе объявления члена, не факт, что это будет верно сколь угодно долгое время. Спустя месяцы, эти тесты могут быть удалены, оставляя введённый тривиальный член непокрытым тестами.
Вы можете смотреть на это так: вы можете следовать или не следовать TPP с TDD, но для тривиальных членов, временной разрыв между первым и вторым преобразованиями может измеряться месяцами, а не минутами.

Изучение TDD

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

Анализ первопричины

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

«Единственный способ продвигаться быстро – продвигаться грамотно».

И всё же, давайте посмотрим, на что будет похоже применение TPP к свойствам (java-программисты могут продолжать читать. Свойства в C# — просто синтаксический сахар для геттеров и сеттеров).
Скажем, у меня есть класс DateViewModel и я хочу, чтобы у него было свойство типа int, представляющее год. Первый тест будет таким:

[Fact] public void GetYearReturnsAssignedValue() {     var sut = new DateViewModel();     sut.Year = 2013;     Assert.Equal(2013, sut.Year); } 

Не принимая ничего на веру и во всём сомневаясь, правильной реализацией будет следующая:

public int Year {     get { return 2013; }     set { } } 

Следуя TPP, этот код будет выглядеть именно так. Что ж, давайте напишем ещё один тест:

[Fact] public void GetYearReturnsAssignedValue2() {     var sut = new DateViewModel();     sut.Year = 2010;     Assert.Equal(2010, sut.Year); } 

Вместе эти два теста побуждают меня реализовать свойство корректно:

public int Year { get; set; } 

Несмотря на то, что эти два теста могут быть отрефакторены до одного параметризованного теста, проделанной работы-то всё равно очень много. Нам ведь придётся писать два теста не только для одного свойства, но и проделывать такое для каждого!
«Проделывать абсолютно то же самое?», — спросите вы. Да вы что, совсем что-ли?
Ох, ну вы же программист. Что делают программисты, когда им приходится делать одно и то же снова и снова?
Ну, если вы миритесь с противоречивыми правилами, которые позволяют вам избежать ранящей вас работы, то это неверный ответ. Если работа вас ранит – делайте её почаще.
Программисты автоматизируют повторяющиеся действия. Вы можете поступать также и в случае с тестированием свойств. Вот как я сделал тоже самое, используя AutoFixture:

[Theory, AutoWebData] public void YearIsWritable(WritablePropertyAssertion a) {     a.Verify(Reflect<DateViewModel>.GetProperty<int>(sut => sut.Year)); } 

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

От переводчика: планирую выпустить в следующий раз перевод какого-либо обзора AutoFixture, либо сделать обзор самому. ИМХО, крайне интересный инструмент.

ссылка на оригинал статьи http://habrahabr.ru/post/174781/