Чи варто повторно використовувати змінні?


59

Чи варто повторно використовувати змінні?

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

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

Найкращі практики навіть є проти нулю попередніх змінних (у Java є Sonar, який видає попередження при призначенні nullзмінної, що вам не потрібно робити це для виклику смітника з Java 6. Ви не завжди можете керувати які попередження вимкнено; більшість часу увімкнено за замовчуванням.)


11
З Java 6? Вам ніколи не потрібно було призначати null змінним у будь-якій версії Java. Коли змінна виходить за межі області, вона вивільняє посилання на свій об'єкт.
Кевін Панько

35
повторне використання змінних - одне з перших, що застосовують
кодо-обскуратори

7
Змінне повторне використання конфліктує з вибором гідних ідентифікаторів для змінних. У сучасних мовах насправді не існує жодних переваг для повторного використання змінних, які переважують переваги хороших ідентифікаторів.
Джорен

4
Також зауважте, що не все повторне використання - це повторне використання. Старі програмісти FORTRAN, навіть ті, які перейшли на інші мови, звичайно використовують I, J, K, L, M, N (або i, j, k, l, m, n) для всіх наших циклів DO (for- петля) лічильники. Ми звикли бачити такі речі, як SUM = SUM + A (I, K) * B (K, J), або сума + = a [i] [k] * b [k] [j] ;.
Джон Р. Стром

1
Використання змінних може бути виправданим при програмуванні мікроконтролерів з дуже обмеженими ресурсами (декілька кБайт оперативної пам’яті).
м.Алін

Відповіді:


130

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

Найкраща практика ІМО - використовувати досить короткі методи, які роблять лише одне, усуваючи всю проблему.


як щодо одиничного тестування? наприклад, мені потрібно перевірити, чи після виконання методу вдруге він працює як слід.
IAdapter

20
@IAdapter, одиничний тестовий код - це інша проблема, де стандарти можуть бути більш спокійними, ніж для виробничого коду. Тим не менш, зробити його читабельним і легким у обслуговуванні є першочерговим завданням. Ви також можете (і повинні) витягувати методи з ваших одиничних тестів, щоб вони були короткими та легкими для читання (і щоб усунути необхідність повторного використання змінних).
Péter Török

3
@IAdapter, для описаного вами сценарію я не використовував би змінну. Відповідна річ була б чимось на кшталт того, result1 = foo(); result2 = foo(); Assert.AreEqual(result1, result2);що я не бачу багато місць, де в цьому випадку вам потрібно буде повторно використовувати змінну.
JSB ձոգչ

1
@IAdapter for (int i = 0; i <2; i ++) {результат = foo (); assertEquals (очікуваний результат, результат);}
Білл К

2
+1 - це хороший запах коду, коли потрібно робити менші методи.

49

Змінне повторне використання методу є сильним ознакою того, що вам слід переробляти / розділити його.

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


19

Це залежить.

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

void my_function() {
    HRESULT errorcode;
    errorcode = ::SomeWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
    errorcode = ::AnotherWindowsApiFunction();
    if (FAILED(errorcode)) { /* handle error */ }
}

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

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

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


15

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


тож я повинен написати методи тестування на кшталт -testSomething і заявити про виклик одного методу -testSomethingTwoInvocations і стверджувати лише для другого виклику?
IAdapter

2
Так. Він допомагає точно визначити, якщо перша чи друга ітерація не вдалася. mspec може допомогти в цьому, його спадкове дерево буде дуже корисним.
Брайан Боттчер

Якщо ви використовуєте "змінну", коли ви маєте на увазі "змінну класу" або "змінну екземпляра", тоді вам потрібно виразити себе більш чітко.
gnasher729

13

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

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


4

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

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

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


17
-1, якщо ви не можете вказати на ситуацію в реальному світі, коли це насправді відбувається, і коли повторне використання змінної суттєво зміниться. Це теоретичне рішення теоретичної проблеми, і не дуже вдале в цьому. Там, де компілятор вирішив поставити змінні, викликає занепокоєння компілятор; якщо ваш компілятор генерує поганий код, і якщо він дійсно має значення, то виправте або замініть компілятор.
Калеб

3
@Caleb - у вас не завжди є доступ до вихідного коду компілятора для платформи, на якій ви розробляєте, особливо для вбудованих платформ. У своїй відповіді я також DID зауважую, що я не знаю жодних поточних компіляторів основних потоків (або насправді інтерпретаторів / VM), які не виконують цей аналіз живої активності у всіх випадках, які я переглядав. Тим НЕ менше, я вже працював на власних вбудованих систем з налаштованим компіляторів , які були дуже голі кістки в минулому (10 років тому або близько того ). Система була дуже обмежена у можливостях, і такий був і компілятор, тому така ситуація там сталася.
Joris Timmermans

2
+1 Я робив саме таке повторне використання в минулому проекті (високооптимізований медіа-код, написаний в ANSI C). Наскільки я пам’ятаю, різницю було легко помітно в зборах і вимірювалося в орієнтирах. Ніколи не робив і сподіваюся, що ніколи не доведеться робити таке повторне використання на Java.
гнат

@Caleb виправлення або заміна компілятора може не бути варіантом з кількох причин, і вам просто доведеться робити те, що у вас є.

@ ThorbjørnRavnAndersen Чи можливо, що хтось може бути змушений використовувати паршивий компілятор, і, крім того, ця ефективність може бути настільки критичною, що вам потрібно вдруге вгадати компілятор і маніпулювати кодом, щоб повторно використовувати реєстр? Так. Це ймовірно ? Ні. MadKeithV погоджується, що він не може надати реальний приклад світу. Це найкраща практика ? Це гарний стиль ? Це показник якості коду ? Точно ні. Це корисна відповідь на запитання як написане? Вважаю MadKeithV, але я не думаю, що це так.
Калеб

2

Я б сказав «НІ».

Подумайте над цим сценарієм: ваша програма вийшла з ладу, і вам потрібно розробити те, що сталося, оглянувши основний дамп ... чи бачите ви різницю? ;-)


Якщо ваш основний дамп створений з оптимізованої збірки, він може мати змінні в будь-якому випадку. Тож це менш корисно, ніж це може бути.
Вінстон Еверт

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

2

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


0

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

scrote = int (scrote)

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


1
Поставити такий ряд, як "scrote = int (scrote)" посеред довгої партії коду, - це чудовий спосіб звести з розуму програмістів з технічного обслуговування.
поштовх

Проблема швидше стосується згадуваного вами "довгої партії коду".
Paddy3118

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

0

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

Ваш налагоджувач також може зробити болюче налагодження функції з багатьма змінними. Якщо функцію з 20 змінними важче налагодити, ніж одну з 16 змінними, ви можете розглянути можливість заміни 5 змінних на одну. ("Можна вважати" - це не те саме, що "слід завжди").

Добре, що одна змінна використовується в декількох місцях до тих пір, поки змінна завжди має одне і те ж призначення. Наприклад, якщо десять викликів функцій повертають код помилки, який обробляється негайно для кожного виклику, використовуйте одну змінну, а не 10. Але не використовуйте одну і ту ж змінну для абсолютно різних речей. Як, наприклад, "ім'я" для імені клієнта, і через 10 рядків, використовуючи ту саму змінну для назви компанії, це погано, і ви зможете потрапити в проблеми. Гірше, використовуючи "customerName" для імені клієнта, а 10 рядків пізніше використовуючи ту ж змінну "customerName" для імені компанії.

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


0

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

Ваш налагоджувач також може зробити болюче налагодження функції з багатьма змінними. Якщо функцію з 20 змінними важче налагодити, ніж одну з 16 змінними, ви можете розглянути можливість заміни 5 змінних на одну. ("Можна вважати" - це не те саме, що "слід завжди").

Добре, що одна змінна використовується в декількох місцях до тих пір, поки змінна завжди має одне і те ж призначення. Наприклад, якщо десять функціональних викликів повертають код помилки, який обробляється негайно для кожного виклику, використовуйте одну змінну, а не 10. Існує одна проблема з цим: Зазвичай компілятор скаже вам, коли ви використовуєте неініціалізовану змінну. Але тут, якщо ви прочитаєте код помилки після виклику 6, але змінна насправді не була змінена після виклику 5, ви отримаєте марні дані без попередження компілятора. Оскільки змінна була ініціалізована, з даними, які тепер марні.

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


-2

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

Якщо ви не вдумаєтесь в опущення коду вручну або не дратуєте інших розробників.

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