Я читал Рефакторинг Мартина Фаулера . Как правило, это отлично, но одна из рекомендаций Фаулера, кажется, вызывает небольшие проблемы.
Фаулер рекомендует заменить временные переменные запросом, поэтому вместо этого:
double getPrice() {
final int basePrice = _quantity * _itemPrice;
final double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}
вы вытаскиваете в помощник метод:
double basePrice() {
return _quantity * _itemPrice;
}
double getPrice() {
final double discountFactor;
if (basePrice() > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice() * discountFactor;
}
В целом я согласен, за исключением того, что я использую временные переменные, когда строка слишком длинная. Например:
$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);
Если бы я попытался вставить это, строка была бы длиннее, чем 80 символов.
С другой стороны, я получаю цепочки кода, которые сами по себе не так легко читаются:
$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));
Каковы некоторые стратегии для примирения двух?
coding-style
refactoring
variables
Кевин Берк
источник
источник
$host
и$uri
пример немного надуманные - если только хост не читался из настроек или другого ввода, я бы предпочел, чтобы они были на одной строке, даже если они переносятся или выходят за границы.Ответы:
Как сделать
1. Существуют ограничения на длину строки, чтобы вы могли видеть + понимать больше кода. Они все еще в силе.
2. Подчеркните суждение о слепой конвенции .
3. Избегайте временных переменных, если не оптимизировать производительность .
4. Избегайте использования глубоких отступов для выравнивания в многострочных операторах.
5. Разбейте длинные операторы на несколько строк вдоль границ идеи :
Причины
Основным источником моих (отладочных) проблем с временными переменными является то, что они имеют тенденцию быть изменяемыми. А именно, я буду считать, что они представляют собой одно значение, когда я пишу код, но если функция сложная, какой-то другой фрагмент кода меняет свое состояние на полпути. (Или наоборот, где состояние переменной остается прежним, но результат запроса изменился).
Попробуйте придерживаться запросов, если вы не оптимизируете производительность . Это сохраняет любую логику, которую вы использовали для расчета этого значения в одном месте.
Оба приведенных вами примера (Java и ... PHP?) Допускают многострочные операторы. Если линии становятся длинными, разбейте их. Источник jquery доводит это до крайности. (Первое утверждение начинается со строки 69!) Не обязательно, что я с этим согласен, но есть другие способы сделать ваш код читабельным, чем использование временных переменных.
Некоторые примеры
1. Руководство по стилю PEP 8 для python (не самый красивый пример)
2. Пол М. Джонс из Руководства по стилю Pear (середина пути)
3. Длина строки Oracle + соглашения об упаковке (полезные стратагемы для сохранения до 80 символов)
4. MDN Java Practices (подчеркивает мнение программиста над соглашением)
источник
Я думаю, лучшим аргументом для использования вспомогательных методов вместо временных переменных является удобочитаемость. Если у вас, как у человека, больше проблем с чтением цепочки вспомогательных методов, чем с временными переменными, я не вижу причин, по которым вам следует их извлекать.
(поправьте меня, если я ошибаюсь)
источник
Я не думаю, что вам нужно строго следовать рекомендациям 80 символов или что когда-либо локальная переменная должна быть извлечена. Но длинные линии и локальные темпы должны быть исследованы для лучшего способа выражения той же идеи. По сути, они указывают на то, что данная функция или строка являются слишком сложными, и мы должны разбить их. Но мы должны быть осторожны, потому что плохое разбиение задачи на части только усложняет ситуацию. Таким образом, я должен разбить вещи на доступные и простые компоненты.
Позвольте мне взглянуть на примеры, которые вы опубликовали.
Мое наблюдение заключается в том, что все вызовы twilio api начинаются с "https://api.twilio.com/2010-04-1/", и поэтому существует очень очевидная функция многократного использования:
На самом деле, я считаю, что единственная причина для генерации URL-адреса - это сделать запрос, поэтому я бы сделал:
Фактически, многие URL начинаются с "Accounts / $ accountSid", поэтому я, вероятно, также извлеку это:
И если мы сделаем twilio api объектом, который содержит номер счета, мы можем сделать что-то вроде:
Использование объекта $ twilio позволяет упростить модульное тестирование. Я могу дать объекту другой объект $ twilio, который на самом деле не обращается к twilio, который будет быстрее и не будет делать странные вещи с twilio.
Давайте посмотрим на другой
Здесь я подумаю о любом из них:
или
или
В зависимости от того, какая идиома повторного использования.
источник
На самом деле, я не согласен с выдающимся мистером Фаулером по этому поводу в общем случае.
Преимущество извлечения метода из ранее встроенного кода заключается в повторном использовании кода; код в методе теперь отделен от первоначального использования и теперь может использоваться в других местах кода без копирования и вставки (что потребовало бы внесения изменений в нескольких местах, если бы когда-либо изменялась общая логика копируемого кода) ,
Однако равной, часто большей концептуальной ценностью является «повторное использование стоимости». Мистер Фаулер называет эти извлеченные методы для замены временных запросов «запросами». Ну, что более эффективно; запрашиваете базу данных каждый раз, когда вам нужно определенное значение, или запрашиваете один раз и сохраняете результат (предполагая, что значение достаточно статично, чтобы вы не ожидали его изменения)?
Практически для любого вычисления, кроме относительно тривиального в вашем примере, на большинстве языков дешевле хранить результат одного вычисления, чем продолжать его вычислять. Поэтому общая рекомендация по пересчету по требованию является неискренней; это стоит больше времени разработчика и больше процессорного времени и экономит тривиальный объем памяти, который в большинстве современных систем является самым дешевым ресурсом из этих трех.
Теперь вспомогательный метод в сочетании с другим кодом можно сделать «ленивым». При первом запуске он будет инициализировать переменную. Все последующие вызовы будут возвращать эту переменную до тех пор, пока метод не получит явное указание для пересчета. Это может быть параметр метода или флаг, установленный другим кодом, который изменяет любое значение, от которого зависит вычисление этого метода:
Теперь для этого тривиального вычисления выполнено даже больше работы, чем сохранено, и поэтому я обычно рекомендую придерживаться переменной temp; однако для более сложных вычислений, которые вы, как правило, хотите избегать многократного выполнения и которые вам нужны в нескольких местах в коде, именно так вы и сделаете.
источник
У вспомогательных методов есть место, но вы должны быть осторожны с обеспечением согласованности данных и ненужного увеличения объема переменных.
Например, ваш собственный пример цитирует:
Ясно, что оба
_quantity
и_itemPrice
являются глобальными переменными (или, по крайней мере, на уровне класса), и поэтому существует возможность их изменения за пределамиgetPrice()
Поэтому существует вероятность того, что первый вызов
basePrice()
вернет значение, отличное от второго вызова!Поэтому я хотел бы предположить, что вспомогательные функции могут быть полезны для выделения сложной математики, но в качестве замены локальных переменных вам нужно быть осторожным.
Вы также должны избегать сокращения до абсурда - следует ли рассчитывать
discountFactor
расчет на метод? Итак, ваш пример становится:Разделение за пределы определенного уровня фактически делает код менее читабельным.
источник
Если вы работаете на языке с именованными параметрами (ObjectiveC, Python, Ruby и т. Д.), Временные переменные будут менее полезны.
Однако в вашем примере basePrice выполнение запроса может занять некоторое время, и вы можете сохранить результат во временной переменной для будущего использования.
Как и вы, я использую временные переменные для ясности и соображений длины строки.
Я также видел, как программисты делают следующее в PHP. Это интересно и отлично подходит для отладки, но немного странно.
источник
Обоснование этой рекомендации заключается в том, что вы хотите иметь возможность использовать те же предварительные вычисления в других местах вашего приложения. Смотрите Заменить Temp на Запрос в каталоге шаблонов рефакторинга:
Таким образом, в вашем примере с хостом и URI я бы применил эту рекомендацию только в том случае, если планирую повторно использовать тот же URI или определение хоста.
Если это так, то из-за пространства имен я не буду определять глобальный метод uri () или host (), но буду называть имя с дополнительной информацией, например twilio_host () или archive_request_uri ().
Затем, для проблемы длины строки, я вижу несколько вариантов:
uri = archive_request_uri()
.Обоснование: в текущем методе вы хотите, чтобы URI был упомянутым. Определение URI все еще разложено.
uri() { return archive_request_uri() }
Если вы часто используете рекомендации Фаулера, вы знаете, что метод uri () - это тот же шаблон.
Если из-за выбора языка вам необходим доступ к локальному методу с помощью «self», я бы порекомендовал первое решение для большей выразительности (в Python я бы определил функцию uri внутри текущего метода).
источник