Притча о программистах и кодерах

в 10:33, , рубрики: ооп, паттерны, приёмы программирования, Программирование

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

image

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

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

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

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

И тут возмущённые кодеры возроптали: «Мы знаем все эти фундаментальные принципы! Они все бесполезные и глупые!». А вселенная ответила им: «Возможно, это так так, но, возможно, вы не понимаете их, а потому не умеете применять».

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

Часть 1: Основы

Пример 1:

if ($u->g) $u->reg($u->email, $u->nm);

Просто строчка кода, написанная кодером, но что она делает совсем не очевидно и требует, как минимум комментариев.
Теперь та же строчка кода, написанная программистом:

if ($user->isGuest) $user->register($user->email, $user->name);

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

Пример 2:

//…
$sql = 'SELECT * FROM usr LIMIT 10';
//…
/** view */
if ($n<10) echo ...;

Вроде бы код более или менее ясен, хотя магические константы вроде 10 настораживают, кроме того, код встречается в разных файлах и часто повторяется.
Как бы написал его программист? Да примерно так:

class User
{
	const DB_TABLE_NAME = 'User';
	const MAX_VIEW_USERS_ON_PAGE = 10;
	... ... ...
	$sqlQuery = 'SELECT * FROM `'.User::DB_TABLE_NAME.'` LIMIT '.User::MAX_VIEW_USERS_ON_PAGE;
	…
/** view */
if (count($users) < User::MAX_VIEW_USERS_ON_PAGE) echo ...;

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

Пример 3:

if ((isset($user->online) || (time() - $user->lastVisit < User::LOGOUT_TIMEOUT)) && Post::getNumOfPostsForUser($user->id) > Post::ACTIVE_USER_MIN_POSTS) 
	$Raiting::addBonus($user->id, Rating::BONUS_RATING_POINTS);

Вроде бы переменные с методами и константами названы хорошо, но всё равно как-то чересчур сложно прочесть.

$userOnline = (isset($user->online) || (time() - $user->lastVisit < User::LOGOUT_TIMEOUT));
$userIsActivePoster = Post::getNumOfPostsForUser($user->id) > Post::ACTIVE_USER_MIN_POSTS;

if ($userOnline && $userIsActivePoster) $Raiting::addBonus($user->id, Rating::BONUS_RATING_POINTS);

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

Пример 4:

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

$stringOfText = '';
$delimiter = '_';
foreach ($parts as $part)
    if ($part) $stringOfText .= $part.$delimiter;
$stringOfText .= 'END';

Как сделать ситуацию лучше?

class StringHelper
{
	static function implode($parts, $delimiter)
	{
			$stringOfText = '';
			$delimiter = '_';
			foreach ($parts as $part)
			    if ($part) $stringOfText .= $part.$delimiter;
			$stringOfText .= 'END';
			return $stringOfText;
	}
...
$stringOfText = StringHelper::implode($parts, $delimiter);

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

Часть 2: Объекты и классы

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

Пример 5:

Кодер научился извлекать данные из таблиц базы данных и теперь во всех файлах пишет заклинание:

$sqlQuery = "SELECT * FROM User WHERE id=:id";
$connection = $this->getDbConnection();
$command = $connection->createCommand($sqlQuery);
$user = $command->execute(array(':id'=>$id));
echo $user['name'];

Но благодаря ООП и специально подготовленному классу можно написать значительно короче и понятнее:

$user->loadById($id);
echo $user->name;
Пример 6:

Кодер написал несколько классов для поддерживаемых его сайтом платёжных систем, в каждом есть несколько совершенно одинаковых методов и полей.

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

Часть 3: Модульность

Кодеры недоумевают: «Зачем в программе делать кучу файлов, да ещё и в разных папках? Можно сделать один-два файла и там будут все константы, классы и функции нашего проекта, а переменные все будут глобальными.»

Вселенная в ответ: «Да… но тогда ты сам и разбирай свои километровые Gовно файлы».

Пример 7:

Кодер научился писать в MVC фреймворке, но не познакомился с модульной структурой и пишет весь код в модели:

class Article extends CModel
{
	... ...
	public static function getDecoratedHeader($header)
	{
		$words = explode(' ', $header);
		$words[0] = '<span class="decorator">' . $words[0] . '</span>';
		return implode(' ', $words);
	}
	... ...
}

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

Часть 4: Паттерны

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

Пример 8:

Кодер использует в своём приложении внешний компонент Mailer и пишет в модулях системы вызовы его метода send().
Mailer::send($subject, $message);
Но вот беда, в новой версии этого компонента, исправляющей кучу багов и повышающей быстродействие, метод send() убрали и заменили на метод post() с другими обязательными параметрами. Кодеру придётся перелопатить весь код приложения, исправляя все вызовы компонента, а если бы он использовал один из самых простых паттернов — метод доступа:

class MailerWrap 
{
	public function send($params)
	{
		return Mailer::send($params);
	}
}

Программисту было бы достаточно изменить только один метод с вызовом в классе-обёртке компонента Mailer чтобы воспользоваться всеми преимуществами новой версии.

Часть 5: Фреймворки

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

Пример 9:

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

Часть 6: Оптимизация

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

Пример 10:

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

Пример 11:

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

Заключение

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

Да пребудет с тобой это великое знание, юный падаван!

Автор:

Источник

* - обязательные к заполнению поля