Бульне повернення set.add () у, якщо умовне?


15

Оператор add класу set повертає логічне значення, що є істинним, якщо елемент (який потрібно додати) ще не було, а false - інакше. Пишеться

if (set.add(entry)) {
    //do some more stuff
}

вважається хорошим стилем з точки зору написання чистого коду? Мені цікаво, оскільки ти робиш дві речі одночасно. 1) додавання елемента та 2) перевірка існування елемента.


6
Ви говорите про стандарт java.util.Set, який повертається істинно, addколи елемента ще не було, правда?
user2357112 підтримує Моніку

1
Зазвичай я вважаю протилежний тест:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
njzk2

Чи щось не так у тому, щоб робити дві речі одночасно?
користувач207421

@ user2357112 так, це правильно.
Андреас Браун

Відповіді:


19

Так.

Звичайний момент повернення операції булевого значення полягає в тому, що ви можете використовувати його для прийняття рішень, тобто в межах ifконструкції. Єдиний інший спосіб, як ви могли реалізувати цей потенціал, - це зберегти повернене значення у змінній, а потім негайно повторно використати його в if, що просто нерозумно і, звичайно, ні в якому разі не бажано писати if(operation()) { ... }. Тому просто продовжуйте робити це, ми не будемо судити вас (за це).


Дякую за вашу відповідь. Як @Niels vanReijmersdal у своїй відповіді вказує, що це порушує поділ команди та запитів, чи не так? Ви не вважаєте, що це має значення?
Андреас Браун

4
@AndreasBraun особисто я думаю, що розділення запитів команд німе. Часто логічно для одного методу виконувати дію і повертати значення, пов'язане з цією дією. Забезпечення штучного розмежування між ними призводить до зайвого багатослівного коду.

1
@ dan1111: дійсно. Крім того, "розділення команд і запитів" призводить до "перевірити, тоді діяти" та подібних анти-шаблонів, які можуть порушуватися в багатопотокових контекстах. Досить дивно рекламувати такий поділ, коли решта світу намагаються одночасно будувати атомні плавкі операції для надійних та ефективних рішень SMP…
Holger

2
@ Йорг W Міттаг: сторінка 759 що? Оскільки атомна операція по суті є незахищеною від анти-шаблону "перевірити, тоді діяти", не виглядає вигідним розділити її лише для того, щоб прочитати "всю главу", що б дізнатися, як зробити безпечну нитку конструкції знову. Оскільки ви не назвали жодних фактичних аргументів, у мене немає «конкретних заперечень проти цих аргументів».
Холгер

1
@ Йорг W Міттаг: Ну, я говорю про відповідь на цій сторінці, перешкоджаючи використанню Set.addяк однієї операції, не називаючи альтернативу. Якщо призначена операція полягає в додаванні одного елемента та з'ясуванні, чи він був доданий, немає необхідності в більш потужній альтернативі, яка все ще ускладнює операцію без користі. Я сподіваюся, що вам відомо, що Set.addце вбудований метод JRE, який можна використовувати без попереднього вивчення книги на 700 сторінок.
Холгер

12

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

Я віддаю перевагу наступному:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

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



9
Пошук методу є меншим тягарем, ніж намагання зрозуміти вихідний намір розробника щодо надмірної змінної, оголошеної за межами області, де вона використовується. У всіх сучасних Java IDE розробник може навести курсор миші на метод і негайно отримати доступ до його документації. Хороші розробники роблять це постійно, працюючи з будь-яким незнайомим API.
Кевін Крумвіде,

9
Я другий @Holger. Якщо ви не знаєте Set.add, ви недосвідчені, і вам слід шукати ці методи, щоб навчитися їх і стати більш досвідченими.
Відновіть Моніку

7
notAlreadyPresentне найкраща формулювання. Я б використовував addedі очікую, що читач дізнається, чому значення не буде додано до набору.
njzk2

3
@JustinTime: Використання коментарів для опису того, що робить код, широко вважається поганою практикою. Ваш код повинен бути самоописаним. В огляді коду я б визначив ваш приклад як неприйнятний.
BlueRaja - Danny Pflughoeft

8

Якщо істина означає успіх, то це добре, чіткий код.

Існує поширена конвенція про те, що функція або метод повертає істину (або щось, що оцінює справжнє) на успіх. Поки ваш код випливає, я вважаю, що ставити метод умовно.

Такий код, на мій погляд, зайвий:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

Таке відчуття, що ти повторюєшся.

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


Що означає успіх у контексті додавання предмета до набору? Не кидати виняток означає успіх; true vs. false означає щось більш конкретне в цьому контексті.
Відновіть Моніку

@ Solomonoff'sSecret У цьому випадку виняток означає, що набір відмовився додавати елемент, falseозначає, що елемент не був доданий, оскільки він уже містився, а trueзначить, елемент ще не містився. Оскільки add()додає елемент до набору, якщо набір його ще не містив, trueтому означає, що елемент був успішно доданий до набору.
Час Джастіна - Поновіть Моніку

@JustinTime Ви додаєте власні судження про цінність до двох справ. Інша інтерпретація полягає в тому, що успіх полягає в тому, що елемент знаходиться в наборі, коли метод повертається. Якщо предмет уже був у наборі, addуспішно не вдається нічого робити. Якщо елемент ще не був у наборі, addвдалося додати його до набору. Яке трактування правильне - умовне. Менш довільним визначенням успіху є те, що є з мови: метод успішний, якщо він повертається нормально.
Відновіть Моніку

1
Найменш довільне визначення успіху полягає в тому, що метод успішно виконав завдання, яке його поставив перед собою. Якщо у мене є метод firstLessThanSecond(int l, int r), і він повертається, trueякщо l > rабо falseякщо l <= r, то цей метод не є успішним, навіть якщо він повертається нормально.
Час Джастіна - Поновіть Моніку

1
@JustinTime add- сувора абревіатура контракту. Документація починається з "Додає вказаний елемент до цього набору, якщо його вже немає", який задовольняє, чи був елемент вже в наборі. Ви можете інтерпретувати повернене значення як завгодно, але в кінцевому підсумку це лише ваша інтерпретація. І у вашому другому прикладі метод оцінює, чи відповідає умова справжньою. Якщо умова помилкова, метод, безумовно, не пройшов, оскільки він успішно оцінив умовне.
Відновіть Моніку

2

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

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

Якщо комусь доведеться подовжувати умову, додаючи and/ orумову до умови if, вони можуть .add()в певних випадках не викликати через оцінку короткого замикання. Якщо конкретно не передбачається коротке замикання, це може закінчитися помилкою.


Якщо я напишу, if (set.contains(entry)){set.add(entry); //do more stuff}це також усувається компілятором?
Андреас Браун

1
@AndreasBraun: Я не думаю, що так; компілятор не має поняття про смисловий зв’язок між containsі add. Крім того, це робить подвійну роботу пошуку entryв наборі; це може грати роль для дуже великих наборів і дуже легких петель.
9000

1
Отже, я вважаю, ви б краще не писали, if (set.contains(entry)){set.add(entry); //do more stuff}а замість цього хотіли, наприклад, відповідь Карла Білефельдта?
Андреас Браун

@AndreasBraun: точно (відповів на цю відповідь).
9000

1
хороший момент. Мутації в ifs складні, оскільки у майбутнього розробника можуть виникнути інші умови з коротким замиканням.
njzk2

0

Здається, ваш код порушує розділення запитів команд . Про це йдеться у книзі « Чистий код» та відео « Структура функцій» . Отже, з точки зору чистого коду, я думаю, що це не вважається хорошим стилем.

«Чистий код простий і прямий. Чистий код читається як добре написана проза. Чистий код ніколи не затьмарює наміри дизайнера, а скоріше наповнений чіткими абстракціями та прямими лініями управління.

- автор "Об'єктно-орієнтованого аналізу та дизайну з додатками" Грейді Буч "

Для мене наміри вашого коду незрозумілі. Чи виконано те, якщо обидва виконані, коли запис додається успішним, або також коли він вже є? Що add()повертає? предмет? Код помилки?


2
Що ж, так, я теж думав. Але я також думаю, що у @Kilian Foth є сенс. Якби я хотів розділити запит і команду, я повинен був написати, if (set.contains(entry)){set.add(entry); //do more stuff}що здається дурним. Що ти береш за це?
Андреас Браун

Я не знаю, домен і контекст цього коду дозволяють запропонувати гарне ім'я, але я б зафіксував це у функції з чітким наміром. Наприклад: doesEntryExistOrCreateEntry (запис), це може містити вашу логіку містить () + add () і повертає булеву. Аналогічне запитання тут: softwareengineering.stackexchange.com/questions/149708/…, де обговорюється ця практика поза чистим кодом.
Niels van Reijmersdal

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

1
"Що повертає додаток ()?" Я очікую, що будь-який розробник Java, який робить перегляд коду, знає Collectionapi.
njzk2

3
Погоджено з @ dan1111. Це особливо німе, оскільки саме такі речі створюють родючий грунт для перегонів.
Пол Дрейпер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.