In che modo l'inversione di dipendenza è correlata alle funzioni di ordine superiore?

38

Oggi ho appena visto questo articolo che descrive la rilevanza del principio SOLID nello sviluppo di F # -

F # e principi di progettazione - SOLID

E mentre si affronta l'ultimo - "Principio di inversione delle dipendenze", l'autore ha detto:

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.

Ma non lo spiegò ulteriormente. Quindi, la mia domanda è: in che modo l'inversione della dipendenza è correlata alle funzioni di ordine superiore?

    
posta Gulshan 25.08.2011 - 06:11
fonte

5 risposte

35

Dependency Inversion in OOP significa che si codifica contro un'interfaccia che viene poi fornita da un'implementazione in un oggetto.

Le lingue che supportano funzioni linguistiche superiori possono spesso risolvere semplici problemi di inversione delle dipendenze passando il comportamento come funzione invece di un oggetto che implementa un'interfaccia in senso OO.

In tali lingue, la firma della funzione può diventare l'interfaccia e viene passata una funzione anziché un oggetto tradizionale per fornire il comportamento desiderato. Il buco nel modello centrale è un buon esempio per questo.

Ti consente di ottenere lo stesso risultato con meno codice e più espressività, poiché non è necessario implementare un'intera classe conforme a un'interfaccia (OOP) per fornire il comportamento desiderato per il chiamante. Invece, puoi semplicemente passare una semplice definizione di funzione. In breve: il codice è spesso più facile da mantenere, più espressivo e più flessibile quando si utilizzano funzioni di ordine superiore.

Un esempio in C #

Approccio tradizionale:

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

Con funzioni di ordine superiore:

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

Ora l'implementazione e l'invocazione diventano meno ingombranti. Non è più necessario fornire un'implementazione IFilter. Non abbiamo più bisogno di implementare classi per i filtri.

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

Naturalmente, questo può già essere fatto da LinQ in C #. Ho appena usato questo esempio per illustrare che è più semplice e flessibile utilizzare funzioni di ordine superiore anziché oggetti che implementano un'interfaccia.

    
risposta data 25.08.2011 - 09:20
fonte
6

Se vuoi cambiare il comportamento di una funzione

doThis(Foo)

potresti passare un'altra funzione

doThisWith(Foo, anotherFunction)

che implementa il comportamento che desideri essere diverso.

"doThisWith" è una funzione di ordine superiore perché accetta un'altra funzione come argomento.

Ad esempio potresti avere

storeValues(Foo, writeToDatabase)
storeValues(Foo, imitateDatabase)
    
risposta data 25.08.2011 - 09:43
fonte
4

Risposta breve:

Iniezione di dipendenza classica / Inversion of Control utilizza un'interfaccia di classe come segnaposto per la funzionalità dipendente. Questa interfaccia è implementata da una classe.

Al posto di Interface / ClassImplementation molte dipendenze possono essere implementate più facilmente con una funzione delegate.

Puoi trovare un esempio sia in c # che in ioc- fabbrica-pro-e-contras-per-interfaccia-versus-delegati .

    
risposta data 31.05.2012 - 14:07
fonte
0

Confronta questo:

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

con:

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

La seconda versione è il modo in cui Java 8 riduce il codice boilerplate (looping ecc.) fornendo funzioni di ordine superiore come filter che consente di passare il minimo indispensabile (ovvero la dipendenza da iniettare - l'espressione lambda).

    
risposta data 09.08.2017 - 23:18
fonte
0

Piggy-backing di LennyProgrammers esempio ...

Una delle cose che gli altri esempi mancano è che è possibile utilizzare le funzioni di ordine superiore insieme con l'applicazione di funzione parziale (PFA) per legare (o "iniettare") le dipendenze in una funzione (tramite la sua lista di argomenti) per creare un nuovo la funzione.

Se invece di:

doThisWith(Foo, anotherFunction)

noi (per essere convenzionali nel modo in cui P.F.A. di solito è fatto) abbiamo la funzione worker di basso livello come (swapping order):

doThisWith( anotherFunction, Foo )

Possiamo quindi applicare parzialmente doThisWith in questo modo:

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

Che ci consente in seguito di utilizzare la nuova funzione in questo modo:

doThis(Foo)

O anche:

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

Vedi anche: link

... e, sì, i sommatori / moltiplicatori sono esempi privi di fantasia. Un esempio migliore potrebbe essere una funzione che accetta i messaggi e li registra o li invia per posta elettronica a seconda di cosa è stata inoltrata dalla funzione "consumatore" come una dipendenza.

Estendendo questa idea, le liste di argomenti ancora più lunghe possono essere progressivamente ridotte a funzioni sempre più specializzate con elenchi di argomenti più brevi e brevi e, naturalmente, ognuna di queste funzioni può essere passata ad altre funzioni come dipendenze da applicare parzialmente. p>

OOP è bello se hai bisogno di un fascio di cose con più operazioni strettamente correlate, ma diventa un make-work per creare un gruppo di classi ognuna con un unico metodo "fai da te" pubblico, a la "The Kingdom of Nouns" ".

    
risposta data 14.09.2018 - 03:56
fonte

Leggi altre domande sui tag