Ілюзорне копіювання коду


56

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

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

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

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

Проблеми:

  • відмінності настільки хвилинні, що код у всіх видах виглядає майже однаково ,
  • Є так багато розбіжностей, що, дивлячись на деталі, кодувати не дуже схоже

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


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

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

Пам'ятайте, що ви можете повторно використовувати код, який робить те саме. Якщо ваш додаток робить різні дії на різних екранах, йому знадобляться різні зворотні дзвінки. Ніяких ifs, ands або buts про це.
corsiKa

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

Можливо, вам буде цікаво почитати про програмування, орієнтоване на аспекти.
Бен Джексон

Відповіді:


53

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

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

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

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

Недостатня увага до дизайну - це суттєвий камінь, але також остерігайтеся надмірного дизайну.


Я думаю, що ваш коментар про "нещасні тенденції" та сліпо слідування вказівкам на місці.
Майл

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

2
@Mael Звичайно, ми могли б вважати це просто нещасливим зворотом фрази! : D Однак я думаю, що ми повинні бути такими ж суворими з собою, як і ми з іншими, коли пишемо код (я вважаю себе іншим, коли читаю власний код через 2 тижні після його написання).
Виявлено

2
@ user61852 тоді вам дуже не сподобається The Codeless Code .
RubberDuck

1
@ User61852, ха - ха , - але що , якщо це дійсно все залежить (від інформації , не враховуючи в цьому питанні)? Кілька речей менш корисні, ніж надмірна впевненість.

43

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

Частина знань може бути фактом ("максимально дозволене відхилення від запланованого значення становить 0,1%") або це може бути деяким аспектом вашого процесу ("ця черга ніколи не містить більше трьох елементів"). Це фактично будь-який єдиний фрагмент інформації, закодований у вихідному коді.

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


12
Це! У центрі уваги DRY - уникати повторних змін .
Матьє М.

Це дуже корисно.

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

3
@ gnasher729 Так, в цьому справа. Якщо у двох фрагментах коду є дублювання знань , то ви очікуєте, що коли потрібно змінити інший, то інший також буде потрібно змінити, що призведе до описаної вами проблеми. Якщо вони мають випадкове дублювання , тоді, коли потрібно змінити, інший, можливо, повинен залишитися таким самим. У тому випадку, якщо ви витягли звичайний метод (чи будь-який інший), тепер у вас є інша проблема
Бен Аронсон

1
Також істотне дублювання та випадкове дублювання , див . Випадкове Doppelgänger в Ruby, і я ПОРУШИЛО-редагував свій код і зараз важко працювати. Що трапилось? . Випадкові дублікати також трапляються з обох сторін межі контексту . Короткий зміст: злиття дублікатів лише тоді, коли є сенс для клієнтів зміни цих залежностей одночасно .
Ерік

27

Чи планували ви використовувати стратегію ? У вас був би один клас View, який містить загальний код і підпрограми, викликані кількома представленнями. Діти класу Перегляд міститимуть код, специфічний для цих примірників. Всі вони використовуватимуть загальний інтерфейс, створений для перегляду, і, таким чином, відмінності будуть інкапсульовані та узгоджені.


5
Ні, я не вважав це. Дякую за пропозицію. З короткого прочитання про шаблон Стратегії здається, що я шукаю. Я обов'язково розвідаю далі.
Mael

3
є шаблон шаблону методу . Ви також можете це врахувати
Шакіл

5

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

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

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


2

Одне питання може полягати в тому, що ви надаєте інтерфейс (теоретичний інтерфейс, а не мовна функція) лише для одного рівня функціональності:

A(a,b,c) //a,b,c are your callbacks or other dependencies

Замість декількох рівнів залежно від того, який контроль потрібно:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

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

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

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

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

Використовуйте один об'єкт, де вам потрібно мало контролю.

Використовуйте функціонал найнижчого рівня, коли має статися якесь дивацтво.

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


1

Вже є інші корисні відповіді. Я додам свою.

Дублювання погано, тому що

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

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

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

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