Gibt es eine Programmiersprache, die speziell für die Abhängigkeitsinjektion entwickelt wurde?

21

Viele allgemeine Programmiersprachen sind flexibel genug, um das Einfügen von Abhängigkeiten zu unterstützen. Auch ohne Bibliotheks- oder Framework-Unterstützung. Aber selbst wenn eine Sprache vollständig genug ist, um ein Programmierproblem zu lösen, trifft eine Sprache Entscheidungen, die sich auf das auswirken, was leicht und was schwer zu tun ist.

Gibt es eine Sprache, die speziell dafür entwickelt wurde, die Abhängigkeitsinjektion zu vereinfachen und umgekehrt das Erstellen versteckter Abhängigkeiten zu erschweren?

Klärung:

Aufgrund der Einschränkungen einiger Sprachen (mit Blick auf Java) betrachten viele Leute die Unterstützung bei der Verkabelung und Konstruktion als Teil der Abhängigkeitsinjektion. Hier beabsichtige ich nur eine Sprache, die für DI entwickelt wurde, um zu bedeuten, dass Abhängigkeiten nicht leicht in Nebenwirkungen versteckt werden. Eine Konvention über ein Konfigurationssystem zu haben, würde nur Sauce hinzufügen.

Ich suche keine Sprachempfehlung. Es ist eine historische Frage. Hat sich jemals ein Sprachautor explizit vorgenommen, dies zu tun?

kandierte_orange
quelle
1
Stark verwandt, wenn nicht gar doppelt: Wie kann die Abhängigkeitsinjektion in die Sprache integriert werden?
Robert Harvey
Whee! 3K! Jetzt kann ich sehen, wie sie abstimmen, um diese Frage zu schließen. :) Danke für die Stimmen.
candied_orange
1
Sie sollten diesen Beitrag trotzdem lesen. "Existiert es?" Ist eine weitaus weniger interessante Frage, es sei denn, Sie erweitern sie auf etwas wie "Wie haben sie es gemacht?".
Robert Harvey
1
Würde Haskell zählen? Mit currying und höherwertigen Funktionen können Sie die meisten Probleme lösen, die DI im Allgemeinen in OOP-Sprachen löst. Aufgrund seiner strengen Reinheit sind Sie gezwungen, Nebeneffekte wie E / A usw. zu trennen, und Funktionen können nicht auf magische Weise etwas heraufbeschwören, das ihnen nicht als Argument übergeben wurde. Über die Konfiguration herrscht keine Einigkeit, aber andererseits entferne ich mich auch in meinem OOP-Code langsam davon, da mir aufgefallen ist, dass den meisten Teams dies bei mittelgroßen und größeren Projekten nicht vertraut werden kann.
Wasatz
1
@wasatz: Ja, Haskell zählt. Die meisten "Softwaremuster" sind wirklich nur Problemumgehungen für Mängel in der Programmiersprache.
Robert Harvey

Antworten:

21

Ja, das gibt es tatsächlich. Art von.

Newspeak hat keinen statischen und keinen globalen Zustand. Dies bedeutet, dass die einzige Möglichkeit, auf eine Abhängigkeit zuzugreifen, darin besteht, sie explizit einzufügen. Offensichtlich bedeutet dies , dass die Sprache, oder im Fall von Neusprech genauer gesagt die IDE Bedürfnisse zu Dependency Injection leicht zu machen, da sonst die Sprache wird unbrauchbar.

Die Sprache ist also nicht für DI ausgelegt, sondern die Notwendigkeit für DI ist eine Konsequenz des Sprachentwurfs.

Wenn es keinen statischen und keinen globalen Zustand gibt, kann man nicht einfach in den Äther "hineingreifen" und etwas herausziehen. In Java ist die Paketstruktur beispielsweise ein statischer Zustand. Ich kann nur sagen java.lang.Stringund ich habe selbst die StringKlasse. Das ist in Newspeak nicht möglich. Alles, mit dem Sie arbeiten, muss Ihnen explizit zur Verfügung gestellt werden, sonst können Sie es einfach nicht erreichen. Also, alles ist eine Abhängigkeit, und jede Abhängigkeit ist explizit.

Du willst einen String? Nun, Sie müssen zuerst das stdlibObjekt bitten , Ihnen die StringKlasse zu übergeben. Oh, aber wie kommst du zum stdlib? Naja, du musst den erst bitten platform, dir das stdlibObjekt zu übergeben. Oh, aber wie kommst du zum platform? Nun, Sie müssen zuerst jemanden bitten, Ihnen das platformObjekt zu übergeben. Oh, aber wie kommst du zu dieser Person? Nun, Sie müssen erst noch jemanden bitten, Ihnen das Objekt zu übergeben.

Wie weit geht das im Kaninchenbau? Wo hört die Rekursion auf? Eigentlich den ganzen Weg. Es hört nicht auf. Wie können Sie dann ein Programm in Newspeak schreiben? Genau genommen kann man das nicht!

Sie brauchen eine externe Instanz, die alles zusammenhält. In Newspeak ist diese Entität die IDE. Die IDE sieht das gesamte Programm. Es kann die unterschiedlichen Stücke zusammen verdrahten. Das Standardmuster in Newspeak ist, dass die zentrale Klasse Ihrer Anwendung einen Accessor namens platformhat und die Newspeak-IDE ein Objekt in diesen Accessor einfügt, das über Methoden verfügt, die einige der grundlegenden Programmieranforderungen zurückgeben: eine StringKlasse, eine NumberKlasse, eine ArrayKlasse, und so weiter.

Wenn Sie Ihre Anwendung testen möchten, können Sie ein platformObjekt einfügen, dessen FileMethode eine Klasse mit Dummy-Methoden zurückgibt. Wenn Sie Ihre Anwendung in der Cloud bereitstellen möchten, fügen Sie eine Plattform ein, deren FileKlasse tatsächlich von Amazon S3 unterstützt wird. Plattformübergreifende GUIs funktionieren durch Injizieren verschiedener GUI-Frameworks für verschiedene Betriebssysteme. Newspeak verfügt sogar über einen experimentellen Newspeak-to-ECMAScript-Compiler und ein HTML-unterstütztes GUI-Framework, mit dem Sie eine voll funktionsfähige GUI-Anwendung vom nativen Desktop in den Browser portieren können, indem Sie einfach verschiedene GUI-Elemente einfügen.

Wenn Sie Ihre Anwendung bereitstellen möchten, kann die IDE die Anwendung in ein Objekt auf der Festplatte serialisieren. (Im Gegensatz zu seinem Vorgänger Smalltalk verfügt Newspeak über ein Out-of-Image-Objekt-Serialisierungsformat. Sie müssen nicht das gesamte Image mitnehmen, da alle Abhängigkeiten injiziert werden: Die IDE weiß genau, welche Teile des Systems Ihre Anwendung enthält verwendet und was nicht. Also serialisiert es genau den verbundenen Untergraphen des Objektraums, der Ihre Anwendung umfasst, nicht mehr.)

All dies funktioniert einfach, indem die Objektorientierung auf das Äußerste gehoben wird: Alles ist ein virtueller Methodenaufruf ("message send" in der Smalltalk-Terminologie, von der Newspeak abstammt). Sogar die Superklasse-Suche ist ein virtueller Methodenaufruf! Nimm so etwas wie

class Foo extends Bar // using Java syntax for familiarity

oder in Newspeak:

class Foo = Bar () () : ()

In Java wird dadurch ein Name Fooim statischen globalen Namespace erstellt und Barim statischen globalen Namespace nachgeschlagen und die Bar FooSuperklasse von make erstellt . Selbst in Ruby, das viel dynamischer ist, wird dadurch eine statische Konstante im globalen Namespace erstellt.

In Newspeak bedeutet die entsprechende Deklaration: Erstellen Sie eine Getter-Methode mit dem Namen Foound geben Sie eine Klasse zurück, deren Oberklasse durch Aufrufen der benannten Methode ermittelt wird Bar. Hinweis: Dies ist nicht wie bei Ruby, wo Sie jeden ausführbaren Ruby-Code als Superklasse-Deklaration ablegen können. Der Code wird jedoch nur einmal ausgeführt , wenn die Klasse erstellt wird und der Rückgabewert dieses Codes zur festen Superklasse wird. Nein. Die Methode Barwird für jede einzelne Methodensuche aufgerufen !

Dies hat einige tiefgreifende Auswirkungen:

  • Da ein Mixin im Grunde genommen eine Klasse ist, die ihre Oberklasse noch nicht kennt, und in Newspeak die Oberklasse ein dynamischer virtueller Methodenaufruf ist und daher unbekannt, ist jede Klasse automatisch auch ein Mixin. Sie erhalten Mixins kostenlos.
  • Da eine innere Klasse nur ein Methodenaufruf ist, der eine Klasse zurückgibt, können Sie diese Methode in einer Unterklasse der äußeren Klasse überschreiben, sodass jede Klasse virtuell ist. Sie erhalten kostenlos virtuellen Unterricht:

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

    Newspeak:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • Da es sich bei der Superklasse nur um einen Methodenaufruf handelt, der eine Klasse zurückgibt, können Sie diese Methode in einer Unterklasse der äußeren Klasse überschreiben. In der Superklasse definierte innere Klassen können eine andere Superklasse in der Unterklasse haben. Sie erhalten die Klassenhierarchie-Vererbung kostenlos:

    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`!!!
    }
    

    Newspeak:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • Und schließlich das Wichtigste für diese Diskussion: Da Sie (abgesehen von den in Ihrer Klasse definierten) nur Methoden in Ihrer lexikalisch einschließenden Klasse (n) und Ihrer Superklasse (n), einer äußersten Klasse der obersten Ebene, aufrufen können nicht aufrufen können alle Methoden an allen außer denen , die explizit injiziert werden: ist ein Top-Level - Klasse nicht eine umschließende Klasse , deren Methoden haben es nennen könnte, und es kann nicht eine übergeordnete Klasse andere als die Standard haben, weil die übergeordnete Klasse Erklärung a Methodenaufruf, und es kann offensichtlich nicht an die Oberklasse gehen (es istdie Oberklasse) und es kann auch nicht in die lexikalisch einschließende Klasse gehen, weil es keine gibt. Dies bedeutet, dass die Klassen der obersten Ebene vollständig gekapselt sind, dass sie nur auf das zugreifen können, was sie explizit injiziert bekommen, und dass sie nur injiziert bekommen, was sie explizit verlangen. Mit anderen Worten: Klassen der obersten Ebene sind Module. Sie erhalten ein komplettes Modulsystem kostenlos. Genauer gesagt: Klassen der obersten Ebene sind Moduldeklarationen, ihre Instanzen sind Module. So erhalten Sie kostenlos ein Modulsystem mit parametrischen Moduldeklarationen und erstklassigen Modulen, was viele, auch sehr anspruchsvolle Modulsysteme nicht können.

Um all diese Injektionen schmerzlos zu machen, haben Klassendeklarationen eine ungewöhnliche Struktur: Sie bestehen aus zwei Deklarationen. Eine davon ist die Klasse Konstruktor, das ist nicht der Konstruktor , das konstruiert Instanzen der Klasse, sondern der Konstruktor, der die Umgebung , in dem Körper läuft die Klasse konstruiert. In einer Java-ähnlichen Syntax würde dies ungefähr so ​​aussehen:

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"
}

Newspeak:

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

Beachten Sie, dass die Art und Weise ein Neusprech Programmierer tatsächlich gehen , um die Klasse (n) zu sehen , ist wie folgt:Newspeak IDE mit mehreren verschachtelten Klassen

Ich kann es aber noch nicht einmal richtig machen. Sie müssen selbst damit herumspielen. Gilad Bracha hat einige Vorträge zu verschiedenen Aspekten des Systems gehalten, einschließlich der Modularität. Er hielt einen wirklich langen (2-stündigen) Vortrag , in dessen erster Stunde eine gründliche Einführung in die Sprache einschließlich der Modularitätsgeschichte gegeben wurde. Kapitel 2 der Newspeak Programming Platform befasst sich mit der Modularität. Wenn Sie Newspeak on Squeak - Ein Leitfaden für Verblüffte (auch bekannt als Newspeak-101) überfliegen , bekommen Sie ein Gefühl für das System. Newspeak by Example ist ein Live-Dokument (dh es wird im Newspeak-on-ECMASCript-Port ausgeführt, jede Codezeile kann bearbeitet werden, jedes Ergebnis kann überprüft werden), das die grundlegende Syntax demonstriert.

Aber man muss wirklich damit herumspielen. Es unterscheidet sich so sehr von allen Mainstream- und sogar den meisten Nicht-Mainstream-Sprachen, dass es schwer zu erklären ist, es muss erlebt werden.

Jörg W. Mittag
quelle
3
Meh. Verbieten Sie die Verwendung von statischem und globalem Zustand, und das könnte man über fast jede moderne Programmiersprache sagen.
Robert Harvey
Seltsamerweise sind viele meiner handgefertigten Injektionsbehälter statische Fabriken. Hatte das vorher nicht als schlecht empfunden.
candied_orange
@ Jörg Könntest du diese Antwort irgendwie noch etwas mehr untermauern? Ich habe "newspeaklanguage.org Dependency Injection" gegoogelt und bin leer ausgegangen. Das Nächste, was ich finden konnte, war: news.ycombinator.com/item?id=9620561
candied_orange
@CandiedOrange: Ich war gerade dabei die Antwort zu erweitern aber dann hat sich die "reale Welt" eingemischt. Ist das besser?
Jörg W Mittag
3
@ JörgWMittag Heiliger Mist! Naja es ist bestimmt "mehr". Warte, während ich "besser" bewerte. Könnte die Kraft eines Badezimmerbesuchs benötigen, um dies zu überstehen.
candied_orange
7

Die Programmiersprache Wake wurde für die Verwendung der Abhängigkeitsinjektion entwickelt. Im Grunde hat es das Äquivalent eines Abhängigkeitsinjektions-Frameworks, das in die Sprache selbst eingebettet ist. Klassen definieren die Parameter, die sie needund provideund der Compiler verbindet alles.

Winston Ewert
quelle
6

Es ist keine praktisch nützliche Sprache, aber das in diesem Artikel beschriebene System hat einen interessanten Effekt: Sie können eine abstrakte Klasse mit abstrakten Klassen / Interfaces schreiben (einschließlich ihrer Instanziierung). Ihre Klasse kann dann durch Ersetzen einer Unterklasse von konkretisiert werden Jede abstrakte Klasse, die Sie zum Zeitpunkt der Instanziierung verwendet haben. Auf diese Weise wird die Abhängigkeitsinjektion zumindest in einfachen Fällen überflüssig. Beispielsweise (unter Verwendung einer mit dieser Funktion erweiterten hypothetischen Java-Version) könnten wir folgenden Code verwenden:

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

und ersetze den Client und seine Nutzung durch:

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

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

Beachten Sie, dass dies den Verwendungszweck einer Abhängigkeit vereinfacht und nicht die Erstellung. Es ermöglicht uns auch, das Factory-Muster zu vermeiden (da ein neues Muster jederzeit bei Bedarf erstellt werden kann).

Jules
quelle
Interessant. Dies erinnert mich an Scalas Typ-Member, die auch in Unterklassen überschrieben und in einer Superklasse abstrakt gelassen werden können. Abstract Type Members in Kombination mit Self-Type Annotations bilden die Grundlage für Scalas Ansatz zur Modularität und Abhängigkeitsinjektion. Es überrascht mich überhaupt nicht, dass dieses Papier sowohl von Martin Odersky , dem Designer von Scala, als auch von Gilad Bracha , dem Designer von Newspeak, zitiert wurde .
Jörg W Mittag
0

Ich habe es nicht benutzt, aber der offizielle Slogan der Programmiersprache Plastic lautet " Was passiert, wenn Sie Abhängigkeitsinjektion verwenden und sie in eine Programmiersprache backen? ". Es scheint ziemlich interessant zu sein

B1CL0PS
quelle