Если вы занимались программированием какое-то время, вы, возможно, встречали функции более высокого порядка, но (как и я), возможно, не до конца осознавали, насколько мощными они могут быть. Если вы не слышали о них раньше, не бойтесь, у меня есть объяснение ниже.

Что такое функции высшего порядка?

Проще говоря, функция высшего порядка — это функция, вход или выход которой также являются функцией.

В следующем примере функция Map принимает список определенного типа данных и функцию и возвращает новый список с функцией, примененной к каждому из элементов в списке (в основном эквивалентно методу LINQs Select).

[pastacode lang=»c» manual=»%2F%2F%20definition%0AList%3CTOut%3E%20Map%3CTIn%2C%20TOut%3E(List%3CTIn%3E%20list%2C%20Func%3CTIn%2C%20TOut%3E%20mapper)%0A%7B%0A%20%20%20%20var%20newList%20%3D%20new%20List%3CTOut%3E()%3B%0A%20%20%20%20foreach%20(var%20item%20in%20list)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20var%20newItem%20%3D%20mapper(item)%3B%0A%20%20%20%20%20%20%20%20newList.Add(newItem)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20newList%3B%0A%7D%0A%0A%2F%2F%20usage%0Avar%20myList%20%3D%20new%20List%3Cint%3E%20%7B%201%2C%202%2C%203%2C%204%2C%205%20%7D%3B%0A%0Aint%20multiplyBy2%20(int%20num)%20%3D%3E%20num%20*%202%3B%0Avar%20multipliedList%20%3D%20Map(myList%2C%20multiplyBy2)%3B%0A%2F%2F%20output%20%7B%202%2C%204%2C%206%2C%208%2C%2010%20%7D%3B%0A%0Abool%20isEven%20(int%20num)%20%3D%3E%20num%20%25%202%20%3D%3D%200%3B%0Avar%20isEvenList%20%3D%20Map(myList%2C%20isEven)%3B%0A%2F%2F%20output%20%7B%20false%2C%20true%2C%20false%2C%20true%2C%20false%20%7D%3B%0A» message=»» highlight=»» provider=»manual»/]

 

Вот пример функции более высокого порядка, которая возвращает функцию. Сначала вы вызываете функцию Add с заданным целым числом, которая возвращает функцию, которая добавляет это первое целое число к любому заданному другому целому числу.

[pastacode lang=»c» manual=»Func%3Cint%2C%20int%3E%20Add(int%20a)%20%3D%3E%20(int%20b)%20%3D%3E%20a%20%2B%20b%3B%0A%0Avar%20add9%20%3D%20Add(9)%3B%0A%0Avar%20sum1%20%3D%20add9(1)%3B%0A%2F%2F%20output%2010%0A%0Avar%20sum2%20%3D%20add9(2)%3B%0A%2F%2F%20output%2011%0A» message=»» highlight=»» provider=»manual»/]

Надеюсь, теперь вы понимаете, почему функции высшего порядка так полезны; они позволяют многократно использовать сложный код очень гибким образом, позволяя клиенту определять свои собственные функции ввода / вывода. Например, без использования функций высшего порядка нужно было бы определить новую функцию Map для каждого отдельного типа отображения, в котором они нуждались.

Практический пример — Repository Pattern

Repository Pattern — это очень распространенный шаблон проектирования, используемый в качестве абстракции доступа к данным. Это позволяет выполнять типичные операции CRUD без необходимости прямого взаимодействия клиента с поставщиком данных. Типичный интерфейс репозитория может выглядеть примерно так:

[pastacode lang=»c» manual=»public%20interface%20IProductRepository%0A%7B%0A%20%20%20%20int%20Create(Product%20product)%3B%0A%20%20%20%20bool%20Update(Product%20product)%3B%0A%20%20%20%20bool%20Delete(int%20id)%3B%0A%20%20%20%20Product%20GetById(int%20id)%3B%0A%20%20%20%20IEnumerable%3CProduct%3E%20GetAll()%3B%0A%20%20%20%20IEnumerable%3CProduct%3E%20GetByCategoryId(int%20categoryId)%3B%0A%20%20%20%20IEnumerable%3CProduct%3E%20GetActive()%3B%0A%20%20%20%20%2F%2F%20etc…%0A%7D» message=»» highlight=»» provider=»manual»/]

Как вы можете видеть в этом типичном примере, существует множество определений, которые все возвращают IEnumerable <Product>, и каждый раз, когда вам нужен другой конкретный тип фильтра продукта, который требует добавления в интерфейс и написания совершенно новой реализации. Это не очень хорошо для обслуживания кода…

Вместо этого, используя функции более высокого порядка, мы можем определить единственный метод, который получает IEnumerable <Product>, который принимает функцию фильтра в качестве входных данных. Таким образом, клиент может определять свои собственные фильтры, и ProductRepository не нуждается в постоянном обновлении с новыми реализациями.

[pastacode lang=»c» manual=»interface%20IProductRepository%0A%7B%0A%20%20%20%20%2F%2F%20create%2C%20update%2C%20delete%20omitted%0A%0A%20%20%20%20IEnumerable%3CProduct%3E%20Get(Func%3CProduct%2C%20bool%3E%20filter%20%3D%20null)%3B%0A%7D%0A%0Apublic%20class%20ProductRepository%20%3A%20IProductRepository%0A%7B%0A%20%20%20%20private%20readonly%20List%3CProduct%3E%20_products%20%3D%20new%20List%3CProduct%3E()%3B%20%2F%2F%20data%20source%0A%0A%20%20%20%20public%20IEnumerable%3CProduct%3E%20Get(Func%3CProduct%2C%20bool%3E%20filter%20%3D%20null)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20typically%20you%20might%20use%20the%20LINQ%20Where%20method%20here%0A%20%20%20%20%20%20%20%20%2F%2F%20but%20using%20the%20foreach%20to%20be%20clear%20what%20is%20happening%0A%0A%20%20%20%20%20%20%20%20if%20(filter%20is%20null)%20return%20_products%3B%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20var%20filteredList%20%3D%20new%20List%3CProduct%3E()%3B%0A%20%20%20%20%20%20%20%20foreach(var%20product%20in%20_products)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(filter(product))%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20filteredList.Add(product)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20return%20filteredList%3B%0A%20%20%20%20%7D%0A%7D» message=»» highlight=»» provider=»manual»/]

[pastacode lang=»c» manual=»%2F%2F%20client%20code%0Avar%20allProducts%20%3D%20_productRepository.Get()%3B%0Avar%20productsByCategoryId%20%3D%20_productRepository.Get(p%20%3D%3E%20p.CategoryId%20%3D%3D%201)%3B%0Avar%20activeProducts%20%3D%20_productRepository.Get(p%20%3D%3E%20p.Active)%3B» message=»» highlight=»» provider=»manual»/]

Вывод

В этой статье я представил концепции функций высшего порядка и продемонстрировал, насколько они чрезвычайно полезны при создании многоразового и гибкого кода, который легко поддерживать и понимать. Я также привел практический пример использования, который вы можете найти в корпоративном приложении .NET, — делая шаблон репозитория более пригодным для повторного использования.