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

Давним давно, в далекій предалекой галактиці, на одній провінційної планетке жили розумні ссавці, у яких нещодавно розпочався століття інформаційних технологій. У той вік багатьом доводилося писати програми на різних мовах для різних програмних платформ. І будь-нащадок мавпи з цієї планети, написавши хоча б пару рядків коду, який змусив тупу обчислювальну машину зробити кілька розумних (з точки зору автора) дій, вже вважав себе просвітленим мудрецем, осягнув ДАО інформаційних технологій і називався не інакше як джедаєм програмістом.

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:
Код повторюється в кількох місцях:

$hdr = explode(' ',trim($header));
$hdr[0] = '<span class="hdr-decor">'.$hdr[0].'</span>';
$header = implode(' ',$hdr);

Як зробити ситуацію краще?

class StringHelper
{
/**
* Return header text with CSS
* @param string $header
* @return string
*/ 
public static function getCSSDecoratedHeader($header)
{
$hdr = explode(' ',trim($header));
$hdr[0] = '<span class="hdr-decor">'.$hdr[0].'</span>';
return implode(' ',$hdr);
}
...
$header = StringHelper::getCSSDecoratedHeader($header);

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

Частина 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 можна написати значно коротше і зрозуміліше:

$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);
}
... ...
}

Програміст знає, що в моделі повинен бути виключно код для роботи з даними цієї моделі і такий метод помістить у хелпер StringHelper::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 функцій, після чого зайнявся б оптимізацією самої довго виконується функції. Інші функції, час виконання яких значно (на порядок) менше основний, не став оптимізувати зовсім.

Частина 7: Безпека

Кодер ніколи особливо не замислювався про безпеку коду. Навіщо? Адже ніхто ніколи не буде зламувати його програми…
Приклад 12:
у кодера безліч запитів в додатку, де він просто підставляє передані від користувача дані (GET, POST, COOKIES, JSON, XML) безпосередньо в запит:
$sqlQuery = 'SELECT `name`, `surname` FROM `User` WHERE `id`='.$_GET['id'];
$command->create($sqlQuery);
$result = $command- > execute();
print_r($result);

А що буде, якщо «користувач» передасть в параметрі id наступний рядок «0 UNION SELECT `email`, `password` FROM `User` LIMIT 10»? Напевно буде повний ай-ай-ай!
Програмісти пишуть такі запити приблизно так:
$sqlQuery = 'SELECT * FROM `User` WHERE `id`=:id';
$command->create($sqlQuery);
$result = $command- > execute(array(':id'=>$_GET['id']));
print_r($result);

Та й про підстановку значень функції типу eval() програмісти теж ніколи не забувають, як і про кльове слово «валідація».

Висновок

Принципи побудови всесвіту вкрай прості, як і весь всесвіт, саме тому вона так велика. Як і в будь-якій справі, в розробці програмного забезпечення для довготривалого стійкого розвитку важливо, насамперед, розуміння основних, базових принципів, а не знання конкретного інструментарію, який забезпечує лише поверхневий рівень навичок в короткостроковій перспективі. Всі перераховані вище базові принципи прості за своєю природою і основна маса кодерів про них, як мінімум, знає, проте далеко не кожен реально їх розуміє і використовує у своїй практиці.

Хай буде з тобою це велике знання, юний падаван!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.