:notebook: Эта статья была заархивирована в: "blog"
Переведено с: https://sourcemaking.com/refactoring/smells/dispensables
Dispensables Эта группа неприятных запахов означает, что такой код является необязательным, и его наличие влияет на чистоту и читабельность всего кода.
- Класс резервирования
- риторика о будущем
- Наивный класс данных
- слишком много комментариев
- повторяющийся код
- Расширенное чтение
- использованная литература
Класс резервирования
Избыточность (ленивый класс)
Понимание и поддержание всегда потребляют трудоемкость и трудоемкий. Если класс не стоит ваших усилий, его следует удалить.
проблема вызывает
Возможно, класс изначально задумывался как полностью функциональный класс, но по мере изменения кода он становится бесполезным. Или, может быть, класс изначально был разработан для поддержки будущих функциональных расширений, но никогда не использовался.
Решение
- Бесполезные классы для использования
将类内联化(Inline Class)
убить.
- Если подклассы бесполезны, попробуйте
折叠继承体系(Collapse Hierarchy)
.
доход
- Уменьшите количество кода
- легко поддерживать
когда игнорировать
- Иногда создаются избыточные классы для описания намерений будущей разработки. В этом случае постарайтесь сохранить баланс между ясностью и простотой кода.
Описание метода рефакторинга
Встроенный класс (встроенный класс)
проблема
Определенный класс мало что делает.
решать
Переместите все свойства этого класса в другой класс, а затем удалите исходный класс.
Свернуть иерархию
проблема
Нет большой разницы между суперклассом и подклассами.
решать
Соберите их вместе.
риторика о будущем
Спекулятивная общность
Есть неиспользуемый класс, функция, поле или параметр.
проблема вызывает
Иногда код просто для поддержки будущих функций нужно производить, но это не реализовано. В результате код становится трудным для понимания и поддержки.
Решение
- Если один из ваших абстрактных классов не очень полезен, используйте
折叠继承体系(Collapse Hierarch)
.
- Можно использовать ненужное делегирование
将类内联化(Inline Class)
Устранить. - Можно использовать бесполезные функции
内联函数(Inline Method)
Устранить. - Независимые параметры в функции должны использоваться
移除参数(Remove Parameter)
Устранить. - Бесполезные поля можно удалить напрямую.
доход
- Уменьшите количество кода.
- Легче поддерживать.
когда игнорировать
- Если вы работаете над структурой для создания самой функциональной структуры, не используется, это очень разумно, до тех пор, пока пользовательские кадры нуждаются в этой функции.
- Перед удалением элементов убедитесь, что они не используются в модульных тестах. Это может произойти, если тесту необходимо получить некоторую внутреннюю информацию от класса или выполнить специальные операции, связанные с тестом.
Описание метода рефакторинга
Свернуть иерархию
проблема
Между надклассом и подклассом нет большой разницы.
решать
Соберите их вместе.
Встроить класс (Inline Class)
проблема
Определенный класс мало что делает.
решать
Переместите все свойства этого класса в другой класс, а затем удалите исходный класс.
Встроенный метод
проблема
Функция тела более четкие и понятно, чем имя функции.
class PizzaDelivery {
//...
int getRating() {
return moreThanFiveLateDeliveries() ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return numberOfLateDeliveries > 5;
}
}
решать
Вставьте тело функции в место вызова функции, затем удалите функцию.
class PizzaDelivery {
//...
int getRating() {
return numberOfLateDeliveries > 5 ? 2 : 1;
}
}
Удалить параметр
проблема
Тело функции больше не требует определенного параметра.
решать
Параметры удаления.
Наивный класс данных
纯稚的数据类(Data Class)
Относится к классам, которые содержат только поля и функции получения и установки для доступа к ним. Это просто контейнеры данных для использования другими классами. Эти классы не содержат никакой дополнительной функциональности и не могут выполнять независимые операции над данными, которыми они владеют.
проблема вызывает
Это нормально, когда вновь созданный класс содержит всего несколько публичных полей (возможно, даже несколько геттеров/сеттеров). Но реальная сила объектов заключается в том, что они могут содержать типы поведения или операции, воздействующие на данные.
Решение
- Если в классе есть общедоступные поля, вы должны использовать
封装字段(Encapsulated Field)
Прямой доступ к скрытому полю. - Если класс поля контейнера классов, то следует проверить, что они неправильно приняты пакетом; если нет, то использование
封装集合(Encapsulated Collection)
Инкапсулируйте их. - Узнайте, где эти функции получения/установки используются другими классами. попробуй с
搬移函数(Move Method)
переместите эти вызовы поведения в纯稚的数据类(Data Class)
Приходить. Если функцию нельзя переместить, используйте提炼函数(Extract Method)
Генерирует переносную функцию.
- После того, как класс будет наполнен хорошо продуманными функциями, вы можете захотеть избавиться от старого метода доступа к данным, чтобы предоставить широкий спектр интерфейсов доступа к данным. С этой целью вы можете использовать
移除设置函数(Remove Setting Method)
и隐藏函数(Hide Method)
.
доход
- Улучшение читабельности и организации кода. Операции с конкретными данными теперь сосредоточены в одном месте, а не разбросаны по всему коду.
- Помогает обнаружить дубликаты в клиентском коде.
Описание метода рефакторинга
Инкапсулированное поле
проблема
В ваших классах есть публичные поля.
class Person {
public String name;
}
решать
Объявите его закрытым и предоставьте соответствующую функцию доступа.
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String arg) {
name = arg;
}
}
Инкапсулированная коллекция
проблема
Есть функция, которая возвращает коллекцию.
решать
Пусть функция возвращает копию считывания, только для сбора коллекции, и предоставляет функции в этом классе, чтобы добавить и удалить элементы коллекции.
Метод перемещения
проблема
В вашей программе есть функция, которая больше взаимодействует с другим классом, чем с тем, в котором она находится: вызывает последний или вызывается последним.
решать
Создайте новую функцию с аналогичным поведением в классе, на который функция чаще всего ссылается. Превратите старую функцию в чистую функцию делегата или полностью удалите старую функцию.
Метод извлечения
проблема
У вас есть код для организации.
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
решать
Переместите этот код в новую функцию, заменив старый код вызовом функции.
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Удалить метод настройки
проблема
Поле в классе должно быть установлено, когда объект создан и никогда не изменяется.
решать
Удаляет все функции установки для этого поля.
Скрыть метод
проблема
Есть функция, которая никогда не используется ни одним другим классом.
решать
Измените эту функцию на частную.
слишком много комментариев
Слишком много комментариев (Комментариев)
Сами по себе аннотации не так уж плохи. Но часто бывает так, что длинный комментарий появляется в куске кода, и он существует потому, что код плохой.
проблема вызывает
Авторы комментариев понимают, что их код не является интуитивным или очевидным, и хотят использовать комментарии, чтобы прояснить свои намерения. В этом случае комментарии подобны дезодоранту для плохого кода.
Лучший комментарий — дать функции или классу подходящее имя.
Если вам кажется, что фрагмент кода непонятен без комментариев, попробуйте сначала провести рефакторинг, чтобы попытаться сделать все комментарии излишними.
Решение
- Если комментарий должен объяснять сложное выражение, его можно использовать
提炼变量(Extract Variable)
Разделите выражение на понятные подвыражения. - Если вам нужны комментарии, чтобы объяснить, что делает фрагмент кода, попробуйте
提炼函数(Extract Method)
. - Если функция была доработана, но вам все еще нужно аннотировать, что она делает, попробуйте использовать
函数改名(Rename Method)
чтобы дать функции понятное имя. - Если вам нужно подтвердить состояние системы, используйте
引入断言(Introduce Assertion)
.
доход
- Код становится более интуитивным и очевидным.
когда игнорировать
Комментарии иногда полезны:
- При объяснении, почему что-то должно быть реализовано определенным образом.
- При объяснении какого-то сложного алгоритма.
- Когда ты действительно не знаешь, что делать.
Описание метода рефакторинга
Извлечь переменную
проблема
У тебя непонятное выражение лица.
void renderBanner() {
if ((platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0 )
{
// do something
}
}
решать
Замените результат выражения или его подвыражения не требующими пояснений переменными.
void renderBanner() {
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIE && wasInitialized() && wasResized) {
// do something
}
}
Метод извлечения
проблема
У вас есть кусок кода, который можно сгруппировать вместе.
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
решать
Переместите этот код в новую функцию, заменив старый код вызовом функции.
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Метод переименования
проблема
Имя функции не раскрывает должным образом назначение функции.
class Person {
public String getsnm();
}
решать
Изменить имя функции.
class Person {
public String getSecondName();
}
Ввести утверждение
проблема
Определенный фрагмент кода должен делать определенные предположения о состоянии программы.
double getExpenseLimit() {
// should have either expense limit or a primary project
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
решать
Выразите это предположение явно с помощью утверждения.
double getExpenseLimit() {
Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
Примечание. Пожалуйста, не злоупотребляйте утверждениями. Не используйте его для проверки условий «должно быть истинным», используйте его только для проверки условий «должно быть истинным». На самом деле утверждения — это скорее средство для самопроверки вашего кода. Все утверждения, как правило, исключаются, когда продукт действительно поставляется.
повторяющийся код
Дублирующий код
Дублированный код — один из худших запахов кода. Устранение повторяющегося кода всегда выгодно.
проблема вызывает
Дублированный код обычно возникает, когда несколько программистов одновременно работают над разными частями одной и той же программы. Поскольку они работают над разными задачами, они могут не знать, что их коллеги написали аналогичный код.
Существует также более непонятная форма дублирования, когда определенные части кода выглядят по-разному, но на самом деле делают одно и то же. Этот повторяющийся код часто трудно найти и устранить.
Иногда повторение является целенаправленным. Когда сроки стремятся уложиться, а существующий код «почти соответствует» поставленной задаче, начинающие программисты могут не устоять перед искушением скопировать и вставить соответствующий код. В некоторых случаях программисты просто слишком ленивы.
Решение
- Если две функции одного класса содержат одно и то же выражение, вы можете использовать
提炼函数(Extract Method)
Извлеките дублированный код, и оба местоположения вызовут извлеченный код.
- Если два подкласса, являющиеся родственными друг другу, имеют повторяющийся код:
- Сначала используйте оба класса
提炼函数(Extract Method)
, а затем примените его к извлеченной функции函数上移(Pull Up Method)
, поместите его в суперкласс. - Если повторный код находится в конструкторе, используйте
构造函数本体上移(Pull Up Constructor Body)
. - Если повторяющийся код только похож, но не идентичен, используйте
塑造模板函数(Form Template Method)
получить одинPattern Method Pattern (Шаблонный метод). - Если некоторые функции делают одно и то же с разными алгоритмами, вы можете выбрать более понятный и использовать
替换算法(Substitute Algorithm)
Замените алгоритмы других функций.
- Сначала используйте оба класса
- Если в двух несвязанных классах есть повторяющийся код:
- Пожалуйста, попробуйте использовать
提炼超类(Extract Superclass)
, чтобы создать суперкласс для тех классов, которые поддерживают все предыдущие функции. - Если создать суперкласс сложно, вы можете использовать его в классе
提炼类(Extract Class)
и использовать этот новый компонент в другом классе.
- Пожалуйста, попробуйте использовать
- Если условных выражений много, и они выполняют один и тот же код (разные только условия), можно использовать
合并条件表达式(Consolidate Conditional Expression)
Эти условия объединяются в единую операцию, а использование提炼函数(Extract Method)
Поместите условие в отдельную функцию с понятным названием. - Если все ветки условного выражения имеют частично идентичные фрагменты кода: можно использовать
合并重复的条件片段(Consolidate Duplicate Conditional Fragments)
Поместите фрагмент кода там, где они оба существуют вне условного выражения.
доход
- Объединение повторяющегося кода упрощает структуру кода и уменьшает объем кода.
- Код проще и его легче поддерживать.
Описание метода рефакторинга
Метод извлечения
проблема
У вас есть фрагмент кода, который можно сгруппировать.
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
решать
Переместите этот код в новую функцию, заменив старый код вызовом функции.
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Метод подтягивания
проблема
Некоторые функции дают один и тот же результат в подклассах.
решать
Переместите функцию в суперкласс.
Подтяните тело конструктора
проблема
У вас есть несколько конструкторов в разных подклассах, и их тела почти идентичны.
class Manager extends Employee {
public Manager(String name, String id, int grade) {
this.name = name;
this.id = id;
this.grade = grade;
}
//...
}
решать
Создайте новый конструктор в суперклассе и вызовите его в конструкторе подкласса.
class Manager extends Employee {
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
//...
}
Метод шаблона формы
проблема
У вас есть подклассы, в которых некоторые соответствующие функции выполняют аналогичные операции в одном и том же порядке, но различаются деталями каждой операции.
решать
Поместите эти операции в отдельные функции и сохраните их все с одной и той же сигнатурой, чтобы исходная функция стала одинаковой. Затем переместите исходную функцию в суперкласс.
Примечание: здесь упоминаются только конкретные практики, рекомендуется изучить шаблон проектирования метода шаблона.
Алгоритм замены
проблема
Вы хотите заменить алгоритм с другим чистым алгоритмом.
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals("Don")){
return "Don";
}
if (people[i].equals("John")){
return "John";
}
if (people[i].equals("Kent")){
return "Kent";
}
}
return "";
}
решать
Замените тело функции другим алгоритмом.
String foundPerson(String[] people){
List candidates =
Arrays.asList(new String[] {"Don", "John", "Kent"});
for (int i=0; i < people.length; i++) {
if (candidates.contains(people[i])) {
return people[i];
}
}
return "";
}
Извлечь суперкласс
проблема
Оба класса имеют схожие свойства.
решать
Создайте суперкласс для классов и переместите те же свойства для суперкласса.
Извлечь класс
проблема
Класс делает больше, чем одну вещь.
решать
Создайте новый класс и переместите соответствующие поля и функции из старого класса в новый класс.
Объединить условное выражение
проблема
У вас есть ряд условных ветвей, все с одним и тем же результатом.
double disabilityAmount() {
if (seniority < 2) {
return 0;
}
if (monthsDisabled > 12) {
return 0;
}
if (isPartTime) {
return 0;
}
// compute the disability amount
//...
}
решать
Объедините эти условные ветви в одно условие и переведите это условие в одну функцию.
double disabilityAmount() {
if (isNotEligableForDisability()) {
return 0;
}
// compute the disability amount
//...
}
Консолидация повторяющихся условных фрагментов
проблема
В каждой ветви условного выражения есть один и тот же фрагмент кода.
if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}
решать
Переместите это дублирование в условное выражение.
if (isSpecialDeal()) {
total = price * 0.95;
}
else {
total = price * 0.98;
}
send();
Расширенное чтение
- Плохой запах кода и рефакторинг
- Раздувание кода с неприятным запахом кода
- Злоупотребление объектно-ориентированным кодом
- Барьеры на пути к изменениям: плохой запах кода
- Ненужный запах плохого кода
- Сочетание запахов плохого кода
использованная литература
- Рефакторинг — улучшение дизайна существующего кода - by Martin Fowler
- https://sourcemaking.com/refactoring