JavaRush /Java блог /Random UA /Типові помилки у Java-коді.
Sdu
17 рівень

Типові помилки у Java-коді.

Стаття з групи Random UA
Цей матеріал містить найбільш типові помилки, які я бачив у Java-коді людей, які працюють зі мною. Статичний аналіз (ми використовуємо qulice ), зі зрозумілих причин, не може виявити всі подібні помилки, тому я вирішив перерахувати їх тут. Усі перелічені помилки пов'язані з об'єктно-ориентированным програмуванням загалом і Java зокрема.
Імена класів (Class Names)
Ваш клас має бути абстракцією об'єкта реального життя без « валідаторів » (« validators »), « контролерів » (« controllers »), « менеджерів » (« managers ») тощо. Якщо ім'я Вашого класу закінчується на -er - це поганий дизайн. І, звичайно, допоміжні класи, які є анти-патернами, на кшталт StringUtils , FileUtils , та IOUtils від Apache, чудові приклади жахливих дизайн-паттернів. Ніколи не додавайте суфікси або префікси, щоб розрізняти інтерфейси та класи. Наприклад, всі ці імена жахливі: IRecord , IfaceEmployee , або RecordInterface . Зазвичай, ім'я інтерфейсу це ім'я реального життя, в той час як ім'я класу повинно пояснити деталі реалізації. Якщо нічого конкретного про реалізацію сказати не можна, підійдуть імена " Default ", " Simple ", або щось подібне. Для прикладу: class SimpleUser implements User {}; class DefaultRecord implements Record {}; class Suffixed implements Name {}; class Validated implements Content {};
Імена методів (Method Names)
Методи можуть або повернути " щось " або повернути " void ". Якщо метод повертає щось, його ім'я має пояснити, що буде повернуто. Для прикладу (не використовуйте префікс " get "): boolean isValid(String name); String content(); int ageOf(File file); Якщо повертається " void ", ім'я має роз'яснити, що метод робить. Наприклад: void save(File file); void process(Work work); void append(File file, String line); Існує тільки один виняток із цього правила – тест методи JUnit . Вони описані нижче.
Імена тест методів (Test Method Names)
Імена методів у тестах JUnit мають бути побудовані як пропозиція англійської мови без прогалин. Це легше пояснити на прикладі: /** * HttpRequest can return its content in Unicode. * @throws Exception If test fails */ public void returnsItsContentInUnicode() throws Exception { } Важливо розпочинати першу пропозицію Вашого JavaDoc з імені класу, який Ви тестуєте з наступним « can ». Так, перша пропозиція завжди має бути подібною до фрази « хтось може зробити щось ». Ім'я методу стверджуватиме те саме, але без предмета тестування. Якщо я додам його на початок імені методу, то отримаю закінчену англійську пропозицію, як у наведеному вище прикладі: " HttpRequest returns its content in unicode ". Зверніть увагу, ім'я тест методу не починається з " can ". З " can " починаються тільки JavaDoc коментарі. Крім того, імена методів не повинні починатися з дієслова ( Від перекладача: мабуть, автор має на увазі наказовий спосіб дієслова ). Хороша практика при оголошенні тестового методу вказувати, що викидається виняток.
Імена змінних (Variable Names)
Уникайте складових імен змінних, типу timeOfDay , firstItem , або httpRequest . Я маю на увазі і змінні класу, і змінні методи. Ім'я змінної має бути досить довгим, щоб уникнути двозначності у своїй області видимості, але, по можливості, не надто довгим. Ім'я має бути іменником в однині або множині. Для прикладу: Іноді можуть з'являтися колізії між параметрами конструктора та полями класу, якщо конструктор зберігає вхідні дані у створений об'єкт. У цьому випадку, я рекомендую створити абревіатуру, вилучивши голосні. Приклад: У більшості випадків найкращим ім'ям змінної буде ім'я відповідного класу. Просто напишіть його з маленької літери, і все буде добре: Тим не менш, ніколи не робіть те саме для примітивних типів, на кшталт або . Ви також можете використовувати прикметники за наявності кількох змінних з різними характеристиками. Наприклад: List names; void sendThroughProxy(File file, Protocol proto); private File content; public HttpRequest request; public class Message { private String recipient; public Message(String rcpt) { this.recipient = rcpt; } } File file; User user; Branch branch; Integer number String string String contact(String left, String right);
Конструктори (Constructors)
Без винятку, має бути лише один конструктор, який зберігає дані до змінних об'єктів. Всі інші конструктори повинні викликати це з різними параметрами: public class Server { private String address; public Server(String uri) { this.address = uri; } public Server(URI uri) { this(uri.toString()); } }
Одноразові змінні (One-time Variables)
Уникайте одноразових змінних за будь-яку ціну. Під «одноразовими» я маю на увазі змінні один раз, що використовуються. Як у цьому прикладі: String name = "data.txt"; return new File(name); Змінна використовується лише один раз, і код може бути спрощений до: return new File("data.txt"); Іноді, в дуже рідкісних випадках – переважно через краще форматування – одноразові змінні можуть використовуватися. Проте намагайтеся уникати таких ситуацій.
Винятки (Exceptions).
Зрозуміло, Ви не повинні ніколи «ковтати» винятки, вони повинні прокидатися якомога вище. Винятки приватних методів повинні оброблятися зовні. Ніколи не використовуйте виняток для керування потоком. Код вказаний у прикладі невірний: int size; try { size = this.fileSize(); } catch (IOException ex) { size = 0; } Серйозно, якщо IOException скаже що «диск повний», Ви вважатимете, що розмір файлу дорівнює нулю і продовжите?
Відступи (Indentation).
Для відступів, основне правило полягає в тому, що дужка повинна або закінчувати рядок, або закриватися в цьому ж рядку (для дужки, що закриває, діє зворотне правило). У прикладі нижче код невірний, тому що перша дужка не закрита в тому ж рядку і після неї йдуть символи. У другої дужки та сама проблема, так як є символи перед нею, і немає дужки, що відкриває, в поточному рядку. final File file = new File(directory, "file.txt"); Правильні відступи мають виглядати так: StringUtils.join( Arrays.asList( "first line", "second line", StringUtils.join( Arrays.asList("a", "b") ) ), "separator" ); Друге важливе правило відступів свідчить, що Ви повинні розмістити на одній лінії якнайбільше – в межах 80 символів. Наведений вище приклад не є допустимим, оскільки він може бути ущільнений: StringUtils.join( Arrays.asList( "first line", "second line", StringUtils.join(Arrays.asList("a", "b")) ), "separator" );
Надлишкові константи (Redundant Constants).
Константи класу повинні бути використані, коли Ви хочете розділити доступ до інформації між методами класу, і ця інформація є характеристикою( ! ) Вашого класу. Не використовуйте константи як заміну рядків чи числових літералів – дуже погана практика, це призводить до забруднення коду. Константи (як і інші об'єкти ОВП) повинні мати сенс у реальному світі. Який зміст цих констант у реальному світі: class Document { private static final String D_LETTER = "D"; // bad practice private static final String EXTENSION = ".doc"; // good practice } Інша типова помилка – використовувати константи в unit-тестах, щоб уникнути дублювання рядка/числових літералів у тестових методах. Не робіть цього! Кожен тестовий метод повинен працювати зі своїм набором вхідних значень. Використовуйте нові тексти та цифри у кожному новому тестовому методі. Тести є незалежними. То чому вони повинні ділити одні й самі вхідні константи?
Зчеплення даних у тестах (Test Data Coupling).
Ось приклад зачеплення у тестовому методі: User user = new User("Jeff"); // maybe some other code here MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff")); В останньому рядку ми зчіплюємо " Jeff " з цим же рядковим літералом, зазначеним у першому рядку. Якщо декількома місяцями пізніше хтось захоче змінити значення в третьому рядку, він/вона повинен буде витратити додатковий час на пошук, де ще " Jeff " використовується в цьому методі. Щоб уникнути цього зачеплення даних, слід запровадити змінну.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ