Я пишу программы уже больше 15-ти лет, из них 11 — профессионально. Т.е. получаю за это деньги. Через меня прошло десятки коммерческих проектов разной величины. Вспоминая себя того, совсем юного девелопера, я понимаю насколько разнится
В те годы, 10 лет назад, я думал скорее о хорошем тоне программирования, комментариях, наследовании и паттернах. О заученных постулатах Страуструпа и Кнута. К слову последний, до сих пор загорается яркой лампой в голове, со своей оптимизацией программ. Это «O(N^2) или O(NlogN)» невольно думается, когда выводишь 7 записей по дням недели.
А стоило прочитать серию для экспертов С++, того же Александреску или Скотта Мейрса, так
При этом программа выводила ничего. А заказчик то ждал…
Оглядываясь на то время, и наблюдая как дела обстоят сейчас, я решил собрать небольшой шорт лист из костылей, хаков, обходных путей (workarrounds) и прочих нетривиальных ходов, о которых начинающий разработчик может не задумываться. Да впрочем не только он.
В качестве эпиграфа
Не, ну это же классика кун-фу:
Сначала ты не знаешь, что нельзя делать то-то
Потом знаешь, что нельзя делать то-то
Потом ты понимаешь, что иногда таки можно делать то-то
Ну а потом ты понимаешь, что помимо того-то существует еще шестьдесять шесть способов добиться желаемого, и все из них практически равноправны.
Когда тебя спрашивают «как мне добиться желаемого», ты быстро перебираешь в уме эти шестьдесять шесть способов, прикидываешь то общее, что в них есть, вздыхаешь и говоришь: «вообще-то, главное — гармония.»
На вопрос обиженных учеников: «а как ее добиться?», ты говоришь: «никогда не делайте то-то».
Оригинал
Еще немного философии
Между тем как видит программу пользователь и как видит ее разработчик — пропасть. Мы видим коды, архитектуру, хитросплетение классов и функций. Человек же видит кнопки, поля
ввода и картинки. Не редко, пользователь получает результат, который совсем не ожидает. Не редко, пользователю выдается ошибка, от которой тот начинает нервничать и ерзать на стуле. А нам всего-то строчку поправить.
Так вот главное связующее звено между вешей программой и пользователем — это его восприятие. То есть то, что видит пользователь — такие у него и ощущения от вашей программы. Казалось бы очевидные вещи, но есть другая сторона медали. Это восприятие программиста. Об этом говорилось много раз, говорилось о правильном построение архитектуры, разделении обязанностей между классами, асинхронности и производительности.
Но редко кто заикался о восприятии самого пользователя. Того парня, который никогда не увидит SOLID-принципов за приложением с одной кнопкой. Того человека, который и платит деньги, и во многом благодаря которому эта программа [еще] существует.
Большая часть моего опыта — это фриланс. Или работа на заказ, с прямым общением с заказчиком. В этом плане работа в большой IT-компании в корне отличается восприятием процесса разработки, хотя бы по причине отсутствия контакта с конечным пользователем. Сеньоры в таких компаниях могут неделями и месяцами выстраивать многослойную архитектуру, обобщать интерфейсы и дженерики, и в 99% случаев заниматься оверинженирингом.
Во фрилансе же — ситуация ровно противоположная. Нет, тут не царит повсеместный говнокод… хотя кому я вру.
Часто именно он самый, да кодинг на скорость — вот что получается от хотелок заказчика. Т.к. бюджет лимитирован, а на код, код-ревью, юнит-тесты, QA всем плевать.
Есть исключения, но если мы говорим об условном Джонни из Флориды с 1000$ в кармане, и Вами на другом конце провода в качестве исполнителя — то ситуация именно
такая. Не GoF паттерны с юнит-тестами ждет Джонни за свои кровные, а удовлетворить свой business need.
Ок. Бог с ним с фрилансом. Вариант №2 — крупная компания и дедлайн. Очень распространенная ситуация. Сдавать новую версию нужно вчера, а приложение в один из 100 запусков выдает NPE. Что делать? Сгустились тучи над компанией, все сбежались один баг фиксить. И так и этак, а у кого-то дай и упадет. Не до хорошего уже. Костыль один, второй, третий… и о чудо! — Не падает!
Это было лирическое отступление. А теперь ближе к списку хаков.
Суть
Для начала, давайте отойдем от кода и обратим внимание на две главные вещи, которые
влияют на восприятие пользователя при работе с программой — это А) Картинка Б) Время.
Повторюсь, именно Картинка — то, что видит пользователь, и Время — сколько времени он работает, ждет, загружает, и так далее. Звуки, вибрации, может быть даже запахи — тоже возможно. Но в конечном счете взаимодействие сводится к этим двум.
Скажу более, картинка и время — это не только про пользователя. Это и про заказчика, и про менеджера, и про процесс разработки в целом. Именно поэтому — я всегда начинаю разработку с UI. Всегда. Чем раньше покажешь красивые интерфейс заказчику, тем сильнее будет впечатление, и тем быстрее выстроятся доверительные отношения между вами. Будь я тим-лидом, фрилансером или шараварщиком — первое, что я делал — это Картинка (User Interface). Нет, даже не так. Very Cool User Interface. И плевать на внутренности и все остальное на этом этапе.
На кону время. Время разработки и установление доверительных отношений.
А теперь наконец о хаках. Тех самых хитростях, которые могут помочь вам справиться с дедлайном, пофиксить неуловимый баг или «сделать видимость как должно быть». Вы должно быть их знаете, но только в другом контексте.
Поэтому я добавил примеры из моих реальных проектов, где эти хаки пригодились.
Девхаки. Хаки при разработке.
1. Таймер
Швейцарские нож среди костылей (извините за каламбур). Насколько мне известно, каждый известный язык программирования (не запросов и не разметки) имеет таймер. Точнее стандартная библиотека языка. Таймер можно комбинировать с другими девхаками.
Таймер идет первым по списку, потому что именно он позволяет перехватить любое неведомое событие происходящее через определенный промежуток времени.
Это может быть вообще за пределами программы или происходить в закрытом модуле. Проходит 5 секунд — и обновить, закрыть, прибить, ваш вариант.
Вариация на тему: отдельный поток с функцией Sleep().
Пример из реального проекта:
Плагин для браузера, после загрузки страницы, должен был изменять ее содержимое. Стандартный обработчик loaded или ready() срабатывал слишком рано, и не все элементы и фреймы подгружались к этому моменту. Поэтому пользователю показывалось
белое окно поверх сайта, которое через 10 секунд пропадало. Чего было вполне достаточно, чтобы весь контент загрузился и плагин изменил содержимое всей страницы.
2. Watchdog
Логическое продолжение девхака «таймер», с уточнением, что «собака» мониторит другой процесс, делая регулярные проверки.
Процессом может быть как само приложение, внутри которого сидит «собака», так и внешний процесс. Так что, если вы вдруг услышите от другого разработчика — «А давай на него собаку пустим!» — знайте, речь идет о watchdog.
Примеры из реальных проектов:
1. Программа не должна была быть закрыта или прибита из таск менеджера. Она запускала watchdog, который каждую милисекунду проверял список запущенных процессов, и в случае чего, запускал основной процесс заново.
2. Программа делала backup'ы файлов. Второй Watchdog процесс мониторил создание бекапов в определенной папке и отправлял уведомления на сервер.
3. Callback
Очень часто, особенно в больших проектах, существует проблема передать данные из одного объекта в другой. Для этих целей можно использовать и Dependedncy Injection, однако не везде подходит. Речь же сейчас идет о девхаках. (Злобный читатель может назвать их грязыми хаками, но это не так. Некоторые из них довольно чистые).
В C# для этого отлично подходят обертки над делегатами Action, Func, В других языках — (указатели на) функции. До них будет просто добраться, если сделать статическими полями.
Пример из реального проекта:
В отдельном usercontrol пользователь нажимает checkbox. В основном окне, нужно проверить, чтобы отмеченных флажков было не больше 5.
На C#:
class MyControl
{
public static Func<bool> IsMoreThan5;
void OnChecked(CheckBox checkBox)
{
if (IsMoreThan5(checkBox))
{
Error("Можно отметить только 5 флажков!");
checkBox.IsChecked = false;
}
}
}
class MainWindoow
{
public MainWindoow()
{
MyControl.IsMoreThan5 = MoreThan5; // Задаем Callback
}
private bool MoreThan5()
{
int total = 0;
foreach (var checkBox in AllCheckboxes)
{
if (++total > 5)
return true;
}
return false;
}
}
4. Визуальный эффект
Широко известный способ не дать заскучать пользователю, пока выполняется долгая операция. Также несет информативную состовляющую, сообщая сколько времени, процентов еще ждать.
В реальном проекте:
На одном вебсайте, пока загружались все данные, мы показывали цитаты великих людей. Пользователям нравилось.
5. Скрыть/показать
Не столько интересен сам хак, сколько проблемы, которые он помог решить:
1. Однажды мне попался проект десктопного приложения, где фронт-енд был написан на HTML/JS и хостился в CEFSharp браузере.
Так как приложение было под Windows, а мне гораздо удобнее работать с WPF, чем HTML, то было сделано следующее:
Когда нужно было открыть экран с HTML-UI — показывался CEFSharp браузер с ним. А уже новые окна, которые были сделаны на WPF — показывались вместо браузера. Сам CEFSharp — прятался. Заказчик остался доволен.
2. Многие приложения устроены по принципу: разные меню, фон и основной placeholder — место где изменяется содержимое. Придумано множество фреймворков, где нужно создать 3 разных файла, 2 проекта, и в 5 местах зарегистрировать экраны, чтобы их можно было
переключать. В WPF приложениях я поступаю проще — храню все экраны (контролы) в свернутом виде в основном окне, и по запросу один показываю, а остальные прячу. Никаких оверхедов и фреймворков. Все просто.
6. Отодвинуть окно за пределы экрана
Пример из реального проекта:
Нужно было запускать приложение, но чтобы пользователь его не видел. Malware? — спросите вы. Нет, не оно. В окне приложения грузился другой сайт и его содержимое нужно было проанализировать в фоне. Делаем что-то вроде
window.SetPosition(-10000, 0);
И окно улетает за пределы экрана, выполняя свою работу и не мешая пользователю.
7. Exe wrapping
Миллионы готовых утилит и программ лежит в свободном доступе. Возможно не нужно ничего писать? Зашить утилиту в ресурсы своего приложения, распаковать при старте и запустить. (Опционально)
Есть два момента: 1) антивирусу это может не понравиться 2) проверяйте лицензию программы.
Пример из реального проекта:
ffmpeg — универсальная утилита. Везде, где фото и видео обработка в проекте требовала больших усилий — запускался ffmpeg.
8. Точный случай (Exact case)
Когда-то на хабре пробегал перевод про геймдев, в котором разработчик отчаянно пытался пофиксить баг. Если не ошибаюсь, его персонаж постоянно проваливался за стену именно в одном месте. Тогда он решил сделать фикс таким образом:
когда объект находится именно в этой координате, то сдвигать его в сторону. Profit!
Если же у вас случается exception при каком-то определенном условии — логично его проверять и корректировать данные.
9. Хуки
Это из области системного программирования. Хуки можно повесить глобально на почти любые события в ОС. Будь-то события клавиатуры, мыши, запуск процессов итд.
Пример из проекта:
Писал я как-то кейлоггер… Нет, была и реальная необходимость. Например в приложении для проверки зрения, мышь нужно было останавливать возле определенного символа. Девхак Хук может быть полезен, если например мы имеем закрытый модуль, защищенный
от изменений, а он себя не правильно ведет при нажатии клавиш или кнопок мыши. Хук на клаву делает workarround.
Далее речь пойдет меньше о технических, больше о жизненных хаках. О лайфхаках.
Лайфхаки
10. Чистый проект
Это из области психологии. Чем больше визуального шума, разных сущностей и раздражителей в нашем поле зрения — тем сильнее это давит на
В реальных проектах, я всегда работаю только с одним проектом обособленно от остальных. Стараюсь максимально разделять один модуль и всю систему, при этом обязательно оставляя возможность запускать модуль самостоятельно, без всей системы.
Преимущества очевидны:
a) Время компиляции не всего солюшена, а только небольшого модуля в x5, x10 раз быстрее
б) Внимание сосредоточено там, где должно быть.
11. Аутсорс подзадачи
А задумывались ли вы когда-нибудь, зачем писать код, если его может написать кто-нибудь другой? Мы как разработчики, часто думаем, что именно мы должны делать реализацию всего проекта От и До.
Но ведь есть еще много свободных рук, которые свежим взглядом наговнокодят могут решить часть задачи, избавив вас от рутины или чего еще.
В реальном проекте:
Например у меня не было времени разбираться с модулем, в котором полно матана. В итоге за небольшую сумму отдал задачу написать модуль другому фрилансеру и по завершении спокойно прицепил его во всю систему.
Давая фрилансеру подробную спецификацию и понимания четко что нужно сделать — положительный результат гарантирован.
Более того, только начав формировать задачу, а затем получая заявки от разработчиков, уже на этом этапе можно найти решение, не тратя деньги.
12. Звонок другу
Как вариант — вопрос коллеге. Мы программисты погружены в свой мир, который и является рамками нашего
Хотя бы проговорить задачу и проблему, а на том конце услышать «Угу» — уже разгрузит голову и подведет ближе к решению.
Неплохо работает в комбинации с лайфхаком «Чистый проект».
13. Декомпиляция
Где может пригодиться:
— Узнать как работает код закрытого модуля (в первую очередь)
— Проверить секьюрность программы, в том числе обфусцированной
— Взять кусок кода себе (в последнюю очередь)
На этом мой пост заканчивается, а у вас, я уверен, есть еще десятки хаков в запасе, которые экономят время, силы и решают проблемы.
Автор: Денис