Инверсия зависимости в ООП означает, что вы кодируете интерфейс, который затем предоставляется реализацией в объекте.
Языки, которые поддерживают функции более высокого языка, часто могут решить простые проблемы инверсии зависимостей, передавая поведение как функцию вместо объекта, который реализует интерфейс в ОО-смысле.
В таких языках подпись функции может стать интерфейсом, и вместо традиционного объекта передается функция для обеспечения желаемого поведения. Отверстие в среднем узоре является хорошим примером для этого.
Это позволит вам достичь того же результата с меньшим количеством кода и большей выразительностью, поскольку вам не нужно реализовывать целый класс, соответствующий интерфейсу (OOP), чтобы обеспечить желаемое поведение для вызывающей стороны. Вместо этого вы можете просто передать простое определение функции. Вкратце: код часто легче поддерживать, он более выразителен и более гибок, когда используются функции более высокого порядка.
Пример в C #
Традиционный подход:
public IEnumerable<Customer> FilterCustomers(IFilter<Customer> filter, IEnumerable<Customers> customers)
{
foreach(var customer in customers)
{
if(filter.Matches(customer))
{
yield return customer;
}
}
}
//now you've got to implement all these filters
class CustomerNameFilter : IFilter<Customer> /*...*/
class CustomerBirthdayFilter : IFilter<Customer> /*...*/
//the invocation looks like this
var filteredDataByName = FilterCustomers(new CustomerNameFilter("SomeName"), customers);
var filteredDataBybirthDay = FilterCustomers(new CustomerBirthdayFilter(SomeDate), customers);
С функциями высшего порядка:
public IEnumerable<Customer> FilterCustomers(Func<Customer, bool> filter, IEnumerable<Customers> customers)
{
foreach(var customer in customers)
{
if(filter(customer))
{
yield return customer;
}
}
}
Теперь реализация и вызов становятся менее громоздкими. Нам больше не нужно предоставлять реализацию IFilter. Нам больше не нужно реализовывать классы для фильтров.
var filteredDataByName = FilterCustomers(x => x.Name.Equals("CustomerName"), customers);
var filteredDataByBirthday = FilterCustomers(x => x.Birthday == SomeDateTime, customers);
Конечно, это уже может быть сделано LinQ в C #. Я просто использовал этот пример, чтобы проиллюстрировать, что проще и более гибко использовать функции более высокого порядка вместо объектов, которые реализуют интерфейс.
IFilter<Customer>
не принуждение вообще. Функция высшего порядка гораздо более гибкая, что является большим преимуществом, а возможность писать их в строке - еще одно огромное преимущество. Лямбды также намного легче в состоянии захватывать локальные переменные.public delegate bool CustomerFilter(Customer customer)
. в чистых функциональных языках, таких как haskell, псевдонимы типов тривиальны:type customerFilter = Customer -> Bool
Если вы хотите изменить поведение функции
Вы могли бы передать другую функцию
который реализует поведение, которое вы хотите быть другим.
«doThisWith» - функция высшего порядка, потому что она принимает другую функцию в качестве аргумента.
Например, вы могли бы иметь
источник
Краткий ответ:
Классическое внедрение / инверсия зависимостей использует классовые интерфейсы в качестве заполнителя для зависимых функций. Этот интерфейс реализован классом.
Вместо Interface / ClassImplementation многие зависимости могут быть легко реализованы с помощью функции делегата.
Вы можете найти пример для обоих в c # на ioc-factory-pros-and-contras-for-interface-versus-Delegates .
источник
Сравните это:
с:
Вторая версия - это способ сокращения стандартного кода Java 8 (зацикливание и т. Д.) Путем предоставления функций более высокого порядка, например,
filter
позволяющих передать минимальный минимум (т. Е. Зависимость, которую нужно ввести - лямбда-выражение).источник
Копилка примера LennyProgrammers ...
Одна из вещей, которую пропустили другие примеры, - это то, что вы можете использовать функции более высокого порядка вместе с частичным приложением функций (PFA) для связывания (или «внедрения») зависимостей в функцию (через список аргументов) для создания новой функции.
Если вместо:
у нас (чтобы быть общепринятым в том, как обычно выполняется PFA) есть низкоуровневая рабочая функция как (порядок замены аргументов):
Затем мы можем частично применить doThisWith следующим образом:
Что позволит нам позже использовать новую функцию следующим образом:
Или даже:
Смотрите также: https://ramdajs.com/docs/#partial
... и, да, сумматоры / множители являются невообразительными примерами. Лучшим примером была бы функция, которая принимает сообщения и регистрирует их или отправляет по электронной почте в зависимости от того, что функция «потребителя» передала как зависимость.
Расширяя эту идею, все более длинные списки аргументов могут постепенно сужаться до все более специализированных функций с более короткими и короткими списками аргументов, и, конечно, любая из этих таких функций может быть передана другим функциям в качестве зависимостей для частичного применения.
ООП хорошо, если вам нужен набор вещей с несколькими тесно связанными операциями, но он превращается в рабочую работу по созданию группы классов, каждый из которых имеет один публичный метод "сделай это", в стиле "Царство существительных".
источник