Skip to content

Instantly share code, notes, and snippets.

  • Save eterekhin/52031580fe5fb388025663ed9e796660 to your computer and use it in GitHub Desktop.
Save eterekhin/52031580fe5fb388025663ed9e796660 to your computer and use it in GitHub Desktop.
Fowler.Separate Query from Modifier.Parameterize Method.Replace Parameter with Explicit Methods.Peplace Parameter with Method.Introduce Parameter Object.Remove Setting Method.Hide Method

Separate Query from Modifier

Полезный принцип, он утвержает, что чтение и модификация чего либо должны разделяться, например:

 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 методе(на его месте может быть другой вызов), но даже если вызов был к месту, мы теряем возможность закешировать результат, потому что как только мы это сделаем сразу нарушится работа системы, это плохо, несмотря на то, что она достигается такими средствами

Parameterize Method

Если есть несколько методов выполняющий одно и то же, но с разными константами, например:

        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));

Replace parameter with explicit methods

Очень полезный прием, в примере выше, возможно есть ограничение, что класс должен выдавать клиентам информацию по трем и шести месяцам, поэтому нельзя добавлять параметр 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 и переносим с рантайма на время компиляции создание сотрудника определенного типа

Replace Parameter with Method

Это описано вот здесь: Remove Parameter Если сервер может сам получить данные он не должен требовать их от клиента, это может вызвать неоднозначность

Introduce Parameter Object

Иногда в методы передаются одни и те же значения, тогда можно создать класс, содержащий эти значения и скорее всего в него можно будет вынести код, которые до этого дублировался во всех методах, принимающих такие параметры

      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));
        }

Remove Settings Method

Удаляем setter'ы у тех свойств класса которые не должны изменяться при жизни экземляра класса, тем усиливает инвариант класса, и явно показываем его поведение

Hide Method

Скрывайте методы, которые можно не показывать клиентам. Это очевидно, но нужно об этом помнить при рефакторингах системы

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment