Является ли копирование и вставка кодированием приемлемым - PullRequest
14 голосов
/ 31 декабря 2008

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

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

В более общем случае, вы когда-нибудь сталкивались с ситуацией, когда допускали, что небольшое копирование и вставка кода действительно оправдано.

Ответы [ 14 ]

28 голосов
/ 31 декабря 2008

Задайте этот вопрос о своих функциях

"если это небольшое требование изменится, мне придется изменить обе функции, чтобы удовлетворить его?"

14 голосов
/ 31 декабря 2008

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

Средний случай - использовать макрос, если он у вас есть.

12 голосов
/ 31 декабря 2008

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

6 голосов
/ 31 декабря 2008

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

5 голосов
/ 31 декабря 2008

Re - это когда-либо приемлемо:

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

Re сегменты, которые похожи, но не совсем похожи:

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

Например, если у вас есть сегмент кода, который отличается двумя сегментами - diff # 1 и diff # 2. А в diff # 1 вы можете использовать diff1A или diff1B, а для diff # 2 вы можете использовать diff2A и diff2B.

Если diff1A и diff2A всегда вместе, а diff1B и diff2B всегда вместе, тогда diff1A и diff2A могут содержаться в одном классе команд или в одной абстрактной реализации шаблона, а diff1B & diff2B - в другом.

Однако, если есть несколько комбинаций (например, diff1A и diff2A, diff1A и diff2B, diff1B и diff2A, diff1B и diff2B), вы можете переосмыслить свою функцию, поскольку она может пытаться справиться с слишком многими обязанностями самостоятельно .

Re SQL-операторы:

Использование логики (if-else, циклы) для построения SQL динамически жертвует читабельностью. Но создание всех вариантов SQL будет трудно поддерживать. Так что идите навстречу и используйте сегменты SQL. Извлеките общности в виде сегментов SQL и создайте все варианты SQL с этими сегментами SQL в качестве констант.

Например:

private static final String EMPLOYEE_COLUMNS = " id, fName, lName, status";

private static final String EMPLOYEE_TABLE = " employee";

private static final String EMPLOYEE_HAS_ACTIVE_STATUS = " employee";

private static final String GET_EMPLOYEE_BY_STATUS =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + EMPLOYEE_HAS_ACTIVE_STATUS;

private static final String GET_EMPLOYEE_BY_SOMETHING_ELSE =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + SOMETHING_ELSE;
3 голосов
/ 31 декабря 2008

Как предполагает Мартин Фаулер,

сделай это один раз, хорошо.

сделай это дважды, начинает пахнуть.

сделать это трижды, время на рефакторинг .


РЕДАКТИРОВАТЬ: в ответ на комментарий, источник совета является Дон Робертс:

Три удара и ваш рефакторинг .

Мартин Фаулер описывает это в Рефакторинг глава 2, раздел «Правило трех» (стр. 58).

3 голосов
/ 31 декабря 2008

В кодовой базе моей компании у нас есть серия из примерно 10 крупных операторов SQL, которые имеют высокую степень общности. Все утверждения имеют общее ядро ​​или, по крайней мере, ядро, которое отличается только одним или двумя словами. Затем вы можете сгруппировать 10 операторов в 3 или 4 группы, которые добавляют общие приложения к ядру, опять же, возможно, с одним или двумя словами, отличающимися в каждом приложении. Во всяком случае, представьте, что 10 операторов SQL являются наборами на диаграмме Венна со значительным перекрытием.

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

Код разумен тем, что ни один из SQL никогда не повторяется. Если вам когда-либо понадобится изменить предложение в SQL, вы измените его только в одном месте, и все 10 операторов SQL будут изменены соответствующим образом.

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

С тех пор, как я написал это, я часто задавался вопросом, было бы лучше, если бы мы просто вырезали и вставили SQL-запрос 10 раз. Конечно, если бы мы сделали это, любое изменение в SQL могло бы произойти в 10 местах, но комментарии могли бы помочь нам указать 10 мест для обновления.

Преимущество того, что SQL понятен и все в одном месте, вероятно, перевесит недостатки вырезания и вставки SQL.

2 голосов
/ 02 января 2009
  1. Хороший код - это код многократного использования.
  2. Не изобретай велосипед.
  3. Примеры существуют по причине: чтобы помочь вам учиться, и в идеале лучше кодировать.

Стоит ли копировать и вставлять? Какая разница! Что важно, это , почему вы копируете и вставляете. Я не пытаюсь философствовать о ком-либо здесь, но давайте подумаем об этом практически:

Это из-за лени? "Бла-бла, я делал это раньше ... Я только меняю несколько имен переменных ... готово".

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

Это потому, что вы не понимаете? «Черт ... Я не понимаю, как работает эта функция, но мне интересно, сработает ли она в моем коде…» Возможно! Это может сэкономить ваше время в тот момент, когда вы подчеркнули, что у вас есть крайний срок в 9 часов утра, и вы смотрите красными глазами на часы около 4 часов утра.

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

Итак - копирование и вставка кода? Хорошо, если вы понимаете последствия того, что вы делаете. Иначе? Не делай этого. Кроме того, убедитесь, что у вас есть копия лицензии любого стороннего кода, который вы копируете и вставляете. Кажется, здравый смысл, но вы будете удивлены, сколько людей не делают.

2 голосов
/ 31 декабря 2008

Если это единственный способ сделать это, то пойти на это. Часто (в зависимости от языка) вы можете выполнить небольшие изменения в той же функции с помощью необязательного аргумента.

Недавно у меня была функция add () и функция edit () в скрипте PHP. Они оба сделали практически одно и то же, но функция edit () выполнила запрос UPDATE вместо запроса INSERT. Я просто сделал что-то вроде

function add($title, $content, $edit = false)
{
    # ...
    $sql = edit ? "UPDATE ..." : "INSERT ...";
    mysql_unbuffered_query($sql);
}

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

2 голосов
/ 31 декабря 2008

АБСОЛЮТНО НИКОГДА ..

:)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...