JavaRush /Курси /Java Syntax Zero /Правильна робота програми

Правильна робота програми

Java Syntax Zero
Рівень 15 , Лекція 1
Відкрита

1. Завдання програміста

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

Від новачка часто можна почути «Програма працює, що вам ще потрібно?» А от досвідчений програміст знає, що «працює правильно» — це лише одна з вимог до програми, причому навіть не найголовніша!

Прочитність коду

Найголовніше — щоб код програми могли зрозуміти інші програмісти. Це важливіше, ніж програма, яка правильно працює. Набагато важливіше.

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

Просто візьміть будь-яку скомпільовану програму, наприклад Notepad (блокнот), і змініть у ній колір фону на червоний. Працююча програма у вас є, зрозумілого коду програми — немає: у таку програму неможливо вносити зміни.

Хрестоматійний приклад — коли розробники Microsoft прибрали з Windows гру Pinball, оскільки не змогли перенести її на 64-розрядну архітектуру. Причому в них навіть були вихідні коди цієї гри. Просто вони не могли зрозуміти, як працює написаний код.

Урахування всіх сценаріїв використання

Друга за важливістю вимога до програми — це врахування всіх сценаріїв її роботи. Часто-густо все виявляється трохи складнішим, ніж здається.

Як програміст-початківець бачить надсилання SMS у своїй програмі:

Як це бачить-програміст-професіонал:

А «правильна робота» — це зазвичай лише один із безлічі сценаріїв. І саме тому багато новачків скаржаться на валідатор задач у JavaRush: один сценарій із 10 працює, і програміст-новачок думає, що цього досить.


2. Позаштатні ситуації

Позаштатні ситуації

Під час роботи будь-якої програми можуть виникати позаштатні ситуації.

Наприклад, ви вирішили зберегти файл, а на диску немає місця. Або програма намагається записати дані в пам'ять, а пам'яті замало. Або ви завантажуєте картинку з інтернету, а в процесі завантаження перервався зв'язок.

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

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

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

  • Програма закрилася
  • Програма продовжила працювати, але файл не зберегла.

Новоспеченому програмісту може здатися, що другий варіант кращий, адже програма працює. Але насправді це не так.

Уявіть, що ви 3 години набирали текст документа у Word, хоча ще на другій хвилині роботи стало зрозуміло, що програма не може зберегти документ на диску. Що краще — втратити дві хвилини роботи чи три години?

Якщо програма не може зробити те, що потрібно, краще нехай вона закриється, ніж продовжує вдавати, що все гаразд. Найкраще, що може зробити програма в разі збою, який вона не здатна усунути самостійно, — одразу повідомити користувача про наявність проблеми.


3. Передісторія виникнення винятків

Позаштатні ситуації трапляються не тільки з програмами, а й усередині програми — з методами. Наприклад:

  • Метод намагається записати файл на диск, а місця немає.
  • Метод намагається викликати функцію для змінної, а змінна == null.
  • У методі виникло ділення на 0.

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

Якщо ми намагаємося зберегти файл на диску і такий файл уже існує, можна просто попросити користувача підтвердити перезапис файлу. Якщо на диску немає місця, можна вивести повідомлення для користувача й запропонувати вибрати інший диск. А якщо закінчилася пам'ять — програма аварійно завершиться.

Колись давно програмісти думали над цим питанням і придумали таке рішення: усі методи/функції мають повертати код помилки як результат своєї роботи. Якщо функція відпрацювала відмінно, вона повертала 0, якщо ні — повертала код помилки (не нуль).

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

Код без обробки помилок Код з обробкою помилок
File file = new File("ca:\\note.txt");
file.writeLine("Текст");
file.close();
File file = new File("ca:\\note.txt");
int status = file.writeLine("Текст");
if (status == 1)
{
   ...
}
else if (status == 2)
{
   ...
}
status = file.close();
if (status == 3)
{
   ...
}

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

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

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


Коментарі (21)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Ігор Рівень 29
19 жовтня 2024
зразу згадується Дія, яка просить крутити головою в різні сторони, кліпати очима, а потім каже, що сталася помилка, або не вдалось розпізнати😡 побажав би розробникам того функціоналу "доброго здоров'я"))
Olexandr Рівень 47
16 січня 2024
Хто пояснить, для чого в задачі перезаписали метод equals? Чи він якось використовується валідатором? Я багато часу витратив, поки намагався його якось притулити при провірці...
Anonymous #3315996 Рівень 3
20 січня 2024
я його нікуди і не тулив, а перезаписали щоби було над чим подумать
Ва Дим Рівень 28
25 квітня 2024
взагалі не думав за нього .Йшов чисто по пунктам
IronMan57 Рівень 28
29 грудня 2024
Хоча пройшло вже багато часу, залишу коментар на випадок, якщо в майбутньому в когось виникне аналогічне питання. Конкретно в цій задачі методи equals та hashCode для екземплярів класу User не використовується, тому при бажанні можна на них не звертати увагу. Інша річ, що за великим рахунком майже у кожному самописному класі так само, як потрібно створювати гетери та сетери, так само потрібно перевизначати методи equals та hashCode. Це необхідно тому, що у всіх самописних класів за замовчуванням метод equals наслідується від класу Object, де цей метод повертає true, тільки у випадку однакових посилань на порівнюванні об'єкти, що дуже рідко відповідає реальним потребам порівняння таких об'єктів. Тут є стаття на цю тему - Методи equals & hashCode: практика використання.
Богдан Рівень 37
19 серпня 2023
цікаво👍
Abver Рівень 4 Expert
16 січня 2023
Завдання класне, зробив все вірно, валідацію пройшло, але так і не зміг зробити щоб не виводило помилку при заповненні віку текстом замість цифр.
Костянтин Рівень 58 Expert
20 червня 2023
Можна спробувати ось так - String ageInput = scanner.nextLine(); if (ageInput.matches(".*\\D.*")){ System.out.println("Age must be only digits"); return; } int age = Integer.parseInt(ageInput);
Yaroslav Tkachyk Рівень 23 Expert
11 січня 2023
Класне завдання. Тут більш важливо прочитати написаний код і зрозуміти як програма його реалізовує. Все інше не складно, враховуючи який останній оператор був вивчений :)
Taras Woytowitch Рівень 16
14 січня 2023
о, точно, а я через if-else все робив) а дійсно можна через switch
Jaroslav Рівень 48
20 грудня 2024
Як через switch вирізнити решту варіантів окрім нуля?
IronMan57 Рівень 28
29 грудня 2024
В операторі switch є ключове слово default, після якого можна написати команди, які будуть виповнюватись у випадку, якщо не зустрівся ні один із вище вказаних варіантів case. У цьому випадку case 0 також має окремо оброблятись, так само як -1, чи інші помилки.
Jaroslav Рівень 48
31 грудня 2024
Тобто робити додаткову перевірку на 0, в якій нічого не виводити, а в default друкувати UNKNOWN_ERROR?
IronMan57 Рівень 28
7 січня 2025
Саме так.
Anonymous #696530 Рівень 19
3 жовтня 2022
Тут не завадило б зробити кілька простіших завдань, щоб зрозуміти тему краще..
Roma Chernesh Рівень 16
16 лютого 2023
Ото хоч би одне - було б теж непогано)
Niko Рівень 36
27 липня 2022
System.out.println("Користувача '"+user.getName()+ "' не знайдено."); Программа не приймає якщо не встановити одинарні лапки ' Ім'я '
Yaroslav Tkachyk Рівень 23 Expert
11 січня 2023
Так в умові це й прописано: "Користувача '<<>ім'я користувача<>>' не знайдено." Через printf (String, String) простіше )
Viacheslav B. Рівень 1
18 березня 2024
у мене сприйняло -
System.out.println("Користувача \'"+user.getName()+"\' не знайдено.");
Viacheslav B. Рівень 1
18 березня 2024
тільки потім побачив що була константа

NOT_FOUND
13 березня 2025
так: System.out.printf(NOT_FOUND, user.getName()); або так: System.out.println(NOT_FOUND.formatted(user.getName()));