Этот самый А процессит некие файлы, несколько раз занося попутно какие-то результаты в бд, периодически что-то оттуда доставая и т.д.. В общем там довольно сложный процесс, и в нем есть метод process, на несколько экранов.
Нам понадобилось иметь класс B, который делает почти что то же самое, НО не совсем то. Тоже точно так же все процессит, но чуууточку по-другому.
Навставлять в A в метод process каких-то условий, чтобы вот эти вот отличия учитывались — не получится. Точнее, может и получится, но тогда код будет ужасный, он станет не 3 экрана, а 5, т.к. там вовлекутся структурные изменения (новые циклы добавятся, и т.д.).
Можно просто в класс B скопипастить метод из A, провести все изменения и вуаля. Будем иметь 2 почти одинаковых класса, с общим алгоритмом. Измененяем в одном, забываем про второй, ну и т.д. прелести.
Я не пойму, как же красиво такое сделать??!
Если попробовать сделать вначале копипаст, а потом провести рефакторинг, так чтобы весь общий код например был в абстрактном родителе X... — это тоже плохо, в таких случаях всегда страдает читабельность. Был метод на 3 экрана, но зато понятна последовательность была алгоритма — тут будет 5 экранов размазанных уже по нескольким классам — и все это в погоне за избавлением от копипасты...
Здравствуйте, зиг, Вы писали:
зиг>Есть у меня один класс, назовем его A, extends X.
зиг>Этот самый А процессит некие файлы, несколько раз занося попутно какие-то результаты в бд, периодически что-то оттуда доставая и т.д.. В общем там довольно сложный процесс, и в нем есть метод process, на несколько экранов.
зиг>Нам понадобилось иметь класс B, который делает почти что то же самое, НО не совсем то. Тоже точно так же все процессит, но чуууточку по-другому.
...
А чем не нравиться просто виртуальный метод process?
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, зиг, Вы писали:
зиг>>Может какие-то паттерны есть под это дело?
G>Сначала разбить большой метод на маленькие, не более одного экрана размером. А потом уже делать параметры, которые процессинг настраивают.
эта разбивка уменьшит читаемость.
потом, там в зависимости от параметра — добавляется большой цикл, т.е. оборачивается большая часть кода этим циклом
как такое разбить?
Здравствуйте, зиг, Вы писали:
зиг>Этот самый А процессит некие файлы, несколько раз занося попутно какие-то результаты в бд, периодически что-то оттуда доставая и т.д.. В общем там довольно сложный процесс, и в нем есть метод process, на несколько экранов. зиг>Нам понадобилось иметь класс B, который делает почти что то же самое, НО не совсем то. Тоже точно так же все процессит, но чуууточку по-другому.
... зиг>Может какие-то паттерны есть под это дело?
это не вопрос, это поиск оправданий для нежелания делать рефакторинг.
в этом методе есть куски, которые можно вынести в отдельные функции. После переноса станут ясны взаимосвязи — параметры функций. Попутно общее методы могут уйти в другие классы. Отличающиеся куски можно будет обернуть в виртуальные методы и проблема будет решена.
Здравствуйте, зиг, Вы писали:
зиг>эта разбивка уменьшит читаемость.
Разбивка практически никогда не уменьшает читаемость, если конечно методы называть не process, solve и doIt а так, что бы по названию было понятно что метод делает.
зиг>потом, там в зависимости от параметра — добавляется большой цикл, т.е. оборачивается большая часть кода этим циклом зиг>как такое разбить?
Вынести внутренность цикла в метод, а оставшуюся часть переопределить в одном классе с циклом, в другом без.
Здравствуйте, зиг, Вы писали:
зиг>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, зиг, Вы писали:
зиг>>>Может какие-то паттерны есть под это дело?
G>>Сначала разбить большой метод на маленькие, не более одного экрана размером. А потом уже делать параметры, которые процессинг настраивают.
зиг>эта разбивка уменьшит читаемость.
Неправда. Главное подобрать хорошие имена функциям.
зиг>потом, там в зависимости от параметра — добавляется большой цикл, т.е. оборачивается большая часть кода этим циклом зиг>как такое разбить?
Ну так внутреннюю часть вынести в метод, а потом сам метод передавать параметром в другой, и выполнять в цикле если надо.
Для начала разбей большой метода на маленькие, а только потом думай как обобщить.
Иногда даже самый простой рефакторинг может существенно упростить код. И не надо кидаться разбивать метод на кучу маленьких с целью уменьшить количество строк в методе. Лучше начать с выделения основной логики метода и вспомогательной (а если метод состоит из сотен строк кода, то 90% что вспомогательная логика есть). Вспомогательная логика это шаманства, вызванные особенностями библиотек/языков/фреймворков/любых API/кривых архитектур (и в своем коде и в стороннем), и зачастую именно они "утяжеляют" метод и затрудняют работу с основной логикой метода.
Это все банальности, но даже понимая их часто о них забываешь.
Здравствуйте, chrisevans, Вы писали:
C>Иногда даже самый простой рефакторинг может существенно упростить код. И не надо кидаться разбивать метод на кучу маленьких с целью уменьшить количество строк в методе. Лучше начать с выделения основной логики метода и вспомогательной (а если метод состоит из сотен строк кода, то 90% что вспомогательная логика есть). Вспомогательная логика это шаманства, вызванные особенностями библиотек/языков/фреймворков/любых API/кривых архитектур (и в своем коде и в стороннем), и зачастую именно они "утяжеляют" метод и затрудняют работу с основной логикой метода.
C>Это все банальности, но даже понимая их часто о них забываешь.
В общем, есть метод process, который знает, что некоторые части его логики могут меняться, но не знает как. Есть подклассы, каждый из которых знает как должна меняться его логика для метода process. Значит в метод process эту логику можно передавать.
Здравствуйте, chrisevans, Вы писали:
C>Иногда даже самый простой рефакторинг может существенно упростить код. И не надо кидаться разбивать метод на кучу маленьких с целью уменьшить количество строк в методе. Лучше начать с выделения основной логики метода и вспомогательной (а если метод состоит из сотен строк кода, то 90% что вспомогательная логика есть). Вспомогательная логика это шаманства, вызванные особенностями библиотек/языков/фреймворков/любых API/кривых архитектур (и в своем коде и в стороннем), и зачастую именно они "утяжеляют" метод и затрудняют работу с основной логикой метода.
ну а что вот если вот эти шаманства все и так уже вынесены, и там именно идет алгоритм, описание его последовательности?
если алгоритм состоит из N шагов, то все эти N шагов логично иметь в одном методе?
А если нет, то по какой системе их разбить/объединить в несколько более крупных частей? если предположить, что эти действия самодостаточные, независимые и атомарные..? Если вот так вот разбить то начнет страдать читаемость, т.к. будет непонятно что делает метод firstPart содержащий первые 10 допустим шагов
C>Это все банальности, но даже понимая их часто о них забываешь.
Здравствуйте, зиг, Вы писали:
зиг>ну а что вот если вот эти шаманства все и так уже вынесены, и там именно идет алгоритм, описание его последовательности? зиг>если алгоритм состоит из N шагов, то все эти N шагов логично иметь в одном методе? зиг>А если нет, то по какой системе их разбить/объединить в несколько более крупных частей? если предположить, что эти действия самодостаточные, независимые и атомарные..? Если вот так вот разбить то начнет страдать читаемость, т.к. будет непонятно что делает метод firstPart содержащий первые 10 допустим шагов
C>>Это все банальности, но даже понимая их часто о них забываешь.
На моем опыте в 98% случаев методы больше 2х экранов можно разбить на несколько более простых методов с улучшением ситуации. Но если называть методы firstPart() то конечно читаемость не улучшится
Еще посмотрите паттерн template method, возможно он подойдет к вашей ситуации.
А вообще гадать можно долго, может лучше метод приведете?
Здравствуйте, зиг, Вы писали:
зиг>ну а что вот если вот эти шаманства все и так уже вынесены, и там именно идет алгоритм, описание его последовательности? зиг>если алгоритм состоит из N шагов, то все эти N шагов логично иметь в одном методе?
не бывает алгоритма из N шагов, даже простейшая операция деления состоит не из N абстрактных шагов, а из вполне осмысленных операций, причем не над абстрактными числами, а над числами имеющими особую роль — нужно взять делимое и поделить его на делитель.
Для стековой машины это будет 3 осмысленных процедуры: Загрузить делимое(); Загрузить делитель(); Поделить()
зиг>А если нет, то по какой системе их разбить/объединить в несколько более крупных частей? если предположить, что эти действия самодостаточные, независимые и атомарные..? Если вот так вот разбить то начнет страдать читаемость, т.к. будет непонятно что делает метод firstPart содержащий первые 10 допустим шагов
не, лучше Part1 с одним шагом, Part2 с двумя шагами, Part3 с тремя шагами — веселее будет разбираться. Рефакторинг — это же деление большой процедуры случайным образом на части случайного размера.
Какие в баню 10 шагов?
Вот простейшая процедура сортировки пузырьком, пусть с твоими загадочными процедурами настройки
for i := 1 to N do
.... - команды настройки на 2 экрана
for j := i to N do
.... - команды настройки на 2 экрана
if A[i] < A[j] then begin
X := A[i];
A[i] := A[j];
A[j] := X
end
разберешься ты, что делает эта процедура? Ни за что — суть ее будет размазана по экранам из-за команд настройки
отрефакторим
procedure КомандыНастройкиДляТекущегоЭлемента()
begin
end
procedure КомандыНастройкиПередСравнением()
begin
end
procedure Swap(ItemIndex1, ItemIndex2)
begin
X := A[ItemIndex1];
A[ItemIndex1] := A[ItemIndex2];
A[ItemIndex2] := X
end;
procedure PopItem(CurrentIndex)
begin
for j := CurrentIndex to N do begin
КомандыНастройкиПередСравнением();
if A[CurrentIndex] < A[j] then
Swap(A[CurrentIndex], A[j])
end
end;
for i := 1 to N do begin
КомандыНастройкиДляТекущегоЭлемента();
PopItem(i)
end;
все стало просто и очевидно.
Нужно поменять возрастающую/убывающую последовательность? Не надо рыскать по коду и искать ключевой if среди десятков прочих — он у тебя сразу виден в процедуре PopItem. Выносим его в функцию Compare и делаем ее параметром или виртуальным членом класса. Получаем настройку поведения, которую ты так жаждешь
Здравствуйте, зиг, Вы писали:
зиг>Здравствуйте, chrisevans, Вы писали:
C>>Иногда даже самый простой рефакторинг может существенно упростить код. И не надо кидаться разбивать метод на кучу маленьких с целью уменьшить количество строк в методе. Лучше начать с выделения основной логики метода и вспомогательной (а если метод состоит из сотен строк кода, то 90% что вспомогательная логика есть). Вспомогательная логика это шаманства, вызванные особенностями библиотек/языков/фреймворков/любых API/кривых архитектур (и в своем коде и в стороннем), и зачастую именно они "утяжеляют" метод и затрудняют работу с основной логикой метода.
зиг>ну а что вот если вот эти шаманства все и так уже вынесены, и там именно идет алгоритм, описание его последовательности? зиг>если алгоритм состоит из N шагов, то все эти N шагов логично иметь в одном методе? зиг>А если нет, то по какой системе их разбить/объединить в несколько более крупных частей? если предположить, что эти действия самодостаточные, независимые и атомарные..? Если вот так вот разбить то начнет страдать читаемость, т.к. будет непонятно что делает метод firstPart содержащий первые 10 допустим шагов
Допустим, что ваш алгоритм получает файл и должен пропустить его контент через какие-то фильтры (регулярные выражения/проверки уровней доступа и т.п.) и выдать результат в какие-нибудь объекты. Вот эти фильтры вы можете давать алгоритму.
Простой пример из жизни: есть пылесос, у него есть труба, основное назначение которой — передача загрязненного воздуха к фильтру и дальше в мешок. Труба говорит: у меня есть круглое отверстие, на которое вы можете насадить устройство для доступа к источнику загрязнения. Трубе не нужно знать об углах, площади, форме ваших насадок, т.к. ее основное назначение — передать воздух. А представьте если бы на трубе были десятки кнопок для такой настройки, которые потребовали бы других материалов и конструкции трубы, которые не имеют отношения к ее основной деятельности — передаче воздуха.
Здравствуйте, зиг, Вы писали:
G>>Сначала разбить большой метод на маленькие, не более одного экрана размером. А потом уже делать параметры, которые процессинг настраивают.
зиг>эта разбивка уменьшит читаемость.
Три экрана было читабельным?
Разбиение на функции почти всегда делает код более читабельным. Ведь вместо горы кода появляется небольшой объем кода с внятными именами функций. Нужно только не полениться и продумать как само разбиение, так и имена функций.
А три экрана кода — это почти наверняка говнокод.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>А три экрана кода — это почти наверняка говнокод.
Не обязательно. В одном проекте специально сделали свитч на три экрана вместо Х вариантов возможной оптимизации.
Зато всем все понятно, все в одном месте и удобно для отладки. Также поступили и с верхним уровнем машины состояний.
Это я к тому, что обсуждать проблему абстрактно не совсем правильно.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, AlexNek, Вы писали:
AN>>Здравствуйте, VladD2, Вы писали:
VD>>>А три экрана кода — это почти наверняка говнокод. AN>>Не обязательно.
H>Вот имеено из-за таких как ты стоит слово почти.
Это бы сгодилось в начале дискуссии.
Сейчас я бы даже сказал, что там почти наверняка не говонокод
Здравствуйте, зиг, Вы писали:
зиг>Навставлять в A в метод process каких-то условий, чтобы вот эти вот отличия учитывались — не получится. Точнее, может и получится, но тогда код будет ужасный, он станет не 3 экрана, а 5, т.к. там вовлекутся структурные изменения (новые циклы добавятся, и т.д.). зиг>Можно просто в класс B скопипастить метод из A, провести все изменения и вуаля. Будем иметь 2 почти одинаковых класса, с общим алгоритмом. Измененяем в одном, забываем про второй, ну и т.д. прелести.
зиг>Я не пойму, как же красиво такое сделать??!
А вы уверены, что от нее всегда стоит избавляться любой ценой?
Проблема копипасты в том, что код дублируется и его становится сложнее поддерживать.
Если вы начнете только ради избавления от нее городить паттерны/наследование/шаблонные методы/стратегии и проче-прочее-прочее, то код станет сложным для понимания и, как следствие, его тоже будет непросто поддерживать.