Сейчас трудно вспомнить, когда я впервые осознал, что извлечение функций из больших кусков полезного кода на самом деле было хорошей идеей.
Получил ли я эти знания от «Идеальный код» , либо из «Чистый код» - трудно запомнить.
В целом это не особо важно.
Мы все знаем, что нам следует разделить бизнес-логику на хорошо названные функции.
Самый длинный полнометражный фильм, который я когда-либо снимал на видео, составлял 5 тысяч строк.
Я лично знаю «программиста», написавшего этот код. Помню, как впервые столкнулся с этой функцией.
Нетрудно предсказать, что моей первой реакцией было: «Какого черта!!! Кто родил эту хрень??Э» Да, представьте себе, этот «программист» до сих пор торчит здесь, в офисе, где я сейчас работаю над текущими проектами.
Я не хочу вдаваться в эту историю, но хочу упомянуть, что эта функция длиной в 5 тысяч строк была ядром программы длиной около 150 тысяч строк.
Разработка программы в конечном итоге зашла в тупик из-за ужасной особенности, весьма негативно влиявшей на архитектуру приложения.
В итоге было принято решение переписать приложение с нуля.
Эта история иллюстрирует одну крайность проблемы размера функции, которая привела к катастрофическим последствиям.
Другая крайность — отключить мозг и начать везде распределять классы с однострочными функциями внутри.
Я не имею в виду, что такие функции плохие, я говорю, что не стоит забывать использовать силу своего мозга.
Сначала нужно проанализировать проблему.
Прежде чем углубляться в этот вопрос, я хотел бы отметить, что, вообще говоря, некоторое время назад между дядей Бобом и Кристиной Горман произошла небольшая стычка на эту тему.
дядя Боб представил технику, которую он назвал «Извлекай до упаду».
, что вкратце означает — извлекать функции до тех пор, пока есть что извлекать.
Кристин Горман чувствовал, что эта техника исключает использование мозга .
Кроме того, был Сообщение Джона Сонмеза о рефакторинге одной функции из .
NET BCL (хотя изначальной целью статьи было показать, что большинство комментариев — зло).
Давайте посмотрим на пример рефакторинга Джона.
В качестве примера он взял следующий метод:
Чтобы облегчить чтение этого кода, Джон создал новый класс с переработанным исходным методом внутри него.internal static void SplitDirectoryFile( string path, out string directory, out string file) { directory = null; file = null; // assumes a validated full path if (path != null) { int length = path.Length; int rootLength = GetRootLength(path); // ignore a trailing slash if (length > rootLength && EndsInDirectorySeparator(path)) length--; // find the pivot index between end of string and root for (int pivot = length - 1; pivot >= rootLength; pivot--) { if (IsDirectorySeparator(path[pivot])) { directory = path.Substring(0, pivot); file = path.Substring(pivot + 1, length - pivot - 1); return; } } // no pivot, return just the trimmed directory directory = path.Substring(0, length); } return; }
Вот что он получил: public class DirectoryFileSplitter
{
private readonly string validatedFullPath;
private int length;
private int rootLength;
private bool pivotFound;
public string Directory { get; set; }
public string File { get; set; }
public DirectoryFileSplitter(string validatedFullPath)
{
this.validatedFullPath = validatedFullPath;
length = validatedFullPath.Length;
rootLength = GetRootLength(validatedFullPath);
}
public void Split()
{
if (validatedFullPath != null)
{
IgnoreTrailingSlash();
FindPivotIndexBetweenEndOfStringAndRoot();
if(!pivotFound)
TrimDirectory();
}
}
private void TrimDirectory()
{
Directory = validatedFullPath.Substring(0, length);
}
private void FindPivotIndexBetweenEndOfStringAndRoot()
{
for (int pivot = length - 1; pivot >= rootLength; pivot--)
{
if (IsDirectorySeparator(validatedFullPath[pivot]))
{
Directory = validatedFullPath.Substring(0, pivot);
File = validatedFullPath.Substring(pivot + 1, length - pivot - 1);
pivotFound = true;
}
}
}
private void IgnoreTrailingSlash()
{
if (length > rootLength && EndsInDirectorySeparator(validatedFullPath))
length--;
}
}
Вау, да? Не так-то просто решить, действительно ли рефакторинг помог сделать код более читабельным.
Ощущение такое, что на самом деле читать стало сложнее.
Раньше была сравнительно небольшая функция с полезными комментариями, которая теперь превратилась в класс с четырьмя функциями внутри без комментариев.
Я бы не сказал, что новый класс плохой и весь рефакторинг был плохой идеей, и программист, который делал рефакторинг, должен быть казнен.
Нисколько.
Я не такой кровожадный.
Между этими двумя примерами кода есть несколько различий.
Давайте посмотрим на эти различия:
- Если вы пытаетесь получить глубокое понимание того, что делает функция верхнего уровня, эту функцию становится труднее читать, чем было изначально, потому что теперь вам придется просматривать все функции и понимать, что происходит в каждой из них.
Напротив, первоначальную версию можно легко просмотреть.
- Если вы пытаетесь понять, что концептуально делает функция верхнего уровня, то рефакторинговую версию легче читать, потому что мы можем сразу увидеть, что концептуально делает функция внутри себя.
- Третье отличие, которое я вижу, — это стоимость поддержки.
Что касается нашего конкретного примера, то я бы сказал, что стоимость поддержания рефакторинговой версии выше исходной (нужно как минимум рефакторить).
В общем, ответ на вопрос, какой вариант дороже поддерживать, кроется в требованиях.
Эти требования определяют, важно ли в конкретной ситуации следовать SRP (принципу единой ответственности) или нет. Если эту функцию можно написать один раз и забыть навеки, то нет смысла тратить время на ее рефакторинг.
Напротив, если ожидается рост функциональности, то у вас есть все основания провести рефакторинг функции в отдельный класс.
Вы сразу броситесь извлекать класс с четырьмя функциями внутри? Мой совет: не делайте этого без какой-либо причины, даже если ваша кодовая база имеет 100% тестовое покрытие.
Почему? Потому что технического долга здесь нет. Я говорю о серьёзном техническом долге, который причиняет страдания.
Так что нет ничего плохого в методе «извлекать до упаду».
По моему мнению, вам просто нужно иметь в виду некоторые соображения.
Подводя итог, хочу сказать, что никогда не следует делать бессмысленных вещей.
Надо сначала подумать, проанализировать, сделать вывод и только потом действовать.
Теги: #рефакторинг #программирование #.
NET #дизайн и рефакторинг
-
Заправка Чернил: Часто Задаваемые Вопросы
19 Oct, 24 -
Тривертинг: Вариант Применения
19 Oct, 24 -
Errrr — Простейший Регистратор Ошибок
19 Oct, 24 -
Оптимизация Затрат С Помощью Amazon S3
19 Oct, 24