Зачем нужен рефакторинг

Продолжение. Часть 1. Вводная. Можно не читать =)

Продолжаю рассуждения на тему рефакторинга, красивого и рабочего кода.
Самое распространенное объяснение этому – сделать код программы более понятный для чтения, понимания. Я не хочу копировать википедию или другие ресурсы, поэтому опишу именно свое видение данной «проблеме».

Разберем случаи для рефакторинга.

Вариант 1) Мы продумали модель ПС, разработали его архитектуру, в голове (или на бумаге) расставили все точки над i, написали весьма неплохой программный код, как вдруг – нужен дополнительный функционал, который, ну никак не вкладывается в архитектурный проект нашего проекта. Что необходимо делать в этом случае. Переписывать код – слишком дорого. Поэтому логично будет воспользоваться рефакторингом.
Рефакторинг позволяет изменить архитектурное устройства ПС, не затрагивая его функциональность. Другими словами, рефакторинг – это начальная стадия перед изменением функциональности – (реинжиниринг)

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

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

Во многих источниках принимается, что метод длиной выше, чем 50 строк лучше разбить на несколько. В большинстве случаев я с этим соглашусь, но прежде чем бездумно бежать и править, править, править, на мой взгляд, логичным будет бросить взгляд – другой а лучше позвать знакомого, который сведущ в программировании и подумать: «А может быть данный метод и так понятен и дробление на мелкие методы его только усложнит?». Такое бывает крайне редко, но тем не менее имеет место быть.

Может быть и обратная ситуация – имеется 2 или больше мелких методов, вызов к которым происходит только через определенный один метод. В итоге мы получаем просто вложенную цепочку из 2-3-N методов. Опять же, ИМХО, это может усложнить читабельность кода, по причине неиспользования последних нигде, кроме этой цепочке. (опять же, здесь требуется проследить, чтобы объединение этих методов не усложнило читабельность программы)

В разработке можно столкнуться с одной ужасной вещью (которую, насколько я понимаю, обычно наследуют с языков типа Pascal) – перечисление переменных в начале метода. Для языков C#, C++, JS, Java и ряда других – это тихий ужас… согласитесь смотреть на это без слез нельзя –
Пример с головы и не несет смысловой нагрузки, за исключением демонстрации для чего можно использовать рефакторинг

  1. int ACount,BCount,n,i,j,index;//написал мало.. обычно больше на порядок
  2. //Работа только с ACount и BCount
  3. int[] AArray = new int[ACount];
  4. int[] BArray = new int[BCount];
  5. //долгая работа с переменной AArray, с использованием i,j, index,n ,ACount
  6. n = BCount;
  7. //Наконец-то обработка массива BArray уж с использованием остальных переменных.
* This source code was highlighted with Source Code Highlighter.


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

  1. int ACount,BCount;
  2. //Работа только с ACount и BCount
  3. int[] AArray = new int[ACount];
  4. int n,i,j,index;
  5. //долгая работа с переменной AArray, с использованием i,j, index,n ,ACount
  6. n = BCount;
  7. int[] BArray = new int[BCount];
  8. //Наконец-то обработка массива BArray уж с использованием остальных переменных.
* This source code was highlighted with Source Code Highlighter.

Да, согласен – это пример очень «надуманный», но тем не менее основную идею он продемонстрировал.

Каким еще не должен быть метод?

Конечно же повторения строк это не удобно…
К примеру, мы решили изобрести велосипеднаписать сортировку массивов, причем в программе необходимо будет сортировать много разнотипных массивов… (int, char, и так далее… )
Здесь также лучше воспользоваться рефакторингом и создать универсальный метод, который будет обрабатывать эти классы. Как будет выглядеть такой метод, мы посмотрим в третьей части – рефакторинг на примерах.

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

Да, согласен, иногда использование маленьких классов (1-2 метода) бывает оправданным, но не стоит этим увлекаться, также как и писать классы-монстры – эдакие комбайны, с функцией просмотра TV и печатью текста.

Классы должны реализовывать 1 сущность. 1 функциональная задача на класс. Как правило, мелкие классы (1-2 метода) являются классами – хелперами.
Вот вроде бы и все, о чем я хотел поговорить с Вами по поводу рефакторинга.
Буду рад Вашим замечанием и дополнениям. (Ведь я далеко не все затронул по поводу предмета – рефакторинга ;)
2012-09-19