Допустимо ли копировать и вставлять длинный, но простой код вместо того, чтобы помещать их в класс или функцию?

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 оба по умолчанию предоставляются framework), и хотя сегмент кода длинный, он простой и не сложный (он длинный только потому, что есть набор настроек, таких как url, размер шрифта ...), и сегмент кода имеет небольшие различия между классами (например: url, данные запроса, случаи с дескриптором кода ошибки, нормальный дескриптор случаи ...)

У меня вопрос: допустимо ли копировать и вставлять длинный, но простой код вместо того, чтобы заключать их в функцию, чтобы уменьшить зависимость кода?

ggrr
источник
89
Представьте, что в этом коде есть ошибка, например, не освобождение выделенных вами объектов. (Ваша инфраструктура освобождает Alertобъекты?) Теперь представьте, что вам нужно найти каждый скопированный экземпляр этого кода, чтобы исправить ошибку. А теперь представьте, что это должен делать не вы, а сумасшедший убийца топоров, который знает, что именно вы создали все эти копии в первую очередь.
Себастьян Редл
8
И кстати, смешивание сетей и отображение ошибок в одном месте - это уже большое нет-нет, ИМХО.
слеске
11
Нет никогда. Совершенно неприемлемо. Если бы вы были в моем проекте, вы бы больше не были в моем проекте, и вы были бы в программе обучения или PIP.
nhgrif
10
И вместе с этим приходит руководитель, который говорит: «Это окно с предупреждением в середине моего экрана - абсолютный ужас. Я смотрю кошачьи джифы, и всплывающее окно блокирует мой вид каждый раз, когда он появляется. Пожалуйста, переместите его в верхний правый угол». «. Спустя 3 недели «Какого черта ты сделал ?! Я больше не могу закрывать свои кошачьи jifs, потому что твое всплывающее окно закрывает X в правом верхнем углу, исправь это».
MonkeyZeus
11
Я думаю, что все здесь, кажется, думают, что это плохая идея. Но чтобы перевернуть вопрос, почему бы вам НЕ поместить этот код в отдельный класс или функцию?
Карл Гёртсен

Ответы:

87

Вам необходимо учитывать стоимость изменений. Что, если вы хотите изменить способ подключения? Насколько легко это будет? Если у вас много дублированного кода, то поиск всех мест, которые нужно изменить, может быть довольно трудоемким и подверженным ошибкам.

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

Есть определенные редкие случаи, когда дублирование не является проблемой. Например, если вы проводите эксперимент, а код будет отброшен в конце дня. Но в целом стоимость дублирования перевешивает небольшую экономию времени, когда нет необходимости извлекать код в отдельную функцию .

См. Также /software//a/103235/63172.

Вон Катон
источник
19
... и кто знает, действительно ли эти 30 строк такие же, как те, на которые вы смотрели ранее, или кто-то переключился на другой порт или IP-адрес в своей копии по какой-либо причине. Подразумеваемое предположение, что « любая группа из 30 строк, начинающихся с HttpRequestодинакового », легко ошибочна.
null
@null Отличная мысль. Однажды я работал над кодом, в котором были скопированы и вставлены соединения с базами данных, но у некоторых были тонкие различия в настройке. Я понятия не имел, были ли это важные, преднамеренные изменения или просто случайные различия
user949300
54

Нет.

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

Один, чтобы установить соединение и обработать нормальный ответ 200. Например, что если в некоторых случаях вы меняете POST на PUT? Что, если вы создаете миллионы таких соединений и нуждаетесь в многопоточности или пуле соединений? Наличие кода в одном месте с аргументом для метода сделает это намного проще

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

user949300
источник
Вы могли бы также процитировать SRP: наличие блока кода, который имеет только одну цель, делает его намного более простым для понимания и сопровождения ..
Роланд Тепп,
Это один из случаев, когда DRY и SRP фактически совпадают. Иногда они этого не делают.
user949300
18

допустимо ли копировать и вставлять ...

Нет.

Для меня решающим аргументом является следующий:

... это обычно используется ...

Если вы используете фрагмент кода в более чем одном месте, то, когда он меняется, вам нужно изменить его в более чем одном месте, или вы начинаете получать несоответствия - начинают происходить «странные вещи» (т.е. вы вводите ошибки).

это просто и не сложно ...

И так должно быть проще сделать рефакторинг в функцию.

... есть набор настроек, таких как URL, размер шрифта ...

А что любят менять пользователи? Шрифты, размеры шрифтов, цвета и т. Д. И т. Д.

В настоящее время; во скольких местах вам придется изменить этот же кусок кода, чтобы они снова получили одинаковый цвет / шрифт / размер? (Рекомендуемый ответ: только один ).

... сегмент кода имеет небольшие различия между классами (например: URL, данные запроса, случаи обработки кода ошибки, обычные случаи обработки ...)

Вариация => функциональные параметры.

Фил В.
источник
«А что любят менять пользователи? Шрифты, размеры шрифтов, цвета и т. Д.» Вы действительно хотите изменить это в десятках мест?
Дан
8

Это не имеет никакого отношения к копированию и вставке. Если вы берете код из другого места, во второй вы берете код, это ваш код и ваша ответственность, поэтому, независимо от того, копируется он или полностью написан вами, не имеет значения.

В ваших оповещениях вы принимаете некоторые дизайнерские решения. Скорее всего, аналогичные проектные решения должны быть приняты для всех предупреждений. Поэтому вполне вероятно, что у вас должен быть метод где-то "ShowAlertInAStyleSuitableForMyApplication" или, возможно, немного короче, и его следует вызывать.

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

gnasher729
источник
6

Дублирование в некоторых случаях нормально. Но не в этом. Этот метод слишком сложен. Существует нижний предел, когда дублирование проще, чем «вычленение» метода.

Например:

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

глупо, просто сделай a + b.

Но когда вы становитесь чуть-чуть сложнее, тогда вы обычно выходите за рамки.

foo.a + foo.b

должен стать

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

В твоем случае я вижу четыре «метода». Вероятно, в разных классах. Один для выполнения запроса, другой для получения ответа, другой для отображения ошибок и некоторый вид обратного вызова, который будет вызван после возврата ответа для обработки ответа. Лично я, скорее всего, добавил бы «обертку», чтобы облегчить вызовы.

В конце, чтобы сделать веб-запрос, я бы хотел, чтобы звонок выглядел примерно так:

Web.Post(URI, Params, ResponseHandler);

Эта строка - то, что у меня было бы по всему моему коду. Затем, когда мне нужно было внести изменения в «как я получаю материал», я мог сделать это быстро, с гораздо меньшими усилиями.

Это также сохраняет код СУХОЙ и помогает с SRP .

coteyr
источник
0

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

  1. Исправить, когда он сломался
  2. Изменить функциональность
  3. Повторно используйте это.

Разве не было бы замечательно присоединиться к незавершенному проекту или продолжить работу над проектом в течение нескольких лет, и когда новый запрос «подключиться к Интернету и показать результаты подключения» был в некоторой степени легко найти из-за наличия хороший дизайн вместо того, чтобы полагаться на поиск по всему коду для httprequest? В любом случае, с Google, наверное, легче найти.

Не волнуйтесь, я новичок, и я проведу рефакторинг этого блока кода, потому что я очень расстроен из-за того, что присоединился к этой бестолковой команде с ужасной базой кода, или сейчас я нахожусь под большим давлением, как и остальные Вы и просто скопируете и вставите его. По крайней мере, это удержит босса от моей спины. Затем, когда проект действительно будет определен как катастрофа, я буду первым, кто порекомендует нам переписать его, скопировав и вставив версию в последнюю и лучшую среду, которую никто из нас не понимает.

JeffO
источник
0

Класс и / или функция лучше, по крайней мере, на мой взгляд. На этот раз файл становится меньше, что очень выгодно, если вы работаете с веб-приложениями или приложениями для устройств с небольшим объемом памяти (IoT, старые телефоны и т. Д.).

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

Я написал целый интерпретатор SQL, чтобы лучше переключаться с MySQL на MySQLi в PHP, потому что мне просто нужно изменить мой интерпретатор, и все работает, хотя это немного экстремальный пример.

MY1
источник
-1

Чтобы решить, должен ли код дублироваться или перемещаться в функцию, которая вызывается дважды, попробуйте определить, какая из них более вероятна:

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

  2. Необходимо будет изменить хотя бы одно использование кода, чтобы они различались.

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

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

Supercat
источник