Натягиваем ФП на ООП

от автора

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

private double fBm(Vector2D v, int y) {     double result = 0f;     double freq = Frequency;      for (int i = 0; i < Octaves; ++i)     {         result += NoiseFn(permutation, v * freq) * Amplitude;         freq *= Lacunarity;         Amplitude *= Gain; // <-- Вот тут.     }      return result; }

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

Во-первых нужно сделать this обязательным. Поставить поле не с той стороны выражения слишком легко. Если бы я везде явно указал контекст, скорее всего сразу бы заметил, что ступил. К сожалению, компилятор C# нельзя заставить требовать this, а в статических проверках студии есть только правило которое работает наоборот, то есть заставляет убирать this. Так что, видимо придется писать свое.

Во-вторых нужно исключить использование полей класса из тела метода, а все изменения записывать в конце метода, если они есть. У нас получается три абзаца. В первом мы объявляем все что нам понадобится, во втором делаем полезную работу, в третьем сохраняем состояние.

public void DoAThing(int input) {     // Первый абзац     int a = this.A;     int b = this.B;     int result = 0;      // Второй абзац     for (int i = 0; i < input; ++i)         result += (a + b) * i;      // Третий абзац     this.Thing = result; }

В первом абзаце все состояние которое нужно мы сохраняем локально.
Во втором, this не фигурирует.
В третьем this всегда должен быть первым словом. Тут же будут вызываться другие методы которые меняют состояние.

return можно добавить в любое место, на поведение метода он не повлияет.

Плюсы

  • Багов со случайным изменением состояния станет меньше.
  • Отсутствие третьего абзаца делает метод чистой функцией, а значит его можно запускать параллельно.

Минусы

  • Дисциплина. Фу-фу-фу. Без автоматических проверок, легко все испортить.
  • В некоторых случаях этот подход будет работать медленней.
  • Может выглядеть странно или глупо. Вот к примеру:
    {     int a = this.A;     int b = this.B;     return a + b; }

Вместо заключения
Как думаете? В этом есть смысл или я парюсь?
Если вы уже делаете подобное или натягиваете другие части ФП на ООП, пожалуйста поделитесь.

ссылка на оригинал статьи https://habrahabr.ru/post/318908/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *