Полезный принцип, он утвержает, что чтение и модификация чего либо должны разделяться, например:
public Salary PaidSalary()
{
var currentMonth = DateTime.Now.Month;
decimal result = 0;
if (currentMonth < 1)
result += January.Value;
if (currentMonth < 2)
result += February.Value;
if (currentMonth < 3)
result += April.Value;
if (currentMonth < 4)
result += May.Value;
if (currentMonth < 5)
result += June.Value;
if (currentMonth < 6)
result += July.Value;
if (currentMonth < 7)
result += August.Value;
if (currentMonth < 8)
result += September.Value;
if (currentMonth < 9)
result += October.Value;
if (currentMonth < 10)
result += November.Value;
if (currentMonth < 11)
result += December.Value;
IsPaidSalary = true;
return new Salary(result);
}
Метод Paid Salary считает зарплату и изменяет переменную IsPaidSalary - проще разделить эти операции:
public Salary PaidSalary()
{
IsPaidSalary = true;
return GetSalary();
}
На самом деле код до этого не такой уж и плохой, в операции записи допустимо делать операции чтения, а наоборот - нет:
public Salary GetSalary()
{
var currentMonth = DateTime.Now.Month;
decimal result = 0;
if (currentMonth < 1)
result += January.Value;
if (currentMonth < 2)
result += February.Value;
if (currentMonth < 3)
result += April.Value;
if (currentMonth < 4)
result += May.Value;
if (currentMonth < 5)
result += June.Value;
if (currentMonth < 6)
result += July.Value;
if (currentMonth < 7)
result += August.Value;
if (currentMonth < 8)
result += September.Value;
if (currentMonth < 9)
result += October.Value;
if (currentMonth < 10)
result += November.Value;
if (currentMonth < 11)
result += December.Value;
IsPaidSalary = true;
return new Salary(result);
}
Во-первых вызов IsPaidSalary смотрится просто не корректно в get методе(на его месте может быть другой вызов), но даже если вызов был к месту, мы теряем возможность закешировать результат, потому что как только мы это сделаем сразу нарушится работа системы, это плохо, несмотря на то, что она достигается такими средствами
Если есть несколько методов выполняющий одно и то же, но с разными константами, например:
public Salary GetTotalSalaryForFirstThreeMonths()
=> new Salary(SalaryByLastYear.Take(3).Sum(x => x.Value));
public Salary GetTotalSalaryForFirstSixMonths()
=> new Salary(SalaryByLastYear.Take(6).Sum(x => x.Value));
Прием заключается в введении одного метода примающего дополнительный параметр:
public Salary GetTotalSalaryForMonth(int month)
=> new Salary(SalaryByLastYear.Take(month).Sum(x => x.Value));
Очень полезный прием, в примере выше, возможно есть ограничение, что класс должен выдавать клиентам информацию по трем и шести месяцам, поэтому нельзя добавлять параметр month. Вот еще пример:
private Person(PaymentType pt)
{
PaymentType = pt;
}
public static Person CreatePerson(PaymentType paymentType) => new Person(paymentType);
Фабричный метод принимает объект и в зависимости от значение что-то делает, можно явно определить методы создания сотрудника с фиксированной зарплатой и почасовой зарплатой:
public static Person CreatePersonWithFixedSalary() => new Person(PaymentType.Fix);
public static Person CreatePersonWithHourlySalary() => new Person(PaymentType.Hourly);
Тем самым мы не раскрываем PaymentType и переносим с рантайма на время компиляции создание сотрудника определенного типа
Это описано вот здесь: Remove Parameter Если сервер может сам получить данные он не должен требовать их от клиента, это может вызвать неоднозначность
Иногда в методы передаются одни и те же значения, тогда можно создать класс, содержащий эти значения и скорее всего в него можно будет вынести код, которые до этого дублировался во всех методах, принимающих такие параметры
public Salary CalculateSalary(DateTime start, DateTime end)
{
if (start > end)
throw new ArgumentException();
return new Salary(SalaryByLastYear.Take(end.Month).Skip(start.Month).Sum(x => x.Value));
}
Введем класс Range, в который вынесем проверку что start=<date и получение номера месяца из структуры DateTime
public class Range
{
public readonly int StartMonth;
public readonly int EndMonth;
public Range(DateTime start, DateTime end)
{
if (start > end)
throw new ArgumentException();
StartMonth = start.Month;
EndMonth = end.Month;
}
}
public Salary CalculateSalary(Range range)
{
return new Salary(SalaryByLastYear.Take(range.StartMonth).Skip(range.EndMonth).Sum(x => x.Value));
}
Удаляем setter'ы у тех свойств класса которые не должны изменяться при жизни экземляра класса, тем усиливает инвариант класса, и явно показываем его поведение
Скрывайте методы, которые можно не показывать клиентам. Это очевидно, но нужно об этом помнить при рефакторингах системы