Esiste un linguaggio di programmazione appositamente progettato per l'iniezione delle dipendenze?


21

Molti linguaggi di programmazione generali sono abbastanza flessibili da consentire di supportare l'iniezione di dipendenza. Anche senza supporto di libreria o framework. Ma anche se una lingua è Turing abbastanza completa da risolvere qualsiasi problema di programmazione, una lingua fa delle scelte che incidono su ciò che è facile e ciò che è difficile da fare in essi.

Esiste un linguaggio appositamente progettato per rendere semplice l'iniezione di dipendenze e, al contrario, rendere difficile la creazione di dipendenze nascoste?

Una precisazione:

A causa delle limitazioni di alcune lingue (guardando Java) molte persone considerano l'assistenza per il cablaggio e la costruzione come parte dell'iniezione di dipendenza. Qui intendo solo un linguaggio progettato per DI per significare che le dipendenze non sono facilmente nascoste negli effetti collaterali. Avere una convenzione anche sul sistema di configurazione verrebbe aggiunto solo sugo.

Non sto cercando una raccomandazione linguistica. È una domanda storica. Qualche autore di lingue ha mai deciso esplicitamente di farlo?


1
Duplicazione fortemente correlata, se non addirittura chiara: come si può integrare l'iniezione di dipendenza nella lingua?
Robert Harvey,

Whee! 3K! Ora posso vederli votare per chiudere questa domanda. :) Grazie per i voti.
candied_orange

1
Dovresti comunque leggere quel post. "Esiste" è una domanda molto meno interessante, a meno che non la si espanda a qualcosa del tipo "come hanno fatto?"
Robert Harvey,

1
Haskell conta? Con le funzioni di curry e di ordine superiore puoi risolvere la maggior parte dei problemi che DI generalmente risolve nei linguaggi OOP e con la sua rigidità sulla purezza sei costretto a separare effetti collaterali come IO ecc. E le funzioni non possono evocare magicamente qualcosa che non sono state passate come argomento. Non si ottiene alcuna convenzione sulla configurazione, ma d'altra parte mi sto lentamente allontanando da questo anche nel mio codice OOP al giorno d'oggi poiché ho notato che la maggior parte dei team non si può fidare di ciò su progetti di medie dimensioni e più grandi.
wasatz,

1
@wasatz: Sì, conta Haskell. La maggior parte dei "modelli di software" sono in realtà solo soluzioni alternative per carenze nel linguaggio di programmazione.
Robert Harvey,

Risposte:


21

Sì, c'è davvero. Una specie di.

Newspeak non ha uno stato statico né uno stato globale. Ciò significa che l'unico modo possibile per ottenere l'accesso a una dipendenza è farla esplicitamente iniettare. Ovviamente, ciò significa che la lingua, o nel caso di Newspeak più precisamente, l'IDE deve rendere facile l'iniezione di dipendenza, altrimenti la lingua sarà inutilizzabile.

Quindi, il linguaggio non è progettato per DI, piuttosto la necessità di DI è una conseguenza del design del linguaggio.

Se non esiste uno stato statico né uno stato globale, non puoi semplicemente "raggiungere" l'etere ed estrarre qualcosa. Ad esempio, in Java, la struttura del pacchetto è statica. Posso solo dire java.lang.Stringe ho me stesso la Stringlezione. Questo non è possibile in Newspeak. Tutto ciò con cui lavori, ti deve essere esplicitamente fornito, altrimenti non puoi ottenerlo. Quindi, tutto è una dipendenza e ogni dipendenza è esplicita.

Vuoi una stringa? Bene, devi prima chiedere stdliball'oggetto di consegnarti la Stringclasse. Oh, ma come si accede a stdlib? Bene, devi prima chiedere platforma di consegnarti l' stdliboggetto. Oh, ma come si accede a platform? Bene, devi prima chiedere a qualcun altro di consegnarti l' platformoggetto. Oh, ma come si accede a quella persona? Bene, devi prima chiedere a qualcun altro di consegnarti l'oggetto.

Fino a che punto arriva nella tana del coniglio? Dove si ferma la ricorsione? Fino in fondo, in realtà. Non si ferma. Quindi, come puoi scrivere un programma in Newspeak? Beh, a rigor di termini, non puoi!

Hai bisogno di qualche entità esterna che lega tutto insieme. In Newspeak, quell'entità è l'IDE. L'IDE vede l'intero programma. Può collegare insieme i diversi pezzi. Lo schema standard in Newspeak è che la classe centrale della tua applicazione ha un accessor chiamato platforme l'IDE di Newspeak inietta un oggetto in quell'accessorio che ha metodi che restituiscono alcune delle necessità di base della programmazione: una Stringclasse, una Numberclasse, una Arrayclasse, e così via.

Se si desidera testare l'applicazione, è possibile iniettare un platformoggetto il cui Filemetodo restituisce una classe con metodi fittizi. Se desideri distribuire la tua applicazione sul cloud, devi iniettare una piattaforma la cui Fileclasse è effettivamente supportata da Amazon S3. Le GUI multipiattaforma funzionano iniettando diversi framework GUI per diversi sistemi operativi. Newspeak ha persino un compilatore sperimentale da Newspeak a ECMAScript e un framework GUI supportato da HTML che ti consente di trasferire un'applicazione GUI completamente funzionante dal desktop nativo nel browser senza modifiche, semplicemente iniettando diversi elementi GUI.

Se si desidera distribuire l'applicazione, l'IDE può serializzare l'applicazione in un oggetto su disco. (A differenza del suo antenato, Smalltalk, Newspeak ha un formato di serializzazione di oggetti fuori dall'immagine. Non è necessario portare l'intera immagine con sé, proprio perché tutte le dipendenze vengono iniettate: l'IDE conosce esattamente quali parti del sistema vengono utilizzate dall'applicazione usa e non lo fa. Quindi serializza esattamente il sottografo connesso dello spazio oggetti che comprende la tua applicazione, niente di più.)

Tutto questo funziona semplicemente portando l'orientamento agli oggetti all'estremo: tutto è una chiamata di metodo virtuale ("messaggio di invio" nella terminologia di Smalltalk, di cui Newspeak è un discendente). Anche la ricerca di superclasse è una chiamata di metodo virtuale! Prendi qualcosa del genere

class Foo extends Bar // using Java syntax for familiarity

o, in Newspeak:

class Foo = Bar () () : ()

In Java, questo creerà un nome Foonello spazio dei nomi globale statico, cercherà Barnello spazio dei nomi globale statico e creerà Bar Foola superclasse. Anche in Ruby, che è molto più dinamico, ciò creerà comunque una costante statica nello spazio dei nomi globale.

In Newspeak, la dichiarazione equivalente significa: creare un metodo getter denominato Fooe renderlo restituire una classe che cerca la sua superclasse chiamando il metodo denominato Bar. Nota: questo non è come Ruby, in cui è possibile inserire qualsiasi codice Ruby eseguibile come dichiarazione di superclasse, ma il codice verrà eseguito solo una volta quando viene creata la classe e il valore restituito di quel codice diventa la superclasse fissa. No. Il metodo Barviene chiamato per ogni singola ricerca del metodo!

Ciò ha alcune profonde implicazioni:

  • poiché un mixin è fondamentalmente una classe che non conosce ancora la sua superclasse, e in Newspeak la superclasse è una chiamata di metodo virtuale dinamica, e quindi sconosciuta, ogni classe è automaticamente anche un mixin. Ottieni mixin gratuitamente.
  • poiché una classe interna è solo una chiamata di metodo che restituisce una classe, è possibile sovrascrivere quel metodo in una sottoclasse della classe esterna, quindi ogni classe è virtuale. Ottieni lezioni virtuali gratuitamente:

    class Outer {
      class Inner { /* … */ }
    }
    
    class Sub extends Outer {
      override class Inner { /* … */ }
    }
    

    Neolingua:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • poiché la superclasse è solo una chiamata di metodo che restituisce una classe, è possibile sovrascrivere quel metodo in una sottoclasse della classe esterna, le classi interne definite nella superclasse possono avere una superclasse diversa nella sottoclasse. Ottieni l'eredità della gerarchia di classi gratuitamente:

    class Outer {
      class MyCoolArray extends Array { /* … */ }
    }
    
    class Sub extends Outer {
      override class Array { /* … */ }
      // Now, for instances of `Sub`, `MyCoolArray` has a different superclass 
      // than for instances of `Outer`!!!
    }
    

    Neolingua:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • e, infine, il più importante per questa discussione: poiché (a parte quelli che hai definito nella tua classe, ovviamente) puoi solo chiamare metodi nelle tue classi che si chiudono in modo lessicale e nelle tue superclassi, una classe più esterna di livello superiore non può chiamare qualsiasi metodo a tutti tranne quelli che sono esplicitamente iniettati: una classe di livello superiore non hanno una classe contenitrice cui metodi potrebbe chiamare, e non può avere una superclasse diversa da quella predefinita, poiché la dichiarazione superclasse è un metodo di chiamata, e ovviamente non può andare alla superclasse (lo èla superclasse) e inoltre non può andare alla classe che racchiude il lessico, perché non ce n'è. Ciò significa che le classi di livello superiore sono completamente incapsulate, possono accedere solo a ciò che vengono esplicitamente iniettate e vengono iniettate solo ciò che chiedono esplicitamente. In altre parole: le classi di livello superiore sono moduli. Ottieni un intero sistema di moduli gratuitamente. In effetti, per essere più precisi: le classi di livello superiore sono dichiarazioni di moduli, le sue istanze sono moduli. Quindi, ottieni un sistema di moduli con dichiarazioni di moduli parametrici e moduli di prima classe gratuiti, cosa che molti sistemi di moduli, anche molto sofisticati, non possono fare.

Al fine di rendere indolore questa iniezione, le dichiarazioni di classe hanno una struttura insolita: sono costituite da due dichiarazioni. Uno è il costruttore della classe, che non è il costruttore che costruisce istanze della classe, ma piuttosto il costruttore che costruisce l'ambiente in cui viene eseguito il corpo della classe. In una sintassi simile a Java, sarebbe simile a questo:

class Foo(platform) extends Bar {
  Array  = platform.collections.Array
  String = platform.lang.String
  File   = platform.io.File
| // separator between class constructor and class body
  class MyArray extends Array { /* … */ }
  // Array refers to the method defined above which in turn gets it from the 
  // platform object that was passed into the class "somehow"
}

Neolingua:

class Foo using: platform = Bar (
  Array = platform collections Array
  String = platform streams String 
  File = platform files ExternalReadWriteStream
) (
  class MyArray = Array () () : ()
) : ()

Nota che il modo in cui un programmatore di Newspeak vedrà effettivamente le classi è così:IDE di Newspeak che mostra più classi nidificate

Non posso nemmeno iniziare a rendergli giustizia, comunque. Dovrai giocarci da solo. Gilad Bracha ha tenuto un paio di conferenze su vari aspetti del sistema, inclusa la modularità. Ha tenuto un discorso davvero lungo (2 ore) , la cui prima ora è un'introduzione approfondita al linguaggio, inclusa la storia della modularità. Il capitolo 2 della piattaforma di programmazione Newspeak tratta la modularità. Se sfogli Newspeak su Squeak - A Guide for the Perplexed (alias Newspeak-101) , avrai un'idea del sistema. Newspeak per esempio è un documento live (cioè è in esecuzione all'interno della porta Newspeak-on-ECMASCript, ogni riga di codice è modificabile, ogni risultato è ispezionabile) che dimostra la sintassi di base.

Ma davvero, devi giocarci intorno. È così diverso da tutti i linguaggi mainstream e anche dalla maggior parte dei non mainstream che è difficile da spiegare, deve essere sperimentato.


3
Meh. Vieta l'uso dello stato statico e dello stato globale e potreste dirlo su quasi tutti i moderni linguaggi di programmazione.
Robert Harvey,

Curioso, molti dei miei contenitori per iniezione fatti a mano sono fabbriche statiche. Non l'avevo mai considerato una cosa negativa.
candied_orange

@ Jörg In qualche modo puoi eseguire il backup di questa risposta un po 'di più? Ho cercato su Google "Iniezione delle dipendenze di newspeaklanguage.org" e ne sono uscito vuoto. La cosa più vicina che potei trovare fu questa: news.ycombinator.com/item?id=9620561
candied_orange

@CandiedOrange: ero in procinto di espandere la risposta ma poi il "mondo reale" ha interferito. Va meglio?
Jörg W Mittag,

3
@ JörgWMittag Santa merda! Beh, è ​​sicuramente "di più". Aspetta mentre valuto "migliore". Potrebbe essere necessario il potere di una visita in bagno per superare questo.
candied_orange,

7

Il linguaggio di programmazione Wake è progettato per utilizzare l'iniezione di dipendenza. Fondamentalmente, ha l'equivalente di un framework di iniezione di dipendenza inserito nel linguaggio stesso. Classi definiscono i parametri che neede provideei ganci compilatore tutto.


6

Non è un linguaggio praticamente utile, ma il sistema descritto in questo documento ha un effetto interessante: ti permette di scrivere una classe astratta usando classi / interfacce astratte (inclusa la loro istanza) La tua classe può quindi essere concretizzata sostituendo una sottoclasse di ogni classe astratta che hai usato nel punto di istanza. In questo modo si elimina la necessità di iniezione di dipendenza in casi almeno semplici, ad esempio (utilizzando una versione ipotetica di Java estesa con questa funzione) si potrebbe prendere questo codice:

public interface I {
    void something ();
)

public class Concrete implements I {
    public void something () { ... }
}

public class Client {
    I myI;
    public Client (I injected) { myI = injected; }
    ...
}

...

    Client c = new Client (new Concrete());
    ...

e sostituisci il Cliente e il suo utilizzo con:

public class Client {
   I myI = new I();
   ...
}

   Client c = new Client { I -> Concrete } ();

Si noti che ciò semplifica il punto di utilizzo di una dipendenza, piuttosto che la creazione. Ci consente anche di evitare il modello Factory (come nuovo posso essere creato su richiesta ogni volta che vogliamo).


Interessante. Questo mi ricorda i membri tipo di Scala, che possono anche essere sovrascritti in sottoclassi e lasciati astratti in una superclasse. I membri di tipo astratto combinati con le annotazioni di tipo auto formano la base dell'approccio di Scala alla modularità e all'iniezione di dipendenza. Non mi sorprende affatto che questo articolo sia stato citato sia da Martin Odersky , il designer di Scala, sia da Gilad Bracha , il designer di Newspeak.
Jörg W Mittag,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.