Чи корисно використовувати функції просто для централізації загального коду?


20

Я дуже часто переживаю цю проблему. Наприклад, я зараз записую функцію читання та функцію запису, і вони обидва перевіряють, чи bufє покажчик NULL і чи modeзмінна знаходиться в певних межах.

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

Отже, чи слід використовувати функцію для чогось подібного? Які плюси і мінуси?


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

1
Чудове запитання. Я так багато разів дивився те саме.
rdasxy

Відповіді:


31

Це чудове використання функцій.

Це буде досить анемічна функція (не робить багато) ...

Це добре. Функції повинні виконувати лише одне.

досить локалізовано ...

Мовою ОО, зробіть це приватним.

(так не загального призначення)

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

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

Придумайте хороше ім'я для цього, і воно прекрасно стоїть самостійно. "ValidateFileParameters" або щось подібне. Тепер добре стоїть сама по собі.


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

2
@deadalnix: Добре. Коли я писав, я зрозумів, що мої моменти - це грубе надмірне спрощення. Написання та легше налагодження, безумовно, є перевагою розділення речей на функції (як і можливість тестування окремих функцій).
Відновити Крамію Моніку

11

Це так тотально повинно бути функцією.

if (isBufferValid(buffer)) {
    // ...
}

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

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

Дозвольте поставити вам краще питання. Як це не зробити гарною практикою?

Зробіть правильно. :)


4
Якщо isBufferValid - це просто, return buffer != null;я думаю, вам там шкодить читабельність.
пдр

5
@pdr: У такому простому випадку це шкодить читабельності лише в тому випадку, якщо ви налаштовані на розум управління, і справді, дійсно, дійсно хочете знати, як перевіряє код, чи буфер дійсний. Тож це суб’єктивно в тих простих випадках.
Spoike

4
@pdr Я не знаю, як це шкодить читабельності за будь-яким стандартом. Ви звільняєте інших розробників від турботи про те, як ви щось робите, і орієнтуєтесь на те, що ви робите. isBufferValidце, безумовно, читабельніше (в моїй книзі), ніж buffer != nullтому, що вона чіткіше передає мету. І знову, не кажучи вже про це, врятує вас і від дублювання. Що ще вам потрібно?
Ям Маркович

5

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

Звичайно, є обмеження, продиктовані здоровим глуздом. Ви не хочете, щоб WriteToConsole(text)метод, наприклад, тілом був просто Console.WriteLine(text), наприклад. Але помилка в стороні читабельності - хороша практика.


2

Як правило, корисно використовувати функції для видалення дублювання коду.

Однак це можна зайняти занадто далеко. Це заклик судження.

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

if (buf==null) throw new BufferException("Null read buffer!");

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

checkForNullBuffer(buf, "Null read buffer!");

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


Можливо, ваш приклад говорить більше про відсутність підтримки контракту мовою, ніж про реальну необхідність дублювання. Але хороші моменти все одно.
deadalnix

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

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

Ну це лише приклад, але, мабуть, ви будете мати різні повідомлення на кожному сайті виклику функції - наприклад, "Буфер зчитування з нуля" та "Буфер запису нуля" наприклад. Якщо ви не хочете однакових повідомлень журналу / помилок для кожної ситуації, ви не можете їх повторити .......
mikera

-1 Передумови.checkNotNull - це хороша практика, а не погана. Не потрібно включати рядкове повідомлення. google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234

2

Централізація коду зазвичай завжди є хорошою ідеєю. Ми повинні максимально повторно використовувати код.

Однак важливо зазначити, як це зробити. Наприклад, коли у вас є код, який робить compute_prime_number () або check_if_packet_is_bad (), це добре. Цілком ймовірно, що алгоритм функціональності сам розвиватиметься, що отримає користь.

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

Ось кілька питань, які ви повинні задати, перш ніж задавати

  1. Чи функція, яку ви створюєте, має власне притаманне значення чи це лише купа рядків?

  2. Який ще контекст потребуватиме використання тих же функцій? Чи ймовірно, що вам може знадобитися трохи узагальнити API перед його використанням?

  3. Яким буде очікування (різних частин) додатків, коли ви кинете винятки?

  4. Які сценарії бачать, що функції будуть розвиватися?

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

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


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

1

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

Що таке копіювання коду?

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

Розглянемо приклад нижче:

if(user.getPrileges().contains("admin")) {
    // Do something
}

стає

if(user.isAdmin()) {
    // Do something
}

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


1
Приклад Btw ілюструє Закон Деметера.
MaR

1

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


0

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

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

Що робити, якщо вам доведеться перевірити ще щось?

Ви збираєтесь знайти всі місця, які вам доведеться додати додатковий чек або просто змінити функцію?


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

1
@EpsilonVector моє правило полягає в тому, що якщо мені доведеться його змінити один раз, то я можу також рефактор. Отже, у вашому випадку я б залишив його, і якби мені довелося його змінити, він став би функцією.
Танос Папатанасіу

0

Це майже завжди добре, якщо виконуються наступні умови:

  • покращує читабельність
  • використовується в обмеженому обсязі

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

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