Re[2]: Как решить проблему копипаста.
От: SkyDance Земля  
Дата: 16.03.12 05:26
Оценка:
L>Проблема копипасты в том, что код дублируется и его становится сложнее поддерживать.

Есть эмпирическое правило: если код копи-пастится более одного раза (т.е. присутствует в 3х местах), это 100% кандидат на немедленный рефакторинг.

Но если гарантированно заведомо ясно, что код больше никогда-никогда не будет скопи-пасчет — почему б и не оставить. Особенно если там не три экрана, а десять строк.

Вообще, надо тоже понимать, что копи-паст в некотором объеме неизбежен, особенно при работе с синтаксически неудобными штуками.
Re[3]: Как решить проблему копипаста.
От: maxkar  
Дата: 16.03.12 13:15
Оценка:
Здравствуйте, зиг, Вы писали:

зиг>Здравствуйте, chrisevans, Вы писали:


C>>Иногда даже самый простой рефакторинг может существенно упростить код. И не надо кидаться разбивать метод на кучу маленьких с целью уменьшить количество строк в методе. Лучше начать с выделения основной логики метода и вспомогательной (а если метод состоит из сотен строк кода, то 90% что вспомогательная логика есть). Вспомогательная логика это шаманства, вызванные особенностями библиотек/языков/фреймворков/любых API/кривых архитектур (и в своем коде и в стороннем), и зачастую именно они "утяжеляют" метод и затрудняют работу с основной логикой метода.


зиг>ну а что вот если вот эти шаманства все и так уже вынесены, и там именно идет алгоритм, описание его последовательности?

зиг>если алгоритм состоит из N шагов, то все эти N шагов логично иметь в одном методе?
зиг>А если нет, то по какой системе их разбить/объединить в несколько более крупных частей? если предположить, что эти действия самодостаточные, независимые и атомарные..? Если вот так вот разбить то начнет страдать читаемость, т.к. будет непонятно что делает метод firstPart содержащий первые 10 допустим шагов

Так выделять теперь просто. В начальном посте вы писали
зиг>Измененяем в одном, забываем про второй, ну и т.д. прелести.

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

Не следует путать ситуацию со случаем "просто алгоритм". В этом случае проблем с "изменили в одном месте — нужно менять в другом" просто нет. Так как назначение алгоритмов различно, они и меняются различно. Это соображение по различной дальнейшей эволюции дает и "легальную" копипасту в ряде случаев. Поддержка различных форматов данных и протоколов обмена, например. Особенно, если часть из этих форматов/протоколов еще развиваются. Попытка запихнуть "похожие" алгоритмы в один класс с настройками может выглядеть очень плохо (а может и не выглядеть, зависит от конкретной ситуации).
Re[4]: Как решить проблему копипаста.
От: зиг Украина  
Дата: 16.03.12 19:43
Оценка:
Здравствуйте, maxkar, Вы писали:


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


M>Не следует путать ситуацию со случаем "просто алгоритм". В этом случае проблем с "изменили в одном месте — нужно менять в другом" просто нет. Так как назначение алгоритмов различно, они и меняются различно. Это соображение по различной дальнейшей эволюции дает и "легальную" копипасту в ряде случаев. Поддержка различных форматов данных и протоколов обмена, например. Особенно, если часть из этих форматов/протоколов еще развиваются. Попытка запихнуть "похожие" алгоритмы в один класс с настройками может выглядеть очень плохо (а может и не выглядеть, зависит от конкретной ситуации).


вот-вот, как вот не путать? где тонкая грань — когда надо отрефакторить и запихнуть похожее в один класс с вирт методами, — а когда иметь эту самую легальную копипасту?
заранее ведь и не скажешь, будет дальнейшая эволюция или нет.
Re[5]: Как решить проблему копипаста.
От: dudkin  
Дата: 16.03.12 19:47
Оценка:
Здравствуйте, зиг, Вы писали:

зиг>заранее ведь и не скажешь, будет дальнейшая эволюция или нет.


деолектика, панимаш!
надо писать программы так чтобы никто не мог разобраться но и сам не запутаться
Re[5]: Как решить проблему копипаста.
От: b-3 Россия  
Дата: 17.03.12 07:40
Оценка: :)
Здравствуйте, зиг, Вы писали:

зиг>вот-вот, как вот не путать? где тонкая грань — когда надо отрефакторить и запихнуть похожее в один класс с вирт методами, — а когда иметь эту самую легальную копипасту?

зиг>заранее ведь и не скажешь, будет дальнейшая эволюция или нет.

"Будет эволюция или нет" в разработке ПО необходимо знать. Не только значение (набор требований), но и его производную по времени (насколько конкретные требования подвержены изменениям в средней перспективе).
Должен быть человек, который осмысливает вопросы архитектуры приложения в целом. Должны быть стратегии контроля качества ПО, которые регулируют количество инструментальных классов и степень фреймворкописательства в проекте. В тех 15% случаев, когда копипаста не является следствием ранее случившегося нарушения азбучных истин разработки ПО и/или ранее написанного говнокода — именно стратегии должны давать ответ на вопрос, что лучше — скопипастить код и удвоить поддержку, или написать "универсальный" класс, утроив время на разработку конкретного модуля.
Разумеется, это не отменяет тех 85% случаев, когда копипаста устраняется "по букварю", поэтому вам здесь в основном предлагают почитать букварь.

Мой самый большой грех копипаста был в размере 1200 строк. Некая программа использовала монструозную древовидную структуру данных, которую, как выяснилось слишком поздно, потребуется нетривиальным образом трансформировать. Ну я накатал паттерн обсервер под одну задачу,и уже тогда было страшно. Потому вылезла вторая задача по трансформации дерева, а потом и третья задача, и встала перспектива копипасты кода. Я тогда долго думал, как же придать этой логике универсальность... и пришёл к пониманию, что для этого необходим аппарат уровня XSLT с громоздкими шаблонами, либо функциональщина с полиморфными лямбдами и функциями высшего порядка. В итоге было решено, что это оверкилл, и один обсервер стал тремя похожими обсерверами. До сих пор за тот код стыдно, хоть вроде и поступил рационально, взвешено. Утешаю себя рассуждениями про меньшее зло.
Забанен с формулировкой "клинический дисидент".
copy modify or create new
От: AlexNek  
Дата: 17.03.12 14:04
Оценка:
Здравствуйте, b-3, Вы писали:

b-3>Здравствуйте, зиг, Вы писали:


b-3>Мой самый большой грех копипаста был в размере 1200 строк.

...
b-3>Утешаю себя рассуждениями про меньшее зло.
Похоже проблема копипасты все же есть. Вот и мы сейчас на работе дискутируем скопировать и модифицировать старую часть или начать ее "разрабатывать с нуля" по образу и подобию.
А именно. Есть два устройства и два плагина к ним. Один уже давно есть, другой требуется реализовать.
При разработке первого плагина учитывалось наличие в будующем нескольких плагинов, поэтому многие части которые казались общими вынесены в базовые классы. Также имется достаточное количество интерфейсов и протоколов взаимодействия с главным приложением.
Например, в старом было
public string GeName {get {return "Name1";} }
...
  ReadPart1();

А в новом будет
public string GeName {get {return "Name2";} }
...
  if (ReadPart1()) ReadPart2();

(Код исключительно для примера)
Кода в плагине относительно много, примерно половину старого кода можно сразу выбросить, она специфична исключительно для данного устройства. Остальное можно будет скорректировать, и вполне возможно, что найдутся общие части, которые все же видимо не следует рефакторить.
Например было
  WritePart1();
  WritePart2();

А стало
  WritePart1();
  WritePart2();
  WritePart3();

Но, вполне вероятно, что в третьем плагине будет в этом месте
  WriteAbc();
  WriteCde();

Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re: copy modify or create new
От: jhfrek Россия  
Дата: 17.03.12 14:55
Оценка:
Здравствуйте, AlexNek, Вы писали:

AN>Есть два устройства и два плагина к ним.


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

Скажите, может мне стоит завести базовый класс, вынести туда сортировку, и отнаследовать решатель и парсер от этого класса, что бы сортировку не копипастить?

Я к тому, что копипастить/не копипастить — не определяет архитектуру. Если у вас метод write() — это принципиально присущий всем плагинам метод, да еще и имеющий общую часть, то конечно нужно эту общую часть вынести в базовый класс.

А если один плагин у вас делает write(), а другой showondisplay(), то какой смысл их объединять, пусть даже набор команд в обоих методах будет похожий

AN>Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?


я добавляю постепенно, покрывая добавленную функциональность тестами. Так проще выделять общие методы — при написании придется осознать что пишешь и вспомнить что означал старый код. Так меньше вероятности ошибиться и забыть что-то удалить. Так процесс разработки более естественен и не происходит перегрузки мозга от попыток охватить все скопированное.
Re[2]: copy modify or create new
От: AlexNek  
Дата: 17.03.12 15:31
Оценка:
Здравствуйте, jhfrek, Вы писали:

J>Здравствуйте, AlexNek, Вы писали:


AN>>Есть два устройства и два плагина к ним.


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


J>Скажите, может мне стоит завести базовый класс, вынести туда сортировку, и отнаследовать решатель и парсер от этого класса, что бы сортировку не копипастить?


J>Я к тому, что копипастить/не копипастить — не определяет архитектуру. Если у вас метод write() — это принципиально присущий всем плагинам метод, да еще и имеющий общую часть, то конечно нужно эту общую часть вынести в базовый класс.

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

AN>>Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?


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

J>Так меньше вероятности ошибиться и забыть что-то удалить.
Это фактически главный козырь у сторонников писать с нуля. Тесты то также уже есть их можно также модифицировать.
J>Так процесс разработки более естественен и не происходит перегрузки мозга от попыток охватить все скопированное.
А зачем охватывать все? Двигаемся все равно постепенно.

При таком способе (пишем с нуля) достаточно сильно возрастает время разработаки и уменьшается "преемственность кода". (когда одинаковые действия будут исполнятся по другому), при этом велика вероятность напоротся на забытые камни. Ну типа при возврате строки будем возвращать текст, тогда как в старом плагине это строка берется, например из ресурсов.
Кроме этого при копировании достаточно быстро получается рабочий прототип, пусть и выдающий кривые данные. В этом случае другие члены команды могут работать не с простейней заглушкой, а с чем-то более реальным.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[3]: copy modify or create new
От: jhfrek Россия  
Дата: 17.03.12 17:52
Оценка:
Здравствуйте, AlexNek, Вы писали:

J>>Я к тому, что копипастить/не копипастить — не определяет архитектуру. Если у вас метод write() — это принципиально присущий всем плагинам метод, да еще и имеющий общую часть, то конечно нужно эту общую часть вынести в базовый класс.

AN>Ну вроде написал, что части которые будут предположительно общие уже вынесены в базовые (или отдельные) классы.
AN>Вполне вероятно, что могут найтись части которые будут общими исключительно для этих двух устройств, но городить для этого еще один слой абстракции, думается будет лишним.

согласен, лишний слой только ради борьбы с копипастом нафиг не нужен — навороченная архитектура может быть хуже копипаста

J>>Так меньше вероятности ошибиться и забыть что-то удалить.

AN>Это фактически главный козырь у сторонников писать с нуля. Тесты то также уже есть их можно также модифицировать.

ну да

J>>Так процесс разработки более естественен и не происходит перегрузки мозга от попыток охватить все скопированное.

AN>А зачем охватывать все? Двигаемся все равно постепенно.

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

AN>При таком способе (пишем с нуля) достаточно сильно возрастает время разработаки и уменьшается "преемственность кода". (когда одинаковые действия будут исполнятся по другому),


а это главный козырь у сторонников из противоположного лагеря

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

AN>при этом велика вероятность напоротся на забытые камни. Ну типа при возврате строки будем возвращать текст, тогда как в старом плагине это строка берется, например из ресурсов.

AN>Кроме этого при копировании достаточно быстро получается рабочий прототип, пусть и выдающий кривые данные. В этом случае другие члены команды могут работать не с простейней заглушкой, а с чем-то более реальным.

при создании с нуля тоже получается рабочий прототип, выдающий правильные данные, просто не все или не всеми способами.
Re[5]: Как решить проблему копипаста.
От: maxkar  
Дата: 17.03.12 20:06
Оценка:
Здравствуйте, зиг, Вы писали:

зиг>вот-вот, как вот не путать?

А вот для ответа на этот вопрос нужна "модель" приложения. И "высокоуровневая" семантика действий. Например:
payer.withdrawMoney(amount);
receiver.addMoney(amount);

это перевод денег от одного участника к другому (две строчки вместе). И это действие просится в отдельный метод transferMoney. Именно потому, что это одно и то же действие в рамках "глобальной" модели приложения. Возможная эволюция — уведомление уполномоченных органов о больших переводах между двумя физическими лицами. Вынос метода очень поможет. В этом случае достаточно будет реализовать уведомление только в одном месте. Копипасту этого фрагмента найти будет сложно — и withdrawMoney, и addMoney могут использоваться по-отдельности. А в местах парного использования между ними может быть еще какой-то код (например, добавили логирование для отладки да так там и оставили). Так что ни поиск вызовов, ни поиск по тексту работать не будут.

Второй пример (где-то в драйвере устройства, уровень сообщений, транспортный уровень (потоки) не показан):
public byte[] toProtoMessage(AppMessage message) {
  final byte[] payload = toPayload(message);
  final byte[] checksum = calculateChecksum(payload);
  return formatOutputMessage(payload, checksum);
}

В этом случае семантика — формирование сообщения для конкретного протокола. Если вся остальная работа с устройством ведется только посредством интерфейса драйвера, действие одно и хорошо изолировано. Потому что действие в данном случае "сформировать пакет передачи данных для устройства Х". В другом устройстве тот же код будет делать действие "сформировать пакет передачи данных для устройства Y". Дальше возможны два варианта. Первый — изменение протокола (например, можно добавлять пакет мониторинга (время компьютера, например)). Вполне естественно, что изменение локально и будет выполнено только для одного устройства. Второй — изменение высокоуровневых требований. Но в этом случае разработчик сначала найдет интерфейс драйвера и уже затем будет править каждый драйвер под новую функциональность. В некоторых случаях ему все равно придется пересматривать соответствующие протоколы. Так что он заметит "похожий" код и исправит его. Если это будут делать несколько программистов, они могут внести похожие правки в похожие классы (допустим, каждый из них копипасту не видит). В этом случае если правки корректны (прошло тестирование), тоже все будет нормально. Код может отличаться все больше и больше, но это не страшно до тех пор, пока драйверы делают "разные" действия (работают с разными устройствами).

зиг>где тонкая грань — когда надо отрефакторить и запихнуть похожее в один класс с вирт методами, — а когда иметь эту самую легальную копипасту?

Она не тонкая. Она очень четкая. Смотрим на код, предполагаем, какие требования могут измениться или появиться. Изменения, приводящие к глобальной переработке большой части приложения не рассматриваем. Если получилось так, что при изменении в одном месте придется изменить и код в другом (для выполнения этого же требования), значит копипасты быть не должно. При небольшой практике такие примеры придумывать достаточно просто.

Если пример не придумывается но есть сомнения, лучше все равно сделать рефакторинг. Так что "легальная копипаста" остается только в тех случаях, где она "определенно допустима" и не принесет проблем.

Есть эвристика, которую можно учитывать. Предположим, ваш код поддерживает другой программист. Он читал описание архитектуры, руководящую документацию, но в коде пока ориентируется плохо. И ему нужно сделать какое-то изменение. Вопрос — найдет ли он и второе место, которое нужно изменить? Если копипаста встречается в реализациях одного и того же интерфейса, реализации которого глобально заменимы по всей программе (т.е. весь код работает только в этих абстракциях!), тогда, вероятно, все хорошо. В этом случае программист дойдет до вызова метода интерфейса и пойдет править все реализации. Оба требования (интерфейс и возможность глобальной замены) обязательны! Абстрактные классы с наследованием обычно (ладно, только мной, но все же...) воспринимаются как "специализированные" версии, заточенные в рамках шаблона под одну задачу. В этом случае разработчик найдет одну реализацию (в наиболее вероятном сценарии) и исправит только ее (совершенно не очевидно, что будут "похожие" специализации). Необходимость глобальной заменимости нужна в случае вызовов вроде doSomethingGeneric(new ConcreteImplementation1(), p1, p2). Здесь обобщенный метод (работающий с интерфейсом) получает конкретную реализацию, создающуюся "по-месту". Программист может решить, что достаточно поправить только эту реализацию (так как она отвечает за функциональность в найденном сценарии) и все.

И еще. Предложенный рефакторинг мне не нравится. Наследование здесь в большинстве случаев лишнее. Можно вынести все эти абстрактные методы в интерфейс. Необходимый контекст передавать через параметры или callback'и (если функции должны изменять состояние "родителя"). Возможно, еще и кандидата в абстрактного родителя стоит переделать. Или разобрать на несколько статических методов. Или, например, сделать его конструктор непубличным (создавать экземпляры статическими методами). Еще лучше, если "родитель" реализует какой-то интерфейс. Тогда его можно сделать и не видимым публично (пакетная видимость), предоставив создание экземпляров через статические методы (которые возвращают "интерфейс", а не конкретный тип). Последний вариант хорош тем, что позволяет в дальнейшем производить достаточно глобальные изменения не затрагивая публичный интерфейс (например, один класс может быть заменен на несколько, для нескольких вариантов может начать использоваться "общая" обертка, реализующая интерфейс и т.п.).

зиг>заранее ведь и не скажешь, будет дальнейшая эволюция или нет.

Изменения будут! Я выше тоже ведь писал не "если будет эволюция", а "какая будет эволюция". Изменения будут всегда по закону подлости. Вполне вероятно, что изменения будут даже там, где они "никогда не могут быть". Хотя если последний случай отражен в документации (о том, что приложение не поддерживает определенные сценарии), это не страшно.

Также нужно предполагать, что изменения будет делать человек, плохо ориентирующийся в коде (и, соответственно, не представляющий о существовании копипасты). Выше уже описывал, что есть некоторые особенности работы с кодом, и эти особенности могут влиять на вероятность нахождения всех необходимых мест. Так что выбор копипаста/рефакторинг будет зависеть и от критичности кода, и от необходимости копипасты. В идеале даже одна копипаста одного и того же "семантического" действия должна рефакториться (если при этом образуется другое, никак не связное с первым действие — копипаста легальна).
Re: copy modify or create new
От: maxkar  
Дата: 17.03.12 20:33
Оценка:
Здравствуйте, AlexNek, Вы писали:

AN>Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?


Скорее всего, второй подход будет лучше, но нужно попробовать и посмотреть на результат. Возможные проблемы:
  1. Код плагинов окажется слишком различным. В этом случае проще будет писать с нуля.
  2. Код плагинов окажется слишком похожим. При этом будут какие-то мелкие детали все же будут отличаться (нужно еще, чтобы при чтении кода такие детали ловить было сложно). Здесь уже на практике нужно смотреть, позволит ли увеличение времени на разработку сократить время на тестирование.
  3. При разработке какие-то изменения будут забыты (граничные случаи и т.п.). Чем больше граничных случаев, тем больше вероятность ошибки.

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

Если "база" похожа, стоит взять ее. Как раз из-за соображений протестированности (существующая база уже работает). Радикально другое решение вы строить не собираетесь, так что смысла писать код "второй раз" не имеет смысла. Еще и ценный опыт получите — насколько база подходит под "разные" задачи. Если вдруг появятся третье, четвертое и пятое устройства, вы уже будете знать, чего не хватает в существующем решении и как его лучше модифицировать для большей универсальности. А вот решение о том, как именно писать отдельные плагины, можно оставить разрабочтикам, ответственным за эти плагины. Они выберут ту методологию, с которой комфортнее работать лично им.
Re[4]: copy modify or create new
От: AlexNek  
Дата: 17.03.12 21:52
Оценка:
Здравствуйте, jhfrek, Вы писали:

J>Здравствуйте, AlexNek, Вы писали:



AN>>при этом велика вероятность напоротся на забытые камни. Ну типа при возврате строки будем возвращать текст, тогда как в старом плагине это строка берется, например из ресурсов.

AN>>Кроме этого при копировании достаточно быстро получается рабочий прототип, пусть и выдающий кривые данные. В этом случае другие члены команды могут работать не с простейней заглушкой, а с чем-то более реальным.

J>при создании с нуля тоже получается рабочий прототип, выдающий правильные данные, просто не все или не всеми способами.

Не всегда это может получится также быстро. Кроме того, многие ньюансы вероятно просто забылись. Уже пройденную дорогу нужно будет пройти еще раз.
А так, копируем старый плагин, меням пару идентификаторов, подсовываем ему старое устройство. И ГВИ уже показывает что у нас устройство "Б", а не "А". Теперь делаем фейк данные, ... и новый плагин готов для использования в разработке. Кстати, просто выдать данные в "пустом" плагине не так уж и просто, приложение ожидает строго определенную последовательность событий генерируемую "рабочим" плагином.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[2]: copy modify or create new
От: AlexNek  
Дата: 17.03.12 23:09
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Здравствуйте, AlexNek, Вы писали:


AN>>Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?


M>Скорее всего, второй подход будет лучше, но нужно попробовать и посмотреть на результат. Возможные проблемы:

M>

    M>
  1. Код плагинов окажется слишком различным. В этом случае проще будет писать с нуля.
    Этого не может быть в принципе — скелет будет всегда одинаковым. Так специально спроектировано.
    M>
  2. Код плагинов окажется слишком похожим. При этом будут какие-то мелкие детали все же будут отличаться (нужно еще, чтобы при чтении кода такие детали ловить было сложно). Здесь уже на практике нужно смотреть, позволит ли увеличение времени на разработку сократить время на тестирование.
    Ну если устройства принадлежат к одной группе, то различий будет действительно немного.
    M>
  3. При разработке какие-то изменения будут забыты (граничные случаи и т.п.). Чем больше граничных случаев, тем больше вероятность ошибки.
    А почему это будет специфично именно для модификации? Для нового кода будет абсолютно тоже самое, при том что можно забыть то, что уже было реализовано.
    M>

M>Скорее всего, у вас получится гибридный вариант. Либо будет взят готовый плагин, какие-то его части будут модифицированы, какие-то — переписаны. Или плагин будет писаться с самого начала, но какие-то фрагменты будут браться из уже существующего решения.


M>Если "база" похожа, стоит взять ее. Как раз из-за соображений протестированности (существующая база уже работает).

M>Радикально другое решение вы строить не собираетесь, так что смысла писать код "второй раз" не имеет смысла.
Если бы все так думали. Только согласно какой "умной книжки" код всегда следует писать с нуля маленьким порциями и не сверху вниз, а исключительно снизу вверх.
M>Еще и ценный опыт получите — насколько база подходит под "разные" задачи.
Слишком больших открытий не ожидается, так как концепт уже был отработан в предыдущей программе, которая не использовала плагины.
M>Если вдруг появятся третье, четвертое и пятое устройства, вы уже будете знать, чего не хватает в существующем решении и как его лучше модифицировать для большей универсальности.
M>А вот решение о том, как именно писать отдельные плагины, можно оставить разрабочтикам, ответственным за эти плагины. Они выберут ту методологию, с которой комфортнее работать лично им.
А вот этого не следует делать. Все плагины должны использовать один концепт и один скелет. Иначе, когда будет десяток плагинов никто в них не разберется, они все будут разными.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[2]: copy modify or create new
От: AlexNek  
Дата: 17.03.12 23:13
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Здравствуйте, AlexNek, Вы писали:


AN>>Но основной вопрос все же другой. Писать новый плагин полностью с нуля, посматривая на старый или скопировать нужные части старого и модифицировать их постепенно?


M>Скорее всего, второй подход будет лучше, но нужно попробовать и посмотреть на результат. Возможные проблемы:

M>

    M>
  1. Код плагинов окажется слишком различным. В этом случае проще будет писать с нуля.
    Этого не может быть в принципе — скелет будет всегда одинаковым. Так специально спроектировано.
    M>
  2. Код плагинов окажется слишком похожим. При этом будут какие-то мелкие детали все же будут отличаться (нужно еще, чтобы при чтении кода такие детали ловить было сложно). Здесь уже на практике нужно смотреть, позволит ли увеличение времени на разработку сократить время на тестирование.
    Ну если устройства принадлежат к одной группе, то различий будет действительно немного.
    M>
  3. При разработке какие-то изменения будут забыты (граничные случаи и т.п.). Чем больше граничных случаев, тем больше вероятность ошибки.
    А почему это будет специфично именно для модификации? Для нового кода будет абсолютно тоже самое, при том что можно забыть то, что уже было реализовано.
    M>

M>Скорее всего, у вас получится гибридный вариант. Либо будет взят готовый плагин, какие-то его части будут модифицированы, какие-то — переписаны. Или плагин будет писаться с самого начала, но какие-то фрагменты будут браться из уже существующего решения.


M>Если "база" похожа, стоит взять ее. Как раз из-за соображений протестированности (существующая база уже работает).

M>Радикально другое решение вы строить не собираетесь, так что смысла писать код "второй раз" не имеет смысла.
Если бы все так думали. Только согласно какой "умной книжки" код всегда следует писать с нуля маленьким порциями и не сверху вниз, а исключительно снизу вверх.
M>Еще и ценный опыт получите — насколько база подходит под "разные" задачи.
Слишком больших открытий не ожидается, так как концепт уже был отработан в предыдущей программе, которая не использовала плагины.
M>Если вдруг появятся третье, четвертое и пятое устройства, вы уже будете знать, чего не хватает в существующем решении и как его лучше модифицировать для большей универсальности.
M>А вот решение о том, как именно писать отдельные плагины, можно оставить разрабочтикам, ответственным за эти плагины. Они выберут ту методологию, с которой комфортнее работать лично им.
А вот этого не следует делать. Все плагины должны использовать один концепт и один скелет. Иначе, когда будет десяток плагинов никто в них не разберется, они все будут разными.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[5]: copy modify or create new
От: jhfrek Россия  
Дата: 18.03.12 07:54
Оценка:
Здравствуйте, AlexNek, Вы писали:

J>>при создании с нуля тоже получается рабочий прототип, выдающий правильные данные, просто не все или не всеми способами.

AN>Не всегда это может получится также быстро. Кроме того, многие ньюансы вероятно просто забылись. Уже пройденную дорогу нужно будет пройти еще раз.

вот именно поэтому мне вариант с нуля больше нравится — вспоминание ньюансов позволяет меньше ошибаться и дает идеи для рефакторинга

AN>А так, копируем старый плагин, меням пару идентификаторов, подсовываем ему старое устройство. И ГВИ уже показывает что у нас устройство "Б", а не "А". Теперь делаем фейк данные, ... и новый плагин готов для использования в разработке. Кстати, просто выдать данные в "пустом" плагине не так уж и просто, приложение ожидает строго определенную последовательность событий генерируемую "рабочим" плагином.


ха, ну раз все так просто, то конечно переделывайте существующий.

Просто я сталкивался со случаем, когда все было просто только внешне — класс по чтению и обработке файла. Функция Открыть() внутри которой вызывается ОбработатьДанныеТипаА(). Копируем, меняем идентификатор ОбработатьДанныеТипаА() на заглушку ОбработатьДанныеТипаВ() — наш новый тип, — компилируем, запускаем, все работает. Добавляем остальные методы — тоже работает, но после добавление какого-то из методов начинает слетать. Откатываем — прекращает слетать, накатываем еще раз — не слетает, запускаем несколько раз — не слетает. Снова продолжаем добавлять — через сколько-то методов опять получаем слет, откатываем, все еще слетает, потом прекращает... Короче пара дней была убита на поиск бага, при редко воспроизводящихся слетах непонятно в какой функции.

Оказалось какой-то деятель, вставил нотификацию файл_открыт не в метод Открыть(), а в метод ОбработатьДанныеТипаА(). Причем эта нотификация вызывалась косвенно, порождая пакет других нотификаций, приводящих в конечном итоге к инициализации данных вообще в другом куске кода, о котором никто даже не вспомнил.
Re[6]: copy modify or create new
От: AlexNek  
Дата: 18.03.12 11:13
Оценка:
Здравствуйте, jhfrek, Вы писали:

J>Здравствуйте, AlexNek, Вы писали:


J>>>при создании с нуля тоже получается рабочий прототип, выдающий правильные данные, просто не все или не всеми способами.

AN>>Не всегда это может получится также быстро. Кроме того, многие ньюансы вероятно просто забылись. Уже пройденную дорогу нужно будет пройти еще раз.

J>вот именно поэтому мне вариант с нуля больше нравится — вспоминание ньюансов позволяет меньше ошибаться и дает идеи для рефакторинга

Очень не уверен, что можно вспомнить ньюансы не видя старого кода. Да даже если и смотреть фиг бывает вспомнишь почему именно так сделано (если коммент забыли). Рефакторинга чего? ведь все по новому делаем. А вот когда страое приспосбливаешь к новому, вот именно тогда и возникает море ньюансов.

AN>>А так, копируем старый плагин, меням пару идентификаторов, подсовываем ему старое устройство. И ГВИ уже показывает что у нас устройство "Б", а не "А". Теперь делаем фейк данные, ... и новый плагин готов для использования в разработке. Кстати, просто выдать данные в "пустом" плагине не так уж и просто, приложение ожидает строго определенную последовательность событий генерируемую "рабочим" плагином.


J>ха, ну раз все так просто, то конечно переделывайте существующий.

Мне в принипе было просто интересно узнать различные мнения по данному поводу. Так как "сверху" уже поступила противополжная директива.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[7]: copy modify or create new
От: jhfrek Россия  
Дата: 18.03.12 15:42
Оценка:
Здравствуйте, AlexNek, Вы писали:


J>>вот именно поэтому мне вариант с нуля больше нравится — вспоминание ньюансов позволяет меньше ошибаться и дает идеи для рефакторинга

AN>Очень не уверен, что можно вспомнить ньюансы не видя старого кода.

почему не видя? новый код пишется по образу и подобию старого

AN>Да даже если и смотреть фиг бывает вспомнишь почему именно так сделано (если коммент забыли).


если не вспомнишь, то не добавляем — зачем нам неизвестно что делающий код

AN>Рефакторинга чего? ведь все по новому делаем. А вот когда страое приспосбливаешь к новому, вот именно тогда и возникает море ньюансов.


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

AN>>>А так, копируем старый плагин, меням пару идентификаторов, подсовываем ему старое устройство. И ГВИ уже показывает что у нас устройство "Б", а не "А". Теперь делаем фейк данные, ... и новый плагин готов для использования в разработке. Кстати, просто выдать данные в "пустом" плагине не так уж и просто, приложение ожидает строго определенную последовательность событий генерируемую "рабочим" плагином.

J>>ха, ну раз все так просто, то конечно переделывайте существующий.
AN>Мне в принипе было просто интересно узнать различные мнения по данному поводу. Так как "сверху" уже поступила противополжная директива.

да и я не настаиваю на своей правоте — так, размышляю и делюсь своим опытом.
Re[8]: copy modify or create new
От: AlexNek  
Дата: 18.03.12 16:28
Оценка:
Здравствуйте, jhfrek, Вы писали:

J>Здравствуйте, AlexNek, Вы писали:



J>>>вот именно поэтому мне вариант с нуля больше нравится — вспоминание ньюансов позволяет меньше ошибаться и дает идеи для рефакторинга

AN>>Очень не уверен, что можно вспомнить ньюансы не видя старого кода.

J>почему не видя? новый код пишется по образу и подобию старого

Это как?На одном экране старый код, на другом новый, используя визуальное копирование?

AN>>Да даже если и смотреть фиг бывает вспомнишь почему именно так сделано (если коммент забыли).


J>если не вспомнишь, то не добавляем — зачем нам неизвестно что делающий код

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

AN>>Рефакторинга чего? ведь все по новому делаем. А вот когда страое приспосбливаешь к новому, вот именно тогда и возникает море ньюансов.


J>методов. В старом коде есть метод который читает данные, пихает их в массив, сортирует его и потом как-то массив обрабатывает. Нам надо написать аналог метода для нового кода. Мы внимательно смотрим на старый метод, вникаем в него, понимаем что у нового кода будет отличатся только обработка.

J>Значит сначала рефакторим старый код — выделяем из него функцию обработки,
где будет находится этот "старый код"
J>делаем ее виртуальной и в новом коде только лишь переопределяем эту функцию.
А откуда в новом возьмутся старые части?

AN>>>>А так, копируем старый плагин, меням пару идентификаторов, подсовываем ему старое устройство. И ГВИ уже показывает что у нас устройство "Б", а не "А". Теперь делаем фейк данные, ... и новый плагин готов для использования в разработке. Кстати, просто выдать данные в "пустом" плагине не так уж и просто, приложение ожидает строго определенную последовательность событий генерируемую "рабочим" плагином.

J>>>ха, ну раз все так просто, то конечно переделывайте существующий.
AN>>Мне в принипе было просто интересно узнать различные мнения по данному поводу. Так как "сверху" уже поступила противополжная директива.

J>да и я не настаиваю на своей правоте — так, размышляю и делюсь своим опытом.

Я также не знаю правильного ответа, но что то мне подсказывает, что делать все с нуля не пользуя старый код будет далеко не оптимальным решением.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Re[9]: copy modify or create new
От: jhfrek Россия  
Дата: 18.03.12 16:52
Оценка:
Здравствуйте, AlexNek, Вы писали:

J>>почему не видя? новый код пишется по образу и подобию старого

AN>Это как?На одном экране старый код, на другом новый, используя визуальное копирование?

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

AN>>>Да даже если и смотреть фиг бывает вспомнишь почему именно так сделано (если коммент забыли).

J>>если не вспомнишь, то не добавляем — зачем нам неизвестно что делающий код
AN>То бишь лучше опять ждать аналогичного теста, который выявит старую/решенную проблему?

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

AN>>>Рефакторинга чего? ведь все по новому делаем. А вот когда страое приспосбливаешь к новому, вот именно тогда и возникает море ньюансов.

J>>методов. В старом коде есть метод который читает данные, пихает их в массив, сортирует его и потом как-то массив обрабатывает. Нам надо написать аналог метода для нового кода. Мы внимательно смотрим на старый метод, вникаем в него, понимаем что у нового кода будет отличатся только обработка.
J>>Значит сначала рефакторим старый код — выделяем из него функцию обработки,
AN>где будет находится этот "старый код"

старый код — это обработчик файлов типа А, новый код — это обработчик файлов типа Б, который нам надо написать по образу и подобию обработчика файлов типа А

J>>делаем ее виртуальной и в новом коде только лишь переопределяем эту функцию.

AN>А откуда в новом возьмутся старые части?

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

Это дольше, чем скопировать и переименовать, но это надежнее и приятнее в программировании

J>>да и я не настаиваю на своей правоте — так, размышляю и делюсь своим опытом.

AN>Я также не знаю правильного ответа, но что то мне подсказывает, что делать все с нуля не пользуя старый код будет далеко не оптимальным решением.

зависит от старого кода, в примере который я рассказал написание с нуля вышло бы быстрее чем последующий поиск бага в скопированном
Re[10]: copy modify or create new
От: AlexNek  
Дата: 18.03.12 22:44
Оценка:
Здравствуйте, jhfrek, Вы писали:

J>Здравствуйте, AlexNek, Вы писали:


J>>>почему не видя? новый код пишется по образу и подобию старого

AN>>Это как?На одном экране старый код, на другом новый, используя визуальное копирование?

J>нет, смотрим на старый код, анализируем его, понимаем что делает каждая строчка, превращаем старый код мысленно или на бумаге в алгоритм и аналогичный алгоритм реализуем для нового кода.

Какая вероятность того что эти два кода будут достаточно сильно отличаться?

AN>>>>Да даже если и смотреть фиг бывает вспомнишь почему именно так сделано (если коммент забыли).

J>>>если не вспомнишь, то не добавляем — зачем нам неизвестно что делающий код
AN>>То бишь лучше опять ждать аналогичного теста, который выявит старую/решенную проблему?

J>нет, лучше потратить время и разобраться что старый код делает


AN>>>>Рефакторинга чего? ведь все по новому делаем. А вот когда страое приспосбливаешь к новому, вот именно тогда и возникает море ньюансов.

J>>>методов. В старом коде есть метод который читает данные, пихает их в массив, сортирует его и потом как-то массив обрабатывает. Нам надо написать аналог метода для нового кода. Мы внимательно смотрим на старый метод, вникаем в него, понимаем что у нового кода будет отличатся только обработка.
J>>>Значит сначала рефакторим старый код — выделяем из него функцию обработки,
AN>>где будет находится этот "старый код"

J>старый код — это обработчик файлов типа А, новый код — это обработчик файлов типа Б, который нам надо написать по образу и подобию обработчика файлов типа А


J:Значит сначала рефакторим старый код — выделяем из него функцию обработки,
где будет физически находится этот "старый код" во время рефакторинга?

J>>>делаем ее виртуальной и в новом коде только лишь переопределяем эту функцию.

AN>>А откуда в новом возьмутся старые части?

J>так мы ж отрефакторили — общую часть выделили в базовый класс в котором теперь данные читаются, пихаются и сортируются и обрабатываются абстрактным обработчиком, который перекрывается в обоих методах.

То бишь делаем копию старого кода в которой его рефакторим и проверяем работоспособность.

J>Это дольше, чем скопировать и переименовать, но это надежнее и приятнее в программировании

А что мешает рефакторить старый код сразу на новом месте?

J>>>да и я не настаиваю на своей правоте — так, размышляю и делюсь своим опытом.

AN>>Я также не знаю правильного ответа, но что то мне подсказывает, что делать все с нуля не пользуя старый код будет далеко не оптимальным решением.

J>зависит от старого кода, в примере который я рассказал написание с нуля вышло бы быстрее чем последующий поиск бага в скопированном

Так это же просто замечательно что нашли новый баг, его нужно и со старого кода вычистить.
Cообщение написано в << RSDN@Home 1.2.0 alpha 5-AN-R8 rev. 13227>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.