Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save eterekhin/685c00f8bb96a3d75c91f4b245ca63ef to your computer and use it in GitHub Desktop.
Save eterekhin/685c00f8bb96a3d75c91f4b245ca63ef to your computer and use it in GitHub Desktop.
Introduce Null Object.Introduce Assertion. Rename Method. Add Parameter. Remove Parameter

Introduce Null Object

Удобный паттерн если в приложении много проверок на null, в c# можно использовать динамические прокси для достижения такого эффекта. Предположим, что система спроектирована так, что в массиве

public class Person
    {
        public Salary January { get; }
        public Salary February { get; }
        public Salary March { get; }
        public Salary April { get; }
        public Salary May { get; }
        public Salary June { get; }
        public Salary July { get; }
        public Salary August { get; }
        public Salary September { get; }
        public Salary October { get; }
        public Salary November { get; }
        public Salary December { get; }

        public IEnumerable<Salary> SalaryByLastYear =>
            new[]
            {
                January,
                February,
                March,
                April,
                May,
                June,
                July,
                August,
                September,
                October,
                November,
                December,
            }.ToList();
    }

У нас стоит задача посчитать суммарную зарплату:

  public decimal TotalSalary() => SalaryByLastYear.Sum(x => x.Value);

Но, если год не закончен, то в массиве будут null, можно сделать фильтрацию, но клиента класса могут не ожидать null, поэтому нужно ввести нейтральный объект, представляющий отсутсвие зарплаты по этому периоду и не выбрасывающий Null Reference Exception:

И по умолчанию проиницилизировать свойства класса NullSalary:
```csharp
    public class Salary
    {
        public static Salary NullSalary => new Salary(0);
        public decimal Value { get; }

        public Salary(decimal value)
        {
            Value = value;
        }
    }
...
        public Salary January { get; } = Salary.NullSalary;
        public Salary February { get; } = Salary.NullSalary;
        public Salary March { get; } = Salary.NullSalary;
        public Salary April { get; } = Salary.NullSalary;
        public Salary May { get; } = Salary.NullSalary;
        public Salary June { get; } = Salary.NullSalary;
        public Salary July { get; } = Salary.NullSalary;
        public Salary August { get; } = Salary.NullSalary;
        public Salary September { get; } = Salary.NullSalary;
        public Salary October { get; } = Salary.NullSalary;
        public Salary November { get; } = Salary.NullSalary;
        public Salary December { get; } = Salary.NullSalary;
...

В F# для этого есть Option тип, который может принимать значения Some Value и None

Introduce Assertion

Перед входом в метод можно поставить условия:

        public decimal TotalSalary()
        {
            Debug.Assert(SalaryByLastYear.All(x => x != null), "SalaryByLastYear.All(x => x != null)");
            return SalaryByLastYear.Sum(x => x.Value);
        }

Теперь читатель кода увидит условия этого метода, еще лучше его переименовать, чтобы он более ясно отражал суть

Rename Method

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

        public decimal TotalSalaryAfterYear()
        {
            Debug.Assert(SalaryByLastYear.All(x => x != null), "SalaryByLastYear.All(x => x != null)");
            return SalaryByLastYear.Sum(x => x.Value);
        }

Но непонятно что делает фукнция если год еще не прошел, чтобы обозначить, что возможен Exception можно явно об этом сказать:

        public decimal TotalSalaryAfterYearIfYearHasNotEndedGenerateException()
        {
            Debug.Assert(SalaryByLastYear.All(x => x != null), "SalaryByLastYear.All(x => x != null)");
            return SalaryByLastYear.Sum(x => x.Value);
        }

Но таких функций лучше делать как можно меньше, лучше не генерировать exception, а запретить вызов этой функции в состоянии, когда год еще не закончился

Add Parameter

Когда назначение функции меняется приходится добавлять дополнительные параметры, в которых метод теперь нуждается, однако если этот параметр можно получить внутри класса, которому принадлежит метод - нужно так и сделать

Remove Parameter

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

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