Como a inversão de dependência está relacionada a funções de ordem superior?

38

Hoje, acabei de ver este artigo que descreveu a relevância do princípio SOLID no desenvolvimento de F # -

Princípios F # e Design - SOLID

E, ao abordar o último - "Princípio da inversão de dependência", o autor disse:

From a functional point of view, these containers and injection concepts can be solved with a simple higher order function, or hole-in-the-middle type pattern which are built right into the language.

Mas ele não explicou mais. Então, minha pergunta é: como está a inversão de dependência relacionada a funções de ordem superior?

    
por Gulshan 25.08.2011 / 06:11
fonte

5 respostas

35

Inversão de dependência em OOP significa que você codifica em uma interface que é fornecida por uma implementação em um objeto.

As linguagens que suportam funções de linguagem mais altas geralmente resolvem problemas simples de inversão de dependência, passando o comportamento como uma função, em vez de um objeto que implementa uma interface no sentido OO.

Em tais idiomas, a assinatura da função pode se tornar a interface e uma função é passada em vez de um objeto tradicional para fornecer o comportamento desejado. O buraco no padrão do meio é um bom exemplo disso.

Permite obter o mesmo resultado com menos código e mais expressividade, já que você não precisa implementar uma classe inteira em conformidade com uma interface (OOP) para fornecer o comportamento desejado para o chamador. Em vez disso, você pode simplesmente passar uma definição de função simples. Resumindo: O código é frequentemente mais fácil de manter, mais expressivo e mais flexível quando se usam funções de ordem superior.

Um exemplo em C #

Abordagem tradicional:

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);

Com funções de ordem superior:

public IEnumerable<Customer> FilterCustomers(Func<Customer, bool> filter, IEnumerable<Customers> customers)
{
    foreach(var customer in customers)
    {
        if(filter(customer))
        {
            yield return customer;
        }
    }
}

Agora, a implementação e a invocação se tornam menos complicadas. Nós não precisamos mais fornecer uma implementação do IFilter. Não precisamos mais implementar classes para os filtros.

var filteredDataByName = FilterCustomers(x => x.Name.Equals("CustomerName"), customers);
var filteredDataByBirthday = FilterCustomers(x => x.Birthday == SomeDateTime, customers);

Claro, isso já pode ser feito pelo LinQ em C #. Acabei de usar este exemplo para ilustrar que é mais fácil e mais flexível usar funções de ordem superior em vez de objetos que implementam uma interface.

    
por 25.08.2011 / 09:20
fonte
6

Se você quiser mudar o comportamento de uma função

doThis(Foo)

você poderia passar outra função

doThisWith(Foo, anotherFunction)

que implementa o comportamento que você quer ser diferente.

"doThisWith" é uma função de ordem mais alta porque usa outra função como argumento.

Por exemplo, você poderia ter

storeValues(Foo, writeToDatabase)
storeValues(Foo, imitateDatabase)
    
por 25.08.2011 / 09:43
fonte
4

Resposta curta:

Injeção de Dependência Clássica / Inversão de Controle usa uma interface de classe como espaço reservado para funcionalidade dependente. Essa interface é implementada por uma classe.

Em vez de Interface / ClassImplementation, muitas dependências podem ser implementadas com mais facilidade com uma função delegada.

Você encontra um exemplo para ambos em c # em ioc- fábrica-pros-e-contras-para-interface-versus-delegados .

    
por 31.05.2012 / 14:07
fonte
0

Compare isso:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = new LinkedList<String>();
for (String name : names) {
    if (name.startsWith("S")) {
        namesBeginningWithS.add(name);
    }
}

com:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = names.stream().filter(n <- n.startsWith("S")).collect();

A segunda versão é a maneira do Java 8 de reduzir o código clichê (looping etc), fornecendo funções de ordem superior como filter , que permite que você passe o mínimo (ou seja, a dependência a ser injetada - a expressão lambda). / p>     

por 09.08.2017 / 23:18
fonte
0

Piggy-back-off do exemplo LennyProgrammers ...

Uma das coisas que os outros exemplos erraram é que você pode usar funções de ordem superior junto com o aplicativo de função parcial (PFA) para ligar (ou "injetar") dependências em uma função (via sua lista de argumentos) para criar um novo função.

Se em vez de:

doThisWith(Foo, anotherFunction)

nós (para ser convencional em como P.F.A. geralmente é feito) temos a função de trabalho de baixo nível como (troca de ordem arg):

doThisWith( anotherFunction, Foo )

Podemos então aplicar parcialmente doThis Com isso:

doThis = doThisWith( anotherFunction )  // note that "Foo" is still missing, argument list is partial

Que nos permite mais tarde usar a nova função da seguinte forma:

doThis(Foo)

Ou até mesmo:

doThat = doThisWith( yetAnotherDependencyFunction )
...
doThat( Bar )

Veja também: link

... e, sim, adicionadores / multiplicadores são exemplos inimagináveis. Um exemplo melhor seria uma função que pega mensagens e as registra ou envia por e-mail, dependendo do que a função "consumidor" passou como dependência.

Estendendo esta ideia, listas de argumentos ainda mais longas podem ser progressivamente reduzidas a funções cada vez mais especializadas com listas de argumentos mais curtas e curtas, e é claro que qualquer uma dessas funções pode ser passada para outras funções como dependências a serem aplicadas parcialmente.

OOP é bom se você precisa de um conjunto de coisas com múltiplas operações relacionadas, mas se transforma em make-work para criar um monte de classes, cada uma com um único método público "faça", à la "The Kingdom of Nouns ".

    
por 14.09.2018 / 03:56
fonte