Пять лет назад я вышел на плато. Мой код достиг определенного уровня качества и гибкости и перестал улучшаться. Вот как я использовал аспекты функционального программирования, чтобы продолжать расти.
Мой код был довольно ТВЕРДЫМ, но все еще оставалось много очень похожего кода, несмотря на активные попытки по возможности удалить дублирование. Это не было точным дублированием, но были четкие шаблоны во всем коде, которые делали обслуживание больше проблем, чем должно было быть.
Затем я узнал, как применять Actions
и Funcs
для дальнейшего улучшения моего кода.
Давайте посмотрим на гипотетический пример:
Хотя это надуманный пример, он иллюстрирует проблему. В этом сценарии у вас есть повторяющийся шаблон проверки свойства, затем выполнения некоторой настраиваемой логики, которую не следует вызывать, если свойство было ложным, с последующим сохранением результата в настраиваемом свойстве объекта.
Это небольшая простая процедура, но дублирование очевидно, и оно препятствует извлечению методов, потому что CalculateX
методы не могут быть вызваны, если в резюме нет соответствующей степени.
Кроме того, предположим, что произошла ошибка и необходимо изменить код. Теперь вам нужно сделать то же изменение в 3-х местах. Если вы пропустите один, скорее всего, возникнут ошибки. Кроме того, сходство побуждает вас заняться разработкой, управляемой копированием / вставкой, что является анти-шаблоном и потенциальным источником ошибок, если не все строки, которые необходимо было изменить, были изменены.
Что изменило мой подход, так это то, что передача типов Action и Func позволяет добиться большей гибкости от ваших методов, предоставляя им настраиваемое поведение.
Action
- это общая сигнатура метода, вызываемого без возвращаемого типа.
Например, подпись того, что принимает логические и целочисленные параметры, будет выглядеть так: Action<bool, int> myAction;
Func
очень похож на Action
, за исключением того, что возвращает значение. Тип возвращаемого значения - это последний аргумент универсального типа. Например, Func<bool, int>
примет логическое значение и вернет целое число.
Итак, мы можем использовать Func
в нашем примере для извлечения метода с некоторым настраиваемым поведением:
Это улучшает наш вызывающий код до следующего короткого сегмента:
Это намного проще, хотя синтаксис немного сложнее читать. В третьем параметре метода мы объявляем лямбда-выражение, подобное тем, которые вы используете для запросов LINQ.
Если вам нужно было работать с Func, который использует параметры (скажем, у вас есть подпись Func<int, decimal>
, вы можете изменить свою логику на следующую: (myInt) => myInt + CalculateMastersDegreeScore();
Хотя это был довольно надуманный пример, надеюсь, он иллюстрирует возможности передачи Func
и Action
различным методам. Это фундамент, на котором построено функциональное программирование, но в небольших количествах это может быть чрезвычайно полезно и в объектно-ориентированном программировании.
Хотя этот синтаксис делает код немного сложнее для чтения, преимущества в удобстве обслуживания реальны, и вероятность появления дефектов, связанных с дублированием, намного ниже.
Попробуйте побочный проект или конкретную область дублирования и дайте мне знать, что вы думаете.
Первоначально опубликовано на https://dev.to 16 сентября 2019 г.