In vitrtual: заметки о жизни виртуальной

RIP Lastfm.ru, ты был хорошим другом.


RIP Lastfm.ru Сегодня вечером пришел нежданчик от Last.fm:

Last.fm вносит ряд изменений в радио, которое ранее было доступно во всем мире. Мы тщательно обдумали эти изменения, и эти изменения не случайны: они были приняты в ответ на на ряд факторов, в разной степени влияющих на наш бизнес в разных странах.

До сегодняшнего дня радио было функцией только для подписчиков. Однако начиная с 15 Янв 2013 мы вынуждены прекратить предоставление потокового радио в твоей стране в связи с лицензионными ограничениями. Это значит, что ты больше не сможешь слушать радио Last.fm.

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

Подписчики по-прежнему смогут пользоваться своими преимуществами: сайтом без рекламы, доступом к демонстрациям в разделе Last.fm Playground и другие возможностями, которые будут добавлены в дальнейшем.

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

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

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

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

Спасибо,
Команда Last.fm

Для тех, кому лень было читать, краткое резюме: в России даже платные подписчики теперь не смогут слушать радио. Но пусть они не унывают: у них по-прежнему останется сайт без рекламы, доступ к красивым графикам на playground и стильный значок VIP в профиле.

Для тех, кому читать не лень, вот то же самое, но подробнее в блоге lasf.fm.

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

Вопрос о том, куда мигрировать сейчас активно обсуждается на Хабре, а я скорее всего перейду на Яндекс.Музыку. Ещё есть libre.fm, но интерфейс и выбор на нём странноват. Кстати, и на YouTube музыку тоже можно послушать, хотя и не очень удобно.

Четыре лика Open Source


Open Source logo Поводом к этому посту стала одна дискуссия об хороших и плохих сторонах открытого софта, в которой мне довелось участвовать. Я являюсь давнишним пользователем Linux, а потому поневоле постоянно имею дело с большим количеством open-source проектов. Более того, так повелось, что большинство инструментов и фреймворков для веб-разработки, которой я занимаюсь, так же является открытым. Поэтому в ходе того обсуждения у меня сформулировалось мнение, заслуживющее отдельного упорядоченного изложения.

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

Мир первый: большой Open Source.

Сюда относятся крупнейшие открытые проекты, над которыми работают сотни людей. Именно о них упоминают в первую очередь, когда хотят проиллюстрировать важную роль открытого ПО в IT-индустрии: ядро Linux, Apache, FreeBSD, KDE, GNOME, Mozilla Firefox. Следом за ними идут продукты менее именитые, но от того не менее важные: Drupal, phpBB, OpenJDK, Eclipse, целая плеяда языков программирования и многие другие.

У этих проектов одна общая черта: за ними стоят различные коммерческие организации, которым открытое ПО служит основой их бизнеса. Для Linux это в первую очередь RedHat, в Apache заинтересована практически вся хостинговая индустрия, FreeBSD прочно прописался на высоконагруженных серверах, в KDE и GNOME заинтересованны множество (полу-)коммерческих дистрибутивов, спонсирующих их разработку, Mozilla Foundation имеет спонсорские соглашения со многими интернет-компаниями, в числе которых Google. Факт тот, что именно наличие заинтересованного бизнеса позволяет открытому проекту перейти в клан тяжеловесов, получая инвестиции в виде долларов или человеко-часов.

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

Мир второй: клубы по интересам.

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

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

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

Мир третий: соло-проекты.

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

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

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

Мир четвёртый: сделал для себя, поделился с остальными.

Такие проекты возникают по принципу: "Возникла проблема, я её решил, вдруг кому-то пригодится?"

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

Если к первому названному миру я отношусь с уважением, ко второму — с восхищением, а к третьему — с пониманием, то к этому, четвёртому, я испытываю самые противоречивые чувства. Тут можно найти всё, что угодно, но найти и заставить работать настолько сложно, что проще самому написать. А от сюда возникает огромное количество велосипедов, что не облегчает поиск в следующий раз :-) И если уж говорить про гитхаб, то у них есть отдельный подсервис специально для таких вот проектов: GitHub:Gist.

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

Каждому своё

Каждый из миров, о котором я говорил, имеет свои достоинства и недостатки для разработчиков и конечных пользователей. Где-то ценою за надёжность становится гибкость в развитии, где-то разработчики готовы пожертвовать стабильностью во имя пребывания на острие технологий. Часто люди сталкиваются лишь с одним из ликов открытого программного обеспечения и пугаются, думая что есть только оно, и совершенно напрасно. Многоликость open source — это его сила, надо лишь уметь ею пользоваться :-)

С днем программиста!


Поздравляю, коллеги, с профессиональным праздником!

Не буду долго перечислять пожелания — всё всем и так понятно :-) Ну а кому все же хочется пожеланий — добро пожаловать на Хабр, по такому случаю там целых два топика: первый и второй. А для тех, кому не лень пошевелить мозгами, там же есть набор праздничных головоломок. Сумеете решить?

Возвращаясь к пожеланиям, я пожелаю лишь одно: не пускайте корни в своей берлоге. Она хоть и уютная, но за ее пределами есть много интересного!

Обработка множества исключений: решение


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

Решение 1, читерское.

В условии было сказано, что надо обрабатывать исключения в методах init(), run() и shutdown(). И хотя это явно не оговаривалось, по логике функционирования эти методы вызываются подряд. Следовательно, можно сделать так:

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

Достоинства решения:

  • простота;
  • минимум дублирования кода;
  • локальность модификации кода.

Недостатки:

  • жестко привязываемся к сценарию последовательного вызова методов init(), run(), shutdown(), в реальной жизни может потребоваться вызывать их из разных частей приложения в разные моменты времени;
  • метод-обработчик исключений должен очень многое знать обо всех возможных исключениях, при появлении нового исключения придется править метод-обработчик.

Решение 2, логичное.

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

Именно его в комментариях предложил Vladimir Rusinov, за что ему спасибо:

Достоинства решения:

  • простота,
  • независимость от мест вызова методов (то есть, их не обязательно вызывать подряд),
  • минимум дублирования кода,
  • локальность правок.

Недостатки решения:

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

Решение 3, хитрое.

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

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

В PHP все исключения имеют по умолчанию два поля (есть и другие, но они нас не интересуют): $message = null и $code = 0, сообщение и код ошибки соответственно. Во всех наших исключениях мы переопределяем значение по умолчанию для кода ошибки, чтобы оно соответствовало типу исключения.

Теперь, когда мы создаем исключение, мы можем задать ему сообщение, которое описывает причину его возникновения, и, если надо, можем уточнить и код ошибки. Если же мы сообщение вручную не зададим - оно будет получено через класс Errors. В случае, если мы поймаем "не наше" исключение (типа простого Exception), у него код ошибки будет 0, и класс Errors успешно опишет его как "Unknown exception".

Достоинства решения:

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

Недостатки решения

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

Итоги

Я привел целых три решения задачи и видно, что ни одно из них не является безоговорочно правильным. А это значит, что даже такая простая задача, как описано в этом упражнении, требует внимательного анализа требований к системе и ваших возможностей, как ее разработчика. В моем случае решение 3 оказалось наименьшим злом, и я использовал его. Но запросто может оказаться, что в другой ситуации оптимально "наивное" решение 1.

Упражнение: обработка множества исключений


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

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

  1. init() — инициализирует, необходимые для обработки запроса объекты.
  2. run() — запускает обработку бизнес-логики.
  3. shutdown() — освободжает ресурсы.

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

Так же будем считать, что нам на любом этапе доступен метод Client::reportError($error_code, $error_message), который обеспечивает вывод клиенту сообщения об ошибке и прекращение работы программы.

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

Естественно, решение "в лоб" не подходит, поскольку оно неизбежно приведет к большому количеству дублирующего кода.

Как бы вы решили такую задачу? Языки решения принимаются любые в пределах разумного: Java, PHP, C++. Не возбраняется использование языко-специфичных конструкций, если они действительно удобны — расширение кругозора никому лишним не будет ;-)

Я нашел два приемлемых решения, и опишу оба завтра, если их никто не назовет до меня.