Обратная совместимость для неудачников

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

Реальные перспективы

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

Изъян в рассуждениях

Проблема в поддержке обратной совместимости в том, что каждый релиз добавляет большое количество хлама. Со временем это создаст остановку в развитии проекта и станет сложнее поддерживать чистоту кода при реализации новых идей. Это создает якорь, который удерживает проект в каменном веке.
Это вопрос нарастающего технического долга. Подумайте об этом. Если вы сделаете ошибку в дизайне API в первой версии проекта, то это ошибка будет преследовать вас на протяжении МНОГИХ версий. Вы не можете просто так взят и погасить этот долг. И это не сферический конь в вакууме*. Посмотрите на функции для строк и массивов в PHP. Почему параметры для них указываются именно в таком порядке? Потому что если их изменить — это обрушит обратную совместимость!

Дальновидность

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

Поступательная совместимость

Основы концепции:

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

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

В действии

Я придерживаюсь этой идеологии на протяжении года. Когда я разрабатывал API для пароля, использовался именно этот подход. Вот почему там есть параметр $options, вместо $cost и $salt. Я постарался предусмотреть будующие изменения и адаптировал под это API. Хорошо ли я это сделал? На этот вопрос мы сможем ответить в будущем. Но я думаю, что получилось намного лучше, чем если бы я следовал идеологии обратной совместимости(в соответствии с которой я мог делать все что хочу и как считаю нужным).

function password_hash($password, $algo, array $options = array()) 

TL/DR

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

*Оригинал: And this isn’t even a theoretical problem

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

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

Автоматизация загрузки только новых серий torrent-раздач (.bat + curl + aria2c) [HowTo]

За ужином интересно посмотреть что-то взрывное престольное или из позднего.
И совсем неприятно, когда стол накрыт, а ты начинаешь лихорадочно проверять по всем раздачам «а не появилось ли чего нового».
И в лучшем случае дальше будет надпись в uTorrent — «Осталось 5-10-15 минут». А стол стынет.

Так дела не делаются.

Задача:

  • автоматическая загрузка только новых серий/выпусков torrent-раздач с rutracker и rutor
  • уведомление о новых загрузках
  • обойтись только Windows и сторонними консольными программами

Решение:

Создание файла-списка раздач с номерами последних скачанных серий (get.txt).

Запуск по расписанию bat-скрипта (get.bat), который для каждой раздачи:

  1. скачивает torrent-файл,
  2. проверяет «есть ли новое?»,
  3. скачивает новое,
  4. обновляет номер последней серии в файле настроек (get.txt),
  5. создает запись в журнале загрузок (log.txt),
  6. копирует ярлык на «журнал загрузок» в «панель у пуска»

Результат:

Если загружена новая серия, то в панель «Ссылки» будет скопирован ярлык на журнал загрузок (иконка на картинке — желтая звезда).

Если после просмотра всего нового «звездочку» удалять руками, то будет работать примета:
«Есть звездочка — есть новое кино, нет звездочки — нового нет».

Установка:

  1. Создать папку программы (например, d:\AutoTorrent) #
  2. Скачать консольную утилиту для загрузки torrent-файлов curl #
  3. Скачать консольную утилиту для работы с torrent-файлами aria2c #
  4. Скачать bat-скрипт проверки новых серий (get.bat) #
  5. Скорректировать настройки bat-скрипта (логин-пароль на rutracker.org) #
  6. Создать файл-список отслеживаемых раздач (get.txt) #
  7. Создать файл-журнал (log.txt) #
  8. Создать ярлык для журнала (Новые серии.lnk) #
  9. Запланировать периодическое выполнение скрипта #

Подробнее:

1. Папка программы

Так как помимо bat-скрипта создаются-используются еще другие файлы и папки, то удобно все это локализовать в отдельном месте.

2. Утилита curl

Используется для загрузки torrent-файлов.
Чтобы скачать torrent-файл с rutracker, нужно в cookies добавить параметр bb_dl=%torrent_id%.
Aria2 тоже может загружать файлы, но подружить с cookies rutracker не получилось.

3. Утилита aria2c

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

Пример не упорядоченной раздачи

*** BitTorrent File Information ***
Comment: rutracker.org/forum/viewtopic.php?t=4400218
Files:
idx|path/length
===+===========================================================================
1|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e10.avi
|609MiB (638,871,552)
—+—————————————————————————
2|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e07.avi
|606MiB (635,699,200)
—+—————————————————————————
3|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e08.avi
|604MiB (634,238,976)
—+—————————————————————————
4|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e02.avi
|602MiB (631,814,144)
—+—————————————————————————
5|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e04.avi
|602MiB (631,265,280)
—+—————————————————————————
6|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e06.avi
|601MiB (631,037,952)
—+—————————————————————————
7|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e03.avi
|601MiB (630,517,760)
—+—————————————————————————
8|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e05.avi
|601MiB (630,310,912)
—+—————————————————————————
9|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e01.avi
|598MiB (627,965,952)
—+—————————————————————————
10|./Игра престолов Сезон 3 (LostFilm)/Game.of.Thrones.s03e09.avi
|557MiB (584,220,672)
—+—————————————————————————
>>> Printing the contents of file ‘rutracker_4400218.torrent’…
4. bat-скрипт get.bat

Написано на bat из спортивного интереса.
Были сложности в работе с кириллицей — с редактированием файла в блокноте, выводом на экран и записью в лог-файл. Поэтому используются многократные финты с chcp.
Из-за сложностей с использованием локальных переменных, появилось много подпрограмм call - exit /b.
В справке по if /? выдается оператор сравнения EQL, хотя bat на него ругается и воспринимает EQU. //Microsoft Windows [Version 6.1.7601]

5. Настройки bat-скрипта get.bat

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

  • workdir=_workdir — Рабочая папка, для сохранения временных файлов и загруженных torrent-файлов, создается автоматически при запуске bat-скрипта.
  • rutracker_login=login — Логин на трекере rutracker.org .
  • rutracker_password=password — Пароль на трекере rutracker.org .
  • video_dir=d:\video — Папка куда сохранять загружаемые файлы раздачи (при загрузке структура папок раздачи воссоздается как обычно).
  • lnk_name=«Новые серии.lnk» — Ярлык на файл журнала загрузок log.txt .
  • lnk_dest=«C:\Users\Admin\Favorites\Ссылки» — Путь куда копируется ярлык при загрузке нового файла. В данном примере, это путь до моей панели инструментов возле кнопки «Пуск».
6. Файл-список отслеживаемых раздач (get.txt)

Формат строк:

  • код трекераrutor или rutracker
  • код раздачи — число из ссылки на раздачу (напр. rutracker.org/forum/viewtopic.php?t=3192884)
  • номер последней скачанной серии — счетчик будет увеличиваться по мере загрузки новых серий
  • комментарий — любой текст для понятности списка

Пример файла get.txt:

rutor 227513 24 "Теория большого взрыва" (Сезон 6) rutracker 3799523 19 Познер rutracker 4400218 10 Игры престолов 
7. Журнал загрузок (log.txt)

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

Пример содержимого

21.05.2013 21:16:57.50: [rutracker 3799523 13 Познер] ./Pozner-2_[rutracker.org]/P2-141_Maksim_Kantor.avi
21.05.2013 20:07:44.66: [rutor 227513 24 «Теория большого взрыва» (Сезон 6)] ./The.Big.Bang.Theory.S06.1080p.WEB-DL.Rus.Eng.HDCLUB/The.Big.Bang.Theory.S06E24.1080p.WEB-DL.Rus.Eng.HDCLUB.mkv
21.05.2013 19:03:35.11: [rutracker 86939 46 ###для теста###] ./Брат-1, Брат-2/2001 — Брат 1/13 — Nautilus Pompilius — Люди на холме (demo).mp3
20.05.2013 19:03:35.08: [rutracker 86939 45 ###для теста###] ./Брат-1, Брат-2/2001 — Брат 1/12 — Nautilus Pompilius — Зверь.mp3
8. Ярлык для журнала (.lnk)

Для оповещения о загрузке нового, используется копирование ярлыка в заметное место — «Рабочий стол», панель «Ссылки». Сам ярлык нужно создать в папке bat-скрипта. Если файла журнала еще не существует, то его нужно создать.

9. Планирование задания

Для периодического исполнения bat-скрипта нужно создать задачу в планировщике задач (Панель управления\Все элементы панели управления\Администрирование\Планировщик заданий).
Из необычного — полный путь к скрипту указывается в кавычках, а рабочая папка без. Хотя в пути есть пробелы.
При каждом исполнении скрипта открывается cmd-окно.
Если кириллица не читабельна — для окна cmd нужно в свойствах выбрать шрифт Lucida.
Если нужно выполнение без cmd-окна, то можно вызывать get.vbs.

Итого

Все работает без вмешательства уже больше месяца.
Для сидирования достаточно открыть скачанный torrent-файл в uTorrent.

Ссылки:
habrahabr.ru/sandbox/51123/
ubuntu.opentomsk.net/discussion/viewtopic.php?id=3220

Файлы:

get.bat

@echo off rem  rem 13.05.2013  Tucker56  http://habrahabr.ru/post/180173/ rem  rem Загрузка новых серий/файлов из torrent-раздач rem rem Шаги: rem - создание списка отслеживаемых раздач - файл get.txt (описание ниже) rem - создание пустого файла журнала загрузок log.txt rem - создание ярлыка (Новые серии.lnk) на журнал загрузок log.txt  rem - указание внутренних параметров (ниже в этом файле) rem - создание периодической задачи запуска этого файла в планировщике задач rem rem Результат (при появлении новых файлов в отслеживаемых раздачах): rem - файлы скачиваются в заданную папку (%video_dir%) rem - в панели Ссылки появляется ярлык на журнал загрузок rem rem Особенности: rem - если в окне выполнения программы вместо кириллицы отображается ЄЁаЁ««Ёж , нужно в свойствах окна выставить шрифт Lucida   @SetLocal EnableDelayedExpansion rem Сохранение исходной кодировки сеанса CMD, для дальнейшей работы программы в cp1251, что нужно для возможности править этот файл в Блокноте for /f "tokens=2 delims=:" %%a in ('chcp') do set /a chcp_cmd=%%a  rem Для восприятия кириллицы в параметрах и отображения кириллицы текстов сообщений в консоли chcp 1251 >nul    rem Сохранение исходной кодировки, для локальных изменений (chcp) for /f "tokens=2 delims=:" %%a in ('chcp') do set /a chcp=%%a  rem -------------------------------------------------------------- rem - ВНУТРЕННИЕ ПАРАМЕТРЫ --------------------------------------- rem workdir=_workdir - рабочая папка, для сохранения временных файлов и загруженных torrent-файлов  rem rutracker_login=login - Логин на трекере http://rutracker.org rem rutracker_password=password - Пароль на трекере http://rutracker.org rem video_dir=d:\video - Папка куда сохранять загружаемые файлы раздачи (при загрузке создается путь до файла как указано в torrent-файле) rem lnk_name="Новые серии.lnk" - Ярлык на файл журнала загрузок log.txt rem lnk_dest="C:\Users\Admin\Favorites\Ссылки" - Путь куда копируется ярлык при загрузке нового файла  set workdir=_workdir set rutracker_login=login set rutracker_password=password set video_dir=d:\Video set lnk_name="Новые серии.lnk" set lnk_dest="C:\Users\Admin\Favorites\Ссылки"  rem - ВНЕШНИЕ ПАРАМЕТРЫ ------------------------------------------ rem Файл get.txt - cписок отслеживаемых torrent-ов rem ----- rem формат записи:  rem идентификатор_трекера{rutracker, rutor} номер_торрента{номер из URL} номер_последней_скаченной_серии{автоматически обновляется после загрузки новых}  rem ----- rem Пример файла get.txt:  rem rutracker 86939 46 rem rutracker 4400218 7  rem rutor 227513 22   rem -------------------------------------------------------------- rem --------------------------------------------------------------    rem Создание рабочей папки, если ее еще не существует mkdir !workdir! >nul 2>nul  rem Один вход на RuTracker, чтобы получить cookie-файл для последующих загрузок torrent-файлов curl -s -c "%workdir%\cookie_rutracker.txt" -A "Opera/10.00 (Windows NT 5.1; U; ru)" ^ 	-d "login_username=!rutracker_login!&login_password=!rutracker_password!&login=%C2%F5%EE%E4" ^ 	"http://login.rutracker.org/forum/login.php" > nul 	del /q !workdir!\~get.txt.new.tmp 2>nul   rem Обход по отслеживаемым раздачам из файла настройки get.txt for /F "tokens=1,2,3*" %%i in (get.txt) do ( 	set tracker_id=%%i 	set torrent_id=%%j 	set last_file_id=%%k 	set torrent_comment=%%l 	set torrent_file=!tracker_id!_!torrent_id!.torrent 	set err=  	rem Загрузка torrent-файла с RuTracker или RuTor 	echo !tracker_id! !torrent_id! !last_file_id!: Загрузка файла раздачи '!torrent_file!' 	if "%%i" == "rutracker" ( 		curl -s -A "Opera/10.00 (Windows NT 5.1; U; ru)" ^ 			-b "%workdir%\cookie_rutracker.txt" ^ 			-b "bb_dl=!torrent_id!" ^ 			"http://dl.rutracker.org/forum/dl.php?t=!torrent_id!" -o "!workdir!\!torrent_file!" 	) else ( 	if "%%i" == "rutor" ( 		curl -s -A "Opera/10.00 (Windows NT 5.1; U; ru)" ^ 			"http://d.rutor.org/download/!torrent_id!" -o "!workdir!\!torrent_file!" 	) else ( 		set err=X 		echo !tracker_id! !torrent_id! !last_file_id!: Неизвестный трекер: '!tracker_id!'  	)  	)   	if !err! NEQ X ( 		 		rem Обработка torrent-файла через call, чтобы можно было в for in использовать --show-files !workdir!\!torrent_file! 		call :process !torrent_file! 	)  	rem Создание обновленного списка отслеживаемых раздач (формирование нового get.txt) 	echo !tracker_id! !torrent_id! !last_file_id! !torrent_comment!>> !workdir!\~get.txt.new.tmp 	echo. )  rem Обновление файла со списком отслеживаемых раздач (get.txt) move /y !workdir!\~get.txt.new.tmp get.txt >nul 2>nul  rem Удаление технического файла работы aria2c del /Q !video_dir!\*.aria2  >nul 2>nul  rem Индикация о новых загрузках в панели "Ссылки" - в if /? указан EQL WTF?! if "!has_new!" EQU "X" copy /Y %lnk_name% %lnk_dest% >nul  rem Возврат кодировки сеанса CMD chcp !chcp_cmd! >nul  rem Для паузы в конце выполнения программы, нужно раскомментировать следующую строчку rem pause exit /b                                                                          rem Обработка torrent-файла - анализ, загрузка новых файлов rem %1 - имя torrent-файла = !torrent_file!, но %1 нужен для использования в for in :process 		echo !tracker_id! !torrent_id! !last_file_id!: Обработка раздачи '!torrent_file!'  		rem [доп инфо] в CMD /U тут без chcp 866 не работал find /c "+" 		for /f "tokens=1" %%a in ('aria2c.exe --show-files %workdir%^\%1 ^| findstr /R "^---+----*" ^| find /c "+"') do set cnt=%%a 		rem Если количество файлов в раздаче больше чем в настройке, то загружаем новые 		if !last_file_id! LSS !cnt! (  			rem В torrent-файле порядок серий может быть не последовательным (http://rutracker.org/forum/viewtopic.php?t=4400218),  			rem поэтому ориентация на сортировку по имени файлов 			del /q !workdir!\~sort.tmp 2>nul 			for /f "tokens=1,2 delims=|" %%x in ('aria2c.exe --show-files %workdir%^\%1 ^| findstr /BRC:"^[ ]*[0-9][0-9]*"') do echo %%y ^| %%x >> !workdir!\~sort.tmp  			set /a skip=last_file_id 			rem Загрузка новых файлов. Через call, чтобы работало skip=%skip% 			call :download_new_files   		) else ( 			echo !tracker_id! !torrent_id! !last_file_id!: Новых файлов нет 		) exit /b  rem Загрузка новых файлов :download_new_files 		rem 1-3) Список файлов раздачи в кодировке UTF-8, поэтому в ней и читаем 		chcp 65001 > nul  		rem Новые - это последние в отсортированном по имени списке файлов раздачи 		for /f "skip=%skip% tokens=1,2 delims=|" %%o in ('sort %workdir%^\~sort.tmp') do ( 			rem 2-3) Возврат кодировки после 65001 			chcp !chcp! >nul  			set /a down=%%p  			rem Через call 			rem 1) чтобы в echo выводить кириллицу нормально и в заголовке сообщениия, и в имени файла, и в консоль, и в журнал 			rem 2) чтобы время %time% обновлялось для каждого нового файла 			call :down_file %%o 		)  		rem 3-3) Возврат кодировки после 65001 		chcp !chcp! >nul  		rem Удаление временных файлов 		del /q !workdir!\~sort.tmp 2>nul  exit /b   rem Загрузка нового файла из раздачи rem %* = имя нового файла :down_file 		 	set /a last_file_id=last_file_id+1 			echo !tracker_id! !torrent_id! !last_file_id!: Загрузка файла '%*'  			aria2c --dir=!video_dir! ^ 				--seed-time=0 ^ 				--file-allocation=none ^ 				--summary-interval=0 ^  				--console-log-level=error ^ 				--max-overall-download-limit=1M ^ 				--allow-overwrite=true ^ 				--select-file=!down! ^ 				!workdir!\!torrent_file!  rem Еще полезно-интересные параметры: rem				--quiet=true ^ rem Параметр, чтобы от процесса загрузки никакого вывода на экран не было  rem				--allow-overwrite=true ^ rem Параметр, чтобы удалять файлы torrent-а, все кроме только скаченного rem Позволяет поддерживать отсутствие недокаченных частей ненужных соседних файлов в torrent-е rem При скачивании сразу нескольких файлов, останется только последний   			rem Ведение журнала загрузок log.txt - новые события сверху 			rem Если в %time% непосредственно в for, то для каждого файла время не обновляется, нужно через call  			copy log.txt !workdir!\~log.txt.tmp >nul 2>nul 			echo %date% %time%: [!tracker_id! !torrent_id! !last_file_id! !torrent_comment!] %* >log.txt 			type !workdir!\~log.txt.tmp >>log.txt  2>nul 			del /q !workdir!\~log.txt.tmp >nul 2>nul   			rem Флаг для оповещения о загрузке новых файлов 			set has_new=X exit /b 
get.vbs

Dim oShell Set oShell = WScript.CreateObject ("WSCript.shell") oShell.run "get.bat", 0 Set oShell = Nothing  

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

ДНК-оригами: как из ДНК делают интересные штуки нанометрового размера

Недавно я обнаружил весьма печальный факт: на Хабре совершенно не освещена такая забавная тема, как ДНК-оригами. Есть только один пост 2009 года, рассказывающий лишь самое начало занимательно истории о том, как из ДНК (да-да, той самой дезоксирибонуклеиновой кислоты, несущей нашу генетическую информацию) можно создавать всякие хитрые, плоские и трехмерные штуки нанометрового размера. Та самая нано-технология, как она есть. В этом обзоре я хочу рассказать о развитии ДНК-оригами: двухмерные смайлики из ДНК, трехмерные фигуры, кристаллы из ДНК с запрограммированной структурой, ДНК-«коробочки» с крышкой, способные нести молекулы нужных веществ и выпускать их после сигнала об открытии крышки, и, наконец, динамические структуры типа ДНК-шагохода (walker), гуляющего по подложке (создатели гордо говорят, что это уже наноробот!). Кто хочет узнать больше о том, зачем все это нужно, почитать о технологиях изготовления красивых нанометровых штук из ДНК или просто посмотреть красивые картинки, добро пожаловать под кат.


Так выглядит ДНК-наноробот

Немного теории

В конце двадцатого — начале двадцать первого века встал вопрос о конструировании объектов нанометрового размера. Для чего? Общий вектор на миниатюризацию существует достаточно давно, причем исторически это всегда было движение «сверху вниз» — например, в 70-х годах при изготовлении микросхем минимальный контролируемый размер составлял 2-8 мкм, далее это значение стремительно уменьшалось и сейчас в серийном производстве находятся чипы, выполненные по 22-нм технологическому процессу. Тут у думающих людей возник вопрос: а нельзя ли двигаться «снизу вверх»? Нельзя ли заставить атомы и молекулы собираться в нужные структуры и затем эти структуры использовать в технике? Очевидны требования к такой «самособирающейся» системе: материалы для нее должны быть достаточно дешевыми и доступными, самосборка сложной пространственной структуры системы должна легко и очевидно «программироваться», система должна быть способна нести полезный функционал. Тут же вспомнили, что в природе такие самособирающиеся системы уже существуют и прекрасно работают — это макромолекулы всех живых организмов, например, белки. Здесь приходит и первое разочаровние — белки слишком сложно устроены, их трехмерная структура задается совершенно неочевидным образом множеством нековалентных взаимодействий и получить белок с произвольной структурой — до сих пор абсолютно нетривиальная и нерешаемая задача. То есть использовать белки для конструирования нужных объектов нано-размеров технически невозможно. Что же делать? Оказывается, есть и другие макромолекулы, чья структура устроена гораздо проще структуры белков.

В 1953 году Уотсон и Крик опубликовали свою модель структуры ДНК, оказавшейся абсолютно верной. ДНК (дезоксирибонуклеиновая кислота) — это интересно устроенный линейный полимер. Одна нить ДНК состоит из монотонно повторяющегося сахаро-фосфатного остова (он асимметричен и имеет направление, различают 5′ и 3′ конец цепи), однако к каждому сахару (дезоксирибозе в случае ДНК) прикреплен один из четырех нуклеотидов (синоним слова нуклеотид — «основание») — аденин, либо тимин, либо цитозин, либо гуанин. Обычно их обозначают одной буквой — А, Т, Ц, Г. Таким образом, в ДНК есть только 4 типа мономеров, в отличие от 20 аминокислот в составе белка, что делает структуру ДНК намного проще. Дальше становится еще веселей — есть так называемое «Уотсон-Криковское спаривание оснований»: аденин может специфично связываться с тимином, а гуанин — с цитозином, образуя пары А-Т и Г-Ц (и еще Т-А и Ц-Г, разумеется), другие взаимодействий между нуклеотидами в упрощенном случае можно считать невозможными (они возможны в виде исключения при некоторых редких условиях, но для нас это не важно). Уотсон-Криковское спаривание оснований еще называется комплементарностью.

Две цепи ДНК, последовательность оснований которых комплементарна, немедленно «слипаются» в двойную спираль.Возникает вопрос: а что, если на одной цепи ДНК находятся две комплементарные области? Ответ: цепь ДНК может согнуться и комплементарные области смогут образовать двойную спираль, а вместе с местом изгиба эта структура будет называться «шпилькой» (DNA hairpin):

На чем же основано «слипание» двух комплементарных цепей ДНК (или, аналогично, двух комплементарных участков одной цепи)? Это взаимодействие держится на водородных связях. Пара А-Т соединяется двумя водородными связями, пара Г-Ц — тремя, поэтому эта пара более энергетически устойчива. Про водородные связи надо понимать следующее: энергия одной водородной связи (5 ккал/моль) не намного превосходит энергию теплового движения, а значит, одна отдельно взятая водородная связь может быть с высокой вероятностью тепловым движением разрушена. Однако, чем больше водородных связей, тем более устойчивой становится система. Это значит, что короткие участки комплементарных оснований ДНК не могут образовать устойчивую двойную спираль, она будет легко «плавиться», однако более длинные комплементарные участки уже смогут образовать стабильные структуры. Стабильность двухцепочечной структуры выражается одним параметром — температурой плавления (Тм, melting temperature). По определению, температура плавления — это температура, при которой в равновесии 50% молекул ДНК с данной длиной и последовательностью нуклеотидов находятся в двухцепочечном состоянии, а другие 50% — в расплавленном одноцепочечном состоянии. Очевидно, что температура плавления напрямую зависит от длины комплементарной области (чем длиннее — тем выше температура плавления) и от нуклеотидного состава (так как в паре Г-Ц три водородные связи, а в паре А-Т — две, то чем больше пар Г-Ц, тем выше температура плавления). Температура плавления для данной последовательности ДНК легко считается по эмпирически выведенной формуле.

От теории к практике

Итак, теорию мы изучили. Что же мы можем сделать на практике? С помощью химического синтеза мы можем напрямую синтезировать цепи ДНК длиной до 120 нуклеотидов (просто потом выход продукта резко падает). Если же нам нужна более длинная цепь, то ее без проблем можно собрать из тех самых химически синтезированных фрагментов длиной до 120 нуклеотидов (например, дядюшка Крейг Вентер отличился тем, что из кусочков собрал ДНК длиной аж 1,08 миллиона пар оснований). То есть в 21 веке мы можем легко и дешево делать ДНК любой последовательности, какой только захотим. А хотим мы, чтобы потом ДНК сворачивалась во всякие хитрые и сложные структуры, которые мы потом сможем использовать. Для этого у нас есть принцип комплементарности — как только в последовательности ДНК появляются комплементарные зоны, они слипаются и образуют двухцепочечный участок. Очевидно, мы хотим делать структуры, стабильные при комнатной температуре, значит мы хотим рассчитать температуру плавления для данных участков и сделать ее достаточно большой. При этом на одной цепи ДНК мы можем делать много разных областей с разными последовательностями и слипаться будут только комплементарные. Так как комплементарных областей может быть несколько, в результате молекула может свернуться достаточно сложным образом! Как-то так, например:

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

Двухмерные структуры из ДНК

Методологический прорыв устроил Paul Rothemund (Калифорнийский Технологический Институт) в 2006 году, именно он и придумал термин «ДНК-оригами». В своей статье в «Nature» он представил множество забавных двухмерных объектов, сделанных из ДНК. Принцип, предложенный им, достаточно прост: взять длинную (примерно 7000 нуклеотидов )«опорную» одноцепочечную молекулу ДНК и затем с помощью сотни коротких ДНК-скрепок, образующих двухцепочечные области с опорной молекулой, согнуть опорную ДНК в нужную нам двухмерную структуру. Вот рисунок из оригинальной статьи, представляющий все стадии разработки. Для начала (а) нарисуем нужную нам форму красным цветом и прикинем, как заполнить ее ДНК (представим ее на этом этапе в виде труб). Далее (b) представим, как провести одну длинную опорную молекулу по нужной нам форме (показана черной линией). На третьем этапе (с) подумаем, где мы хотим разместить «скрепки», стабилизирующие укладку длинной опорной цепи. Четвертый этап (d): больше деталей, прикидываем, как будет выглядеть вся нужная нам структура ДНК и, наконец, (e) мы имеем схему нужной нам структуры, можно заказывать ДНК нужной последовательности!

Как же из химически синтезированных ДНК собрать нужную нам структуру? Здесь на помощь приходит процесс плавления. Мы берем пробирку с водным раствором, бросаем в нее все фрагменты ДНК и нагреваем до 94-98С, температуры, которая гарантировано плавит всю ДНК (переводит ее в одноцепочечную форму). Далее мы просто очень медленно (в течении многих часов, в некоторых работах — в течении нескольких дней) охлаждаем пробирку до комнатной температуры (эта процедура называется «отжиг», annealing). При этом медленном охлаждении, когда температура оказывается достаточно низкой, постепенно образуются нужные нам двухцепочечные структуры. В оригинальной работе в каждом эксперименте примерно 70% молекул успешно собирались в нужную структуру, остальные имели дефекты.

Далее, после того, как структура рассчитана, неплохо бы доказать, что она собирается именно так, как нам надо. Для этого чаще всего используют атомно-силовую микроскопию, которая как раз прекрасно показывает общую форму молекул, но иногда используют и cryo-EM (электронную микроскопию). Автор сделал множество веселых форм из ДНК, на картинках представлены расчетные структуры и результат экспериментального определения структур с помощью атомно-силовой микроскопии. Наслаждайтесь!


Трехмерные структуры из ДНК

После того, как разобрались с конструированием сложных плоских объектов, почему бы не перейти к третьему измерению? Здесь пионерами была группа ребят из Института Скриппса в Ла-Холле, Калифорния, которые в 2004 году придумали, как из ДНК сделать нано-октаэдр. Хотя эта работа и сделана на 2 года раньше плоского ДНК-оригами, в тот раз был решен лишь частный случай (получение октаэдра из ДНК), а в работе по ДНК-оригами было предложено общее решение, поэтому именно работа 2006 года по ДНК-оригами считается основополагающей.

Октаэдр был сделан из одноцепочечной молекулы ДНК длиной примерно 1700 нуклеотидов, имеющей комплементарные области и к тому же скрепленой пятью 40-нуклеотидными ДНК-адаптерами, в результате был получен октаэдр с диаметром 22 нанометра.
На рисунке обратите внимание на цветовую кодировку на двухмерной развертке октаэдра. Видите области, отмеченные одинаковым цветом? Они содержат как комплементарные зоны (параллельные участки соединенные поперечными связями), так и некомплементарные (на схеме они изображены в виде пузырьков), при этом зоны одного цвета, расположенные в разных частях двухмерной развертки, взаимодействуют друг с другом, образуя сложную структуру, изображенную на рисунке 1с и образующую грань трехмерного тетраэдра. Наслаждайтесь красивыми картинками!


В 2009 году ученые из Бостона и Гарвардского Университета опубликовали принципы построения трехмерного ДНК-оригами, как они сами говорят, по подобию пчелиных сот. Одно из достижений этой работы — люди написали open-source программу caDNAno для конструирования трехмерных структур ДНК (она работает на Autodesk Maya). С этой программой даже неспециалист может собрать нужную структуру из готовых блоков с использованием простенького графического интерфейса, а программа рассчитает необходимую последовательность (или последовательности) ДНК, в эту структуру сворачивающуюся.



В следующей своей работе они научились делать из ДНК сложные трехмерные объекты с контролируемым искривлением и порадовали читателей журнала «Science» красивыми картинками разных искривленных объектов из ДНК (там такие классные шестеренки получились!).



Периодические структуры

До сих пор ученые игрались с непериодическими структурами из ДНК. А что если сделать такую структуру, чтобы один блок мог взаимодействовать с другим таким же блоком, и так до бесконечности? Представьте себе три отрезка, расположенных под углом 90 градусов друг к другу (походит на противотанковый еж). Очевидно, что такая структура может быть узлом бесконечной кубической решетки, если каждая сторона такого ежа будет взаимодействовать с другим таким ежом. Именно эту идею в 2010 году воплотили на практике ученые из Нью-Йорка, они сделали такого ДНК-ежа, который немедленно сформировал трехмерную решетку, то есть кристалл из ДНК, так что они использовали рентгеноструктурный анализ, чтобы показать, что ДНК образовали именно такую структуру, какую они и хотели. В свою очередь, так как кристаллы ДНК имели размер до пол-миллиметра (а это уже макро-объект), было гордо заявлено, что теперь из нано-объектов мы умеем собирать макро-объекты.

Вот стерео-картинка узла решетки, если умеете правильно скашивать глаза (это прямая стереопара), можете посмотреть в 3D (на нижней картинке с электронной плотностью ДНК четко видны два узла-«противотанковых ежа»):

Динамические структуры

Следующий шаг в конструировании трехмерных нано-объектов так же очевиден — а что если заставить это все как-то двигаться? Движущийся объект уже можно и нано-роботом назвать. Самый простенький ДНК-шагоход был сделан в 2008 году командой из Калифорнийского Технологического института. Работает он по достаточно простому принципу. Представьте себе одноцепочечную ДНК длиной, скажем, 100 нуклеотидов. Представьте другую одноцепочечную ДНК, короткую, длиной 50 нуклеотидов, комплементарную половине первой молекулы. Что будет, если их смешать? Правильно, они образуют двухцепочечную структуру в районе этих 50 нуклеотидов, вторая же половина первой молекулы останется свободной. А что если к этой структуре добавить еще одну молекулу ДНК, длиной 100 нуклеотидов и полностью комплементарной первой молекуле? Ответ вполне очевиден: она вытеснит короткую цепь длиной 50 нуклеотидов, так как обладает большим сродством к первой молекуле (у них сродство 100 из 100, а с короткой — только 50 из 100 нуклеотидов). Именно так и работал первый ДНК-шагоход. На подложке закреплены молекулы одноцепочечной ДНК, к ним из раствора приходит молекула-шагоход, имеющая комплементарные зоны к двум соседним цепям на подложке и связывается с ними. Если потом мы добавим в раствор другую ДНК, имеющую большее сродство к первой цепи на подложке, то она вытеснит одну ногу шагохода, после чего эта нога свяжется со следующей (третьей) цепью ДНК на подложке. Добавляя новые вытесняющие ДНК можно гнать шагоход все дальше и дальше по подложке. Обратный ход невозможен, так как предыдущие цепи на подложке уже инактивированы связыванием с более длинной молекулой ДНК.

Хотя первый шагоход выглядел настолько примитивно, конструкция была доработана учеными из Нью-Йорка. Они сделали более сложный шагоход с несколькими «руками» и «ногами», прицепили при помощи комплементарных ДНК на подложку «груз» (золотые частицы диаметром 5 и 10 нм) и «запрограммировали» шагоход таким образом, чтобы он прошел по подложке и собрал груз — две маленькие и одну большую золотые частицы. Последовательность шагов легко отследить по стрелочкам, а на экспериментальной картинке справа видны частицы золота и как шагоход их собирает. Нано-робот в действии! На нижней картинке показано, как именно происходит процесс «шагания» и сбора груза, принцип — тот же самый, вытеснение одной ДНК другой.


Но это еще не все. Венцом ДНК-робототехники (думаю, тут в моем голосе слышим некоторый сарказм) стал «наноробот для молекулярного транспорта», как окрестили его создатели из Бостона. Фактически ребята сделали некую «коробочку» из ДНК, закрывающуюся на «замок» из ДНК, который может быть открыт по уже известному нам принципу вытеснения одной ДНК другой, как в шагоходах. Внутри коробочки спрятан груз — частицы золота либо молекулы иммуноглобулина. ДНК можно химически модифицировать таким образом, чтобы на ней можно было закрепить этот самый груз. Итак, мы имеем закрытую коробочку, содержимое коробочки надежно спрятано. Тут мы добавляем молекулу, открывающую коробочку, она открывается и иммуноглобулин, спрятанный внутри, выходит наружу и начинает действовать! Мы же хлопаем в ладоши, умиляемся и радуемся прогрессу.

Ребята даже не поленились сделать демонстрацию proof of principle на живых раковых клетках: они прятали в коробочку антитела, блокирующие ключевые белки клеточного цикла, вводили коробочки в раковые клетки и после добавления открывающего коробочку активатора раковые клетки правда переставали делиться! Таким образом была показана принципиальная возможность использования таких конструкций для направленной доставки лекарств в организме и их выделения в нужное время по сигналу от молекулы-активатора. Одна осталась проблема, как раковую клетку от здоровой надежно отличить…

Для чего козе баян?

Все это, конечно, здорово и хорошо, картинки красивые, но внимательный читатель может спросить: «А где обещанная в самом начале польза для народного хозяйства?». Как и со всякой новой технологией, пока большой практической пользы действительно нет, помимо эстетического удовольствия от созерцания этой нано-красоты. Однако, все только начинается! Во-первых, ДНК может быть химически модифицирована, к ней могут быть добавлены химические группы, обеспечивающие связывание с другими молекулами и тогда ДНК можно использовать как подложку для построения сложных структур из других молекул. Например, сейчас все хотят что-то мастерить из нанотрубок. Если удастся сделать адаптер, связывающийся одним концом с ДНК, а другим — с нанотрубкой, тогда структуры из ДНК можно использовать для соединения нанотрубок. С другой стороны, уже есть сообщения о контролируемой металлизации ДНК, а отсюда уже рукой подать до конструирования электронных устройств на базе структур из металлизированной ДНК. Может быть, ученые изобретут более подходящий полимер, обеспечивающий более удобную самосборку сложных структур, но в любом случае ДНК-оригами займет свое место в истории науки, как один из первых примеров конструирования сложных объектов в нано-масштабе. Как бы то ни было, нас ждет большое и светлое будущее, чего и вам желаю!

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

Фильм про UNIX

В конце 60-х годов в Bell Laboratories Деннис Ритчи и Кен Томпсон, вдохновившись ОС Multics (объединенный проект MIT, GE, and Bell Labs), начали работать над новым проектом. Осознавая недостатки Multics(разработчиком которой, кстати, является ведущий этого видео — Виктор Высоцкий), Ритчи и Томпсон решили создать более полезную, гибкую и портативную систему.
В 1972 году была выпущена вторая редакция UNIX, переписанная на языке Би. В 1969—1973 годах на основе Би Деннисом Ритчи был разработан компилируемый язык, получивший название Си.
С 1974 года UNIX стал распространяться среди университетов и академических учреждений. С 1975 года началось появление новых версий, разработанных за пределами Bell Labs, и рост популярности системы.
Этот фильм «The UNIX System: Making Computers More Productive», один из двух, которые Bell Labs сняли в 1982 году, для популяризации UNIX.
Фильм содержит интервью с главными разработчиками Ритчи, Томпсоном, Керниганом, и многими другими.


Также рекомендую канал ATTTechChannel, где можно найти множество подобных фильмов: www.youtube.com/user/ATTTechChannel

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

Построение масштабируемых приложений на TypeScript. Часть 2 — События или зачем стоит изобретать собственный велосипед

В первой части статьи я рассказывал об асинхронной загрузке модулей при помощи Require.js и стандартных языковых средств TypeScript. Неосторожно я раньше времени задел тему организации работы с абстрактными событиями о чем мне очень быстро напомнили в комментариях. В частности был задан вопрос зачем придумывать собственный велосипед, если существует давно проверенный и отлично работающий Backbone.Events и/или прочие аналоги.

Если вас интересует ответ на этот вопрос, альтернативная реализация на TypeScript и не пугает чтение кода, то прошу под кат.

Все просто — все существующие Javascript фреймворки на сегодняшний день абсолютно не поддер-живают одно из главных преимуществ TypeScript – статическую типизацию и ее следствие — кон-троль типов на стадии компиляции. JS фреймворки в этом винить нет никакого смысла. В языке просто нет средств для этого.

Однако, это далеко не единственна проблема. Что гораздо хуже, в JS очень часто используются примеси, в частности весь Backbone построен на них. Нет, в них нет ничего плохого в самих по себе. Это вполне естественная и жизнеспособная практика в контексте чистого прототипного динамического JS и небольших проектов. В случае TS она приводит к ряду неприятных последствий, особенно при попытке создать приложение хоть сколько-нибудь серьезного размера:

  1. Аналогом примесей в классическом ООП является множественное наследование. Не хотелось бы вступать в «священную войну», но на мой взгляд множественное наследование всегда плохо. Особенно в условиях динамической типизации, без возможности контролировать поведение объектов хотя бы через явную или неявную реализацию интерфейсов а-ля C#. Естественно в JS об этом можно даже не мечтать, поэтому отладка, поддержка и рефакторинг подобного кода — полный кошмар.
  2. Если отвлечься от высоких материй, то TS просто не поддерживает подобное на уровне язы-ка. Бэкбоновский extend это полный аналог наследования в TS и это вполне работает для Model и View, но абсолютно не подходит для событий. Нет, мы конечно можем унаследовать все классы в приложении от Backbone.Event или его аналога в зависимости от фреймворка и добиться результата, но это не решает 3-ей проблемы:
  3. События Backbone или любого другого JS фреймворка не типизированы. Прощай статический анализ и все преимущества TS.
Что вообще такое события и что вообще от них нужно

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

Но все меняется, если появляется некоторый контекст. В нашем случае контекстом является JavaScript, который без любых надстроек в виде TS уже является 100% ООП языком. В частности все сущности в JS это объекты. Так же объектом являются и DOMEvent, создаваемые браузером. Т.е., если продолжить аналогию, то любое событие является объектом.

Допустим, что в случае Backbone событие это тоже объект. Вопрос — а какой? По сути, у нас есть коллекция callback’ов, которые вызываются по тем или иным правилам. Коллекция универсальна. Она способна принять любые функции. Т.е., опять я на этом остановлюсь, у нас нет типизации.

Но постойте. Какова наша цель? Получить статический анализ кода. Значит, событие должно быть объектом и иметь тип — класс. Это первое требование, которое я выдвигаю. События должны быть описаны классами, чтобы их можно было типизировать.

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

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

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

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

Собрав мысли в кучу и включив KMFDM я приступаю к решению созданных самому себе проблем.

Исходники, по-прежнему на Codeplex: https://tsasyncmodulesexampleapp.codeplex.com

Первые мысли

И так, любой объект, событие это класс и т.д. означают 2 вещи: во-первых у нас есть базовый класс Event:

export class Event {     //Реализацию временно опускаю     Add(callback: any): void { /* Делаем полезную работу */ }     Remove(callback: any): void { /* Делаем полезную работу */ }     Trigger(): void { /* Делаем полезную работу */ } } 

Во-вторых использовать мы его будем примерно так, аккуратно украдкой посмотрев в сторону C# и вдохновившись его примером:

/// <reference path="Events.ts" />  import Events = require('Framework/Events');  export class MessagesRepo {     public MessagesLoaded: Events.Event = new Events.Event(); }  class SomeEventSubscriber {     //Не пинайте. Это просто пример     private MessagesRepo = new MessagesRepo();      public Foo()     {         this.MessagesRepo.MessagesLoaded.Add(function () { alert('MessagesLoaded'); });     } }  

Т.е. событие это просто публичный член класса. Не более и не менее. Что нам это дает:

  • События известны на стадии компиляции
  • События могут быть объявлены в любом классе
  • У нас есть минимально необходимый функционал, он сосредоточен в одном месте и легко модифицируется.
  • Все события реализуются одним классом или его наследниками, т.е. мы легко можем поме-нять логику их работы, например создав потомка SecureEvent, унаследованного от Event, выполняющего callback’и только при определенных условиях.
  • Нет типичного геморроя JS фреймворков с контекстом, который теперь строго зависит от эк-земпляров объектов, опечатками в названиях событий и т.д.

Чего у нас по-прежнему нет:

1. Строгой типизации
2. Из-за отсутствия контекста, невозможно выполнить отписку от события callback’а, заданного анонимной функцией, т.е. любой callback мы должны где-то сохранять, что неудобно.
3. Не типизированные параметры события

Строгая типизация

Разберемся с первой проблемой. Используем нововведение TypeScript 0.9 — обобщения (generics):

export class Event<Callback extends Function> {     //Реализацию все еще опускаю     Add(callback: Callback): void { /* Делаем полезную работу */ }     Remove(callback: Callback): void { /* Делаем полезную работу */ }     Trigger(): void { /* Делаем полезную работу */ } } 

И посмотрим на применение:

/// <reference path="Events.ts" />  import Events = require('Framework/Events');  export class MessagesRepo {     public MessagesLoaded: Events.Event<{ (messages: string[]): void }>          = new Events.Event<{ (messages: string[]): void }>(); }  class SomeEventSubscriber {     //Не пинайте. Это просто пример     private MessagesRepo = new MessagesRepo();      public Foo()     {         this.MessagesRepo.MessagesLoaded.Add(function (messages: string[]) { alert('MessagesLoaded'); });     } } 

При этом, следующий код:

public Foo() {     this.MessagesRepo.MessagesLoaded.Add(function (message: string) { alert('MessagesLoaded'); }); } 

Выдаст ошибку:

Supplied parameters do not match any signature of call target: Call signatures of types '(message: string) => void' and '(messages: string[]) => void' are incompatible: Type 'String' is missing property 'join' from type 'string[]'

А callback без параметров (ну не нужны они нам), скомпилируется спокойно:

public Foo() {     this.MessagesRepo.MessagesLoaded.Add(function () { alert('MessagesLoaded'); }); } 

Конструкция Callback extends Function необходима для корректной компиляции, т.к. TS должен знать, что Callback можно вызвать.

Анонимные callback’и и возврат состояний подписки

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

private FooMessagesLoadedCallback = function () { alert('MessagesLoaded'); }  public Foo() {     this.MessagesRepo.MessagesLoaded.Add(this.FooMessagesLoadedCallback); } 

На мой взгляд это полный энтерпрайз головного мозга и убийство всех функциональных черт JS/TS.

Тем не менее, без отписки от событий не обойтись в любом более-менее сложном приложении, т.к. без этого невозможно корректно уничтожать сложные объекты и управлять поведением объектов, участвующих во взаимодействии через события. Например, у нас есть некоторый базовый класс формы FormBase, от которого унаследованы все формы в нашем приложении. Предположим, что у него есть некоторый метод Destroy, который очищает все ненужные ресурсы, отвязывает события и т.д. Классы-потомки переопределяют его при необходимости. Если все функции сохранены в переменных, то нет никакой проблемы передать их событию, а у события через равенство ссылок не никакjй проблемы определить callback и удалить его из коллекции. Данный сценарий невозможен при использовании анонимных функций.

Я предлагаю решать вторую проблему следующим путем:

export class Event<Callback extends Function> {     public Add(callback: Callback): ITypedSubscription<Callback, Event<Callback>>     {          var that = this;          var res: ITypedSubscription<Callback, Event<Callback>> =         {             Callback: callback,             Event: that,             Unsubscribe: function () { that.Remove(callback); }         }          /* Делаем полезную работу */          return res;     }     public Remove(callback: Callback): void { /* Делаем полезную работу */ }     public Trigger(): void { /* Делаем полезную работу */ } }  /** Базовый интерфейс подписки на событие. Минимальная функциональность. Можем просто отпи-саться и все. */ export interface ISubscription {     Unsubscribe: { (): void }; }  /** Типизированная версия. Включает ссылки на событие и callback */ export interface ITypedSubscription<Callback, Event> extends ISubscription {     Callback: Callback;     Event: Event; } 

Т.е просто возвращаем в методе Add ссылку на событие, callback и обертку для метода Remove. После этого остается реализовать элементарный «финализатор» у подписчика:

/** Кстати, такие комментарии опознаются IntelliSense ;) */ class SomeEventSubscriber {     private MessagesRepo = new MessagesRepo();      /** Тут будем хранить все подписки нашего класса */     private Subscriptions: Events.ISubscription[] = [];      public Foo()     {         //Одним движение регистрируем подписку одного события         this.Subscriptions.push(this.MessagesRepo.MessagesLoaded.Add(function () { alert('MessagesLoaded'); }));         //И совершенно другого         this.Subscriptions.push(this.MessagesRepo.ErrorHappened.Add(function (error: any) { alert(error); }));     }      /** Просто проходит по массиву подписок и отписывает все события независимо от типа */     public Destroy()     {         for (var i = 0; i < this.Subscriptions.length; i++)         {             this.Subscriptions[i].Unsubscribe();         }          this.Subscriptions = [];     } } 
Типизация параметров события

Все очень просто. Опять используем обобщения:

export class Event<Callback extends Function, Options> {     public Add(callback: Callback): ITypedSubscription<Callback, Event<Callback, Options>>     {          var that = this;          var res: ITypedSubscription<Callback, Event<Callback, Options>> =         {             Callback: callback,             Event: that,             Unsubscribe: function () { that.Remove(callback); }         }          /* Делаем полезную работу */          return res;     }     public Remove(callback: Callback): void { /* Делаем полезную работу */ }     public Trigger(options: Options): void { /* Делаем полезную работу */ } } 

Класс-издатель теперь будет выглядеть так:

export interface ErrorHappenedOptions {     Error: any; }  export class MessagesRepo {     public MessagesLoaded: Events.Event<         { (messages: string[]): void } //Callback         , string[]> //Options         = new Events.Event<{ (messages: string[]): void }, string[]>();     public ErrorHappened: Events.Event<         { (error: any): void }, //Callback         ErrorHappenedOptions> //Options         = new Events.Event<{ (error: any): void }, ErrorHappenedOptions>(); } 

А вызов события так:

var subscriber: Messages.SomeEventSubscriber = new Messages.SomeEventSubscriber();  subscriber.MessagesRepo.MessagesLoaded.Trigger(['Test message 1']); subscriber.MessagesRepo.ErrorHappened.Trigger({     Error: 'Test error 1' }); 

На этом мои хотелки к событиям заканчиваются. За полными исходными кодами и действующим примером прошу на Codeplex.

Всем спасибо за положительную оценку первой части.

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

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