Чи прийнятно копіювати та вставляти довгий, але простий код, а не загортати їх у клас чи функцію?


29

Припустимо, у мене є сегмент коду для підключення до Інтернету та показ результатів з'єднання, як це:

HttpRequest* httpRequest=new HttpRequest();
httpRequest->setUrl("(some domain .com)");
httpRequest->setRequestType(HttpRequest::Type::POST);
httpRequest->setRequestData("(something like name=?&age=30&...)");
httpRequest->setResponseCallback([=](HttpClient* client, HttpResponse* response){
    string responseString=response->getResponseDataString();
        if(response->getErrorCode()!=200){
            if(response->getErrorCode()==404){
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setFontColor(255,255,255);
                alert->setPosition(Screen.MIDDLE);
                alert->show("Connection Error","Not Found");
            }else if((some other different cases)){
                (some other alert)
            }else
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setPosition(Screen.MIDDLE);
                alert->setFontColor(255,255,255);
                alert->show("Connection Error","unknown error");
            }
        }else{
            (other handle methods depend on different URL)
        }
}

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

Моє запитання: чи допустимо копіювати та вставляти довгий, але простий код, а не загортати їх у функцію для зменшення залежності коду?


89
Уявіть, у вас є помилка в цьому коді, наприклад, не звільнення виділених вами об'єктів. (Чи звільняє ваш фреймворк Alertоб'єкти?) Тепер уявіть, що вам потрібно знайти кожен скопійований екземпляр цього коду, щоб виправити помилку. А тепер уявіть, що не вам належить це зробити, а божевільний вбивця сокири, який знає, що ви були тим, хто створив усі ці копії.
Себастьян Редл

8
І BTW, змішування мереж та відображення помилок в одному місці - це вже велика ні-ні, IMHO.
sleske

11
Ні ніколи. Цілком неприйнятно. Якби ти був у моєму проекті, ти б більше не був у моєму проекті, і ти був би на навчальній програмі або PIP.
nhgrif

10
А поруч іде керівник, який говорить: "Це поле сповіщення посеред мого екрану - це абсолютний терор. Я спостерігаю за котячими перемичками, і спливаюче вікно блокує мій погляд кожного разу, коли він з'являється. Будь ласка, переміщуйте його вгорі праворуч . " 3 тижні пізніше "Що, до біса, ти робив ?! Я більше не можу закривати котячі джипи, тому що ВСЕ спливаюче вікно закриває X у верхньому правому куті, виправте це".
MonkeyZeus

11
Я думаю, що всі тут, здається, вважають це поганою ідеєю. Але для розгортання питання, чому б ви НЕ ставили цей код в окремий клас чи функцію?
Карл Ґерцен

Відповіді:


87

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

Вам також потрібно врахувати ясність. Швидше за все, перегляд 30 рядків коду не буде таким простим для розуміння, як один виклик функції "connectToInternet". Скільки часу буде втрачено, намагаючись зрозуміти код, коли потрібно додати нову функціональність?

Є певні рідкісні випадки, коли дублювання не є проблемою. Наприклад, якщо ви проводите експеримент, і код буде викинутий наприкінці дня. Але в цілому вартість дублювання перевищує малу економію часу, коли не потрібно витягувати код на окрему функцію .

Дивіться також /software//a/103235/63172


19
... і хто знає, чи справді ці 30 рядків такі самі, як ті інші, які ви переглядали раніше, чи хтось з будь-якої причини перейшов на інший порт або IP-адресу в його / її копії. Неявне припущення про те, що " будь-яка купа десь 30 рядків, що починаються з" HttpRequestє однаковими ", легко помилитися.
null

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

54

Ні.

Насправді навіть ваш «простий» код слід розділити на менші частини. Принаймні два.

Один, щоб встановити з'єднання та обробити нормальну реакцію 200. Наприклад, що робити, якщо ви переходите від POST до PUT в деяких випадках? Що робити, якщо ви створюєте мільйони цих з'єднань і вам потрібно кілька багатопотокових або об'єднань з'єднань? Наявність коду в одному місці з аргументом для методу полегшить це

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



Ви також можете процитувати SRP: наявність блоку коду, який має лише одну мету, робить його набагато легшим для розуміння та підтримки ..
Roland Tepp

Це один випадок, коли DRY та SRP фактично вирівнюються. Іноді вони цього не роблять.
user949300

18

чи прийнятно копіювати та вставляти ...

Ні.

Для мене вирішальним аргументом є такий:

... зазвичай використовується ...

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

це прямо і не складно ...

І так має бути все легше перетворюватися на функцію.

... є в комплекті налаштування, такі як URL, розмір шрифту ...

І що люблять змінювати користувачі? Шрифти, розміри шрифту, кольори тощо тощо.

Тепер; у скільки місць вам доведеться змінити той самий фрагмент коду, щоб знову отримати їх однакового кольору / шрифту / розміру? (Рекомендована відповідь: лише одна ).

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

Варіація => параметри функцій.


"І що користувачі люблять змінювати? Шрифти, розміри шрифту, кольори тощо", про це так і йдеться. Ви дійсно хочете змінити це в десятках локацій?
Dan

8

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

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

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


6

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

Наприклад:

def add(a, b)
    return a + b
end

дурний, просто роби + б.

Але коли ви отримуєте лише невеликий, крихітний складніше, зазвичай ви переходите лінію.

foo.a + foo.b

повинні стати

foo.total
def foo
    ...
    def total
        return self.a + self.b
    end
end

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

Зрештою, щоб зробити веб-запит, я хочу, щоб дзвінок виглядав приблизно так:

Web.Post(URI, Params, ResponseHandler);

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

Це також зберігає код DRY і допомагає з SRP .


0

У проекті будь-якого розміру / складності я хочу мати змогу знайти код, коли мені це потрібно для наступних цілей:

  1. Виправте це, коли він зламався
  2. Змініть функціональність
  3. Повторне використання.

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

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


0

Клас та / або функція краще, принаймні, на мою думку. Один раз, це робить файл меншим, що є дуже великим виграшем, якщо ви працюєте з веб-додатками або програмами для пристроїв з невеликим обсягом пам’яті (IoT, старі телефони та ін.)

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

Я написав цілий інтерпретатор SQL, щоб міг краще перейти з MySQL на MySQLi в PHP, тому що мені просто потрібно змінити інтерпретатора, і все працює, хоча це трохи крайній приклад.


-1

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

  1. Необхідно буде змінити обидва способи використання коду однаково.

  2. Потрібно буде змінити хоча б одне використання коду, щоб вони відрізнялися.

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

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

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