Другие части эпической саги о вреде дублирования:
Наиболее очевидный пример дублирующегося кода - дублирующееся поведение. Время от времени мы встречаем абсолютно одинаковые куски кода, разбросанные по приложению. Да, именно по приложению - дублирование может наблюдаться как в пределах одного метода, так и на разных слоях или в разных модулях.
Почему мы видим одинаковый код несколько раз? Потому что для системы именно эта последовательность действий имеет определённое значение, этот код несёт в себе единицу знаний. Хочешь написать что-то ещё раз? Задумайся почему. Небольшой пример - начисление зарплаты в штатах:
В нашем случае дважды встречается выражение _hourlyPayRate * hoursWorked. Первым делом надо понять, что оно значит (а оно определённо что-то значит, раз нам пришлось написать одно и то же дважды). Это ни что иное, как вычисление начисленной заработной платы. Прямо бинго! Теперь надо избавиться от дублирования этих знаний, потому что даже в таком простом случае ваш коллега, добавляя к зарплате рождественский бонус, может забыть изменить одну из копий.
Бороться с дублированием не сложно. Если одинаковый код встречается в пределах одного метода, можно применить рефакторинг Introduce Explaining Variable:
Когда одинаковый код встречается в нескольких методах одного класса, либо он объёмен, помогает рефакторинг Extract Method:
Теперь, когда все знания о расчёте начисленной зарплаты сосредоточены в одном месте, очень просто добавить учёт часов переработок, рождественский бонус и т.п.
Код может дублироваться не только в пределах одного класса, но и среди нескольких классов, порой даже совершенно независимых. В этом случае на помощь приходят рефакторинги Extract Superclass и Extract Class.
Например, мне довольно быстро надоедает у каждого класса в домене создавать поле _id. Принцип DRY указывает на то, что это поле имеет одинаковый смысл в рамках системы. И действительно - это Identity Field - поле, хранящее идентификатор записи базы данных для поддержки соответствия между объектом и строкой базы данных. Поэтому я выделяю Layer Supertype - базовый класс для всех объектов слоя. И выношу в него код, дублирующийся у всех объектов этого слоя:
- Вступление
- Дублирование поведения
- Дублирование логики создания
- Дублирование условной логики
- Дублирование данных
Наиболее очевидный пример дублирующегося кода - дублирующееся поведение. Время от времени мы встречаем абсолютно одинаковые куски кода, разбросанные по приложению. Да, именно по приложению - дублирование может наблюдаться как в пределах одного метода, так и на разных слоях или в разных модулях.
Почему мы видим одинаковый код несколько раз? Потому что для системы именно эта последовательность действий имеет определённое значение, этот код несёт в себе единицу знаний. Хочешь написать что-то ещё раз? Задумайся почему. Небольшой пример - начисление зарплаты в штатах:
public class Employee
{
private int _id;
private decimal _hourlyPayRate;
private decimal _federalTax;
private decimal _stateTax;
public decimal CalculatePayment(int hoursWorked)
{
return _hourlyPayRate * hoursWorked
- _federalTax - _stateTax
- _hourlyPayRate * hoursWorked * 7.65M / 100;
}
// etc.
}
В нашем случае дважды встречается выражение _hourlyPayRate * hoursWorked. Первым делом надо понять, что оно значит (а оно определённо что-то значит, раз нам пришлось написать одно и то же дважды). Это ни что иное, как вычисление начисленной заработной платы. Прямо бинго! Теперь надо избавиться от дублирования этих знаний, потому что даже в таком простом случае ваш коллега, добавляя к зарплате рождественский бонус, может забыть изменить одну из копий.
Бороться с дублированием не сложно. Если одинаковый код встречается в пределах одного метода, можно применить рефакторинг Introduce Explaining Variable:
public decimal CalculatePayment(int hoursWorked)
{
decimal grossPayment = _hourlyPayRate * hoursWorked;
return grossPayment - _federalTax - _stateTax
- grossPayment * 7.65M / 100;
}
Когда одинаковый код встречается в нескольких методах одного класса, либо он объёмен, помогает рефакторинг Extract Method:
public decimal CalculatePayment(int hoursWorked)
{
decimal grossPayment = CalculateGrossPayment(hoursWorked);
return grossPayment - _federalTax - _stateTax
- grossPayment * 7.65M / 100;
}
private decimal CalculateGrossPayment(int hoursWorked)
{
int workHoursInWeek = 40;
int hoursOvertime = hoursWorked - workHoursInWeek;
decimal result = hoursWorked * _hourlyPayRate;
if (hoursOvertime > 0)
result += hoursOvertime * _hourlyPayRate * 1.5M;
if (DateTime.Today.Month == 12)
result += _christmasBonus;
return result;
}
Теперь, когда все знания о расчёте начисленной зарплаты сосредоточены в одном месте, очень просто добавить учёт часов переработок, рождественский бонус и т.п.
Код может дублироваться не только в пределах одного класса, но и среди нескольких классов, порой даже совершенно независимых. В этом случае на помощь приходят рефакторинги Extract Superclass и Extract Class.
Например, мне довольно быстро надоедает у каждого класса в домене создавать поле _id. Принцип DRY указывает на то, что это поле имеет одинаковый смысл в рамках системы. И действительно - это Identity Field - поле, хранящее идентификатор записи базы данных для поддержки соответствия между объектом и строкой базы данных. Поэтому я выделяю Layer Supertype - базовый класс для всех объектов слоя. И выношу в него код, дублирующийся у всех объектов этого слоя:
public abstract class Entity<TId>
{
private readonly TId _id;
protected Entity(TId id)
{
_id = id;
}
public TId Id
{
get { return _id; }
}
// other common behavior
}
public class Employee : Entity<int>
{
// keep in mind - id is not here anymore
private decimal _hourlyPayRate;
private decimal _federalTax;
private decimal _stateTax;
// etc.
}
Комментариев нет:
Отправить комментарий