Perché Inversion of Control è chiamato così?

93

Le parole invert o control non sono affatto usate per definire Inversion of Control nelle definizioni che ho visto.

Definizioni

Wikipedia

inversion of control (IoC) is a programming technique, expressed here in terms of object-oriented programming, in which object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis. ~http://en.wikipedia.org/wiki/Inversion_of_control

Martin Fowler

Inversion of Control is a common pattern in the Java community that helps wire lightweight containers or assemble components from different projects into a cohesive application. ~ based on http://www.martinfowler.com/articles/injection.html (reworded)

Allora perché Inversion of Control ha chiamato Inversion of Control? Quale controllo viene invertito e da cosa? C'è un modo per definire Inversion of Control usando la terminologia: invert e controllo ?

    
posta Korey Hinton 22.07.2013 - 18:36
fonte

7 risposte

65

Supponiamo che tu abbia una sorta di classe "repository" e che il repository sia responsabile della consegna dei dati a te da un'origine dati.

Il repository potrebbe stabilire da solo una connessione all'origine dati. Ma cosa succede se ti ha permesso di passare una connessione alla fonte dei dati attraverso il costruttore del repository?

Consentendo al chiamante di fornire la connessione, è stata disaccoppiata la dipendenza della connessione all'origine dati dalla classe repository, consentendo a qualsiasi origine dati di funzionare con il repository, non solo quella specificata dal repository .

Hai controllo invertito dando la responsabilità di creare la connessione dalla classe repository al chiamante.

Martin Fowler suggerisce di utilizzare il termine "Iniezione di dipendenza" per descrivere questo tipo di Inversion of Control, dal momento che Inversion of Control come concetto può essere applicato in modo più ampio delle semplici dipendenze di iniezione in un metodo di costruzione.

    
risposta data 22.07.2013 - 18:50
fonte
76

Non credo che nessuno possa spiegarlo meglio di Martin Fowler, più in basso l'articolo che hai collegato .

For this new breed of containers the inversion is about how they lookup a plugin implementation. In my naive example the lister looked up the finder implementation by directly instantiating it. This stops the finder from being a plugin. The approach that these containers use is to ensure that any user of a plugin follows some convention that allows a separate assembler module to inject the implementation into the lister.

Come spiegato nei paragrafi precedenti, questo non è esattamente lo stesso del motivo per cui è stata originata l'espressione "Inversion of Control".

When these containers talk about how they are so useful because they implement "Inversion of Control" I end up very puzzled. Inversion of control is a common characteristic of frameworks, so saying that these lightweight containers are special because they use inversion of control is like saying my car is special because it has wheels.

The question, is what aspect of control are they inverting? When I first ran into inversion of control, it was in the main control of a user interface. Early user interfaces were controlled by the application program. You would have a sequence of commands like "Enter name", "enter address"; your program would drive the prompts and pick up a response to each one. With graphical (or even screen based) UIs the UI framework would contain this main loop and your program instead provided event handlers for the various fields on the screen. The main control of the program was inverted, moved away from you to the framework.

È per questo che continua a coniare il termine "Iniezione di dipendenza" per coprire questa specifica implementazione di Inversion of Control.

As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.

Per chiarire un po ': Inversion of Control significa tutto ciò che inverte la struttura di controllo di un programma dal classico design procedurale.

Nei giorni precedenti, un esempio chiave di questo era lasciare che un framework gestisse la comunicazione tra un'interfaccia utente e il tuo codice, piuttosto che lasciare che il codice generasse l'interfaccia utente direttamente.

In tempi più recenti (quando tali quadri erano praticamente dominati, quindi la domanda non era più rilevante), un esempio stava invertendo il controllo sull'istanza degli oggetti.

Fowler e altri hanno deciso che il termine Inversion of Control copriva troppe tecniche e avevamo bisogno di un nuovo termine per l'esempio specifico di istanziazione degli oggetti (Dependency Injection) ma, nel momento in cui l'accordo era stato fatto, la frase "Contenitore IoC" era decollato.

Questo confonde l'acqua molto, perché un contenitore IoC è un tipo specifico di Iniezione di Dipendenza, ma l'Iniezione di Dipendenza è un tipo specifico di Inversione di Controllo. Questo è il motivo per cui ricevi risposte così confuse, indipendentemente da dove appari.

    
risposta data 22.07.2013 - 18:50
fonte
30

Ecco i normali programmi di controllo del flusso normalmente seguiti:

  • Esegui i comandi in sequenza
  • Si mantiene il controllo sul flusso di controllo del programma

Inversion of Control "inverte" quel flusso di controllo, cioè lo capovolge sulla sua testa:

  • Il tuo programma non controlla più il flusso. Piuttosto che chiamare comandi come ritieni opportuno, attendi che qualcun altro ti chiami .

L'ultima riga è quella importante. Piuttosto che chiamare qualcun altro quando ne hai voglia, qualcun altro ti chiama quando ne ha voglia.

Un esempio comune di ciò sono i framework web come Rails. Si definiscono i controller, ma in realtà non si decide quando vengono chiamati. Rails li chiama quando decide che ce n'è bisogno.

    
risposta data 22.07.2013 - 19:33
fonte
12

Riguarda chi controlla l'istanziazione delle dipendenze.

Tradizionalmente, quando una classe / metodo ha bisogno di usare un'altra classe (dipendenza), viene istanziata direttamente dalla classe / metodo. Controlla le sue dipendenze.

Con Inversion of Control (IoC), il chiamante è passato nella dipendenza, quindi crea un'istanza della dipendenza. Il chiamante controlla le dipendenze.

Il controllo di dove una dipendenza è istanziata è stato invertito - invece di essere nella "parte inferiore", dove è il codice che ne ha bisogno, viene istanziato nella "top", dove viene chiamato il codice che ne ha bisogno .

    
risposta data 22.07.2013 - 18:40
fonte
2

Chiamate di codice di livello più alto (cioè, controlli) codice di livello inferiore. Main () chiama function (), function () chiama libraryFunction ().

Questo può essere invertito, quindi la funzione di libreria di basso livello nella parte inferiore chiama le funzioni di livello superiore.

Perché lo faresti? Middleware. A volte vuoi controllare il livello superiore e il livello inferiore, ma c'è un sacco di lavoro nel mezzo che non vuoi fare. Prendi l'implementazione di quicksort nello stdlib C . Chiami quicksort al livello più alto. Si passa a qsort () un puntatore a funzione alla propria funzione che implementa un comparatore su qualunque cosa si voglia. Quando viene chiamato qsort (), chiama questa funzione comparatore. qsort () controlla / chiama / guida la tua funzione di alto livello.

    
risposta data 22.07.2013 - 20:23
fonte
1

Ecco una semplice panoramica:

  1. Controllo si riferisce a ciò che il programma fa dopo
  2. Al primo livello, ci sono in genere due cose che controllano il controllo: l'applicazione stessa e l'utente

Nei tempi passati, il controllo era di proprietà dell'applicazione prima e l'utente seconda. Se l'applicazione aveva bisogno di qualcosa da parte dell'utente, si fermava e chiedeva e quindi passava alla sua attività successiva. L'interazione dell'utente ha fornito principalmente dati piuttosto che controllare cosa l'applicazione ha fatto successivamente. Questo è un po 'estraneo a noi al giorno d'oggi dal momento che non vediamo questo tipo di comportamento molto spesso.

Se lo spostiamo e diamo il controllo primario all'utente, abbiamo invertito il controllo. Ciò significa che al posto dell'utente che aspetta che l'applicazione gli doni qualcosa da fare, l'applicazione rimane in attesa che l'utente gli dia qualcosa da fare. Le GUI sono un ottimo esempio di questo e praticamente tutto con un ciclo di eventi ha il controllo invertito.

Si noti che il mio esempio è al livello più alto e che questo concetto di inversione del controllo può essere astratto a diversi livelli di controllo all'interno dell'applicazione (cioè iniezione di dipendenza). Questo potrebbe essere il motivo per cui è così difficile ottenere una risposta diretta.

    
risposta data 11.05.2015 - 17:26
fonte
0

Cerchiamo di capirlo attraverso i due esempi.

Esempio 1

Nei giorni precedenti, le app utilizzate per generare comandi chiedevano di accettare gli input dell'utente uno dopo l'altro. Oggi, i framework dell'interfaccia utente istanziano vari elementi dell'interfaccia utente, eseguono il ciclo di vari eventi di tali elementi dell'interfaccia utente (come mouse hover, clic ecc.) E i programmi utente / principale forniscono hook (ad esempio ascoltatori di eventi UI in Java) per l'ascolto di tali eventi. Quindi il "controllo" del flusso di elementi dell'interfaccia utente viene spostato dal programma utente al framework dell'interfaccia utente. Nei giorni precedenti era nel programma utente.

Esempio 2

Considera la classe CustomerProcessor di seguito:

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}

Se voglio che processCustomer() sia indipendente da qualsiasi implementazione di getAllCusts() , non solo quella fornita da SqlCustRepo , dovrò eliminare la riga: SqlCustRepo custRepo = new SqlCustRepo() e sostituirla con qualcosa di più generico, in grado di accettare vari tipi di implementazione, in modo tale che processCustomers() funzioni semplicemente per qualsiasi implementazione fornita. Il codice sopra (istanza richiesta classe SqlCustRepo dalla logica del programma principale) è un modo tradizionale e non raggiunge questo obiettivo di disaccoppiamento processCustomers() dall'implementazione di getAllCusts() . In inversione di controllo, il contenitore crea un'istanza della classe di implementazione richiesta (come specificato dalla configurazione xml), la inietta nella logica del programma principale che viene associata come per gli hook specificati (ad esempio dal metodo @Autowired o getBean() in primavera quadro).

Vediamo come questo può essere fatto. Considera il codice seguente.

Config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>

CustRepo.java

interface ICustRepo 
{ ... }

JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }

App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}

Possiamo anche avere

class GraphCustRepo implements ICustRepo { ... }   

e

<bean id="custRepo" class="GraphCustRepo">

e non avremo bisogno di cambiare App.java.

Sopra il contenitore (che è il framework di primavera) ha la responsabilità di scansionare il file xml, creare un'istanza del bean di tipo specifico e iniettarlo nel programma utente. Il programma utente non ha alcun controllo su quale classe viene istanziata.

PS: IoC è un concetto generico ed è raggiunto in molti modi. Gli esempi sopra lo raggiungono per iniezione di dipendenza.

Riferimento: articolo di Martin Fowler .

    
risposta data 03.08.2018 - 11:50
fonte