Existe-t-il un langage de programmation spécialement conçu pour l'injection de dépendances?

21

De nombreux langages de programmation généraux sont suffisamment flexibles pour vous permettre de prendre en charge l'injection de dépendance. Même sans support de bibliothèque ou de framework. Mais même si un langage est suffisamment complet pour résoudre tout problème de programmation, un langage fait des choix qui ont un impact sur ce qui est facile et ce qui est difficile à faire en eux.

Existe-t-il un langage spécialement conçu pour faciliter l'injection de dépendances et inversement, rendre difficile la création de dépendances cachées?

Clarification:

En raison des limitations de certains langages (en vous regardant Java), de nombreuses personnes considèrent l'assistance au câblage et à la construction comme faisant partie de l'injection de dépendances. Ici, je veux seulement un langage conçu pour DI pour signifier que les dépendances ne sont pas facilement cachées dans les effets secondaires. Avoir également une convention sur le système de configuration ne serait qu'ajouter de la sauce.

Je ne cherche pas de recommandation linguistique. C'est une question historique. Un auteur de langue a-t-il déjà explicitement décidé de le faire?

candied_orange
la source
1
Fortement lié, sinon carrément dupliqué: Comment l'injection de dépendance pourrait-elle être intégrée dans le langage?
Robert Harvey
Whee! 3K! Je peux maintenant les voir voter pour clore cette question. :) Merci pour les votes.
candied_orange
1
Vous devriez quand même lire ce post. "Existe-t-il" est une question beaucoup moins intéressante, à moins que vous ne l'étendiez à quelque chose comme "comment l'ont-ils fait?"
Robert Harvey
1
Est-ce que Haskell compterait? Avec les fonctions de curry et d'ordre supérieur, vous pouvez résoudre la plupart des problèmes que DI résout généralement dans les langages OOP et avec sa rigueur sur la pureté, vous êtes obligé de séparer les effets secondaires comme IO, etc. Vous n'avez aucune convention sur la configuration, mais d'un autre côté, je m'éloigne lentement de cela même dans mon code OOP de nos jours car j'ai remarqué que la plupart des équipes ne peuvent pas faire confiance à cela sur des projets de taille moyenne et plus grands.
wasatz
1
@wasatz: Oui, Haskell compte. La plupart des «modèles de logiciels» ne sont en fait que des solutions de contournement aux lacunes du langage de programmation.
Robert Harvey

Réponses:

21

Oui, en effet. Sorte de.

Newspeak n'a ni état statique ni état global. Cela signifie que le seul moyen possible d'accéder à une dépendance est de l'injecter explicitement. Évidemment, cela signifie que le langage, ou dans le cas de Newspeak plus précisément, l'IDE doit faciliter l'injection de dépendances, sinon le langage sera inutilisable.

Ainsi, le langage n'est pas conçu pour DI, mais la nécessité de DI est une conséquence de la conception du langage.

S'il n'y a pas d'état statique ni d'état global, alors vous ne pouvez pas simplement "tendre la main" dans l'éther et retirer quelque chose. Par exemple, en Java, la structure du package est un état statique. Je peux juste dire java.lang.Stringet j'ai moi-même la Stringclasse. Ce n'est pas possible dans Newspeak. Tout ce avec quoi vous travaillez doit vous être explicitement fourni, sinon vous ne pouvez pas y accéder. Donc, tout est une dépendance, et chaque dépendance est explicite.

Tu veux une chaîne? Eh bien, vous devez d'abord demander à l' stdlibobjet de vous remettre la Stringclasse. Oh, mais comment avez-vous accès à la stdlib? Eh bien, vous devez d'abord demander platformà vous remettre l' stdlibobjet. Oh, mais comment avez-vous accès à la platform? Eh bien, vous devez d'abord demander à quelqu'un d'autre de vous remettre l' platformobjet. Oh, mais comment avez-vous accès à cette personne? Eh bien, vous devez d'abord demander à quelqu'un d'autre de vous remettre l'objet.

Jusqu'où descend le terrier du lapin? Où s'arrête la récursivité? Tout le chemin, en fait. Ça ne s'arrête pas. Alors, comment pouvez-vous écrire un programme dans Newspeak? Eh bien, à strictement parler, vous ne pouvez pas!

Vous avez besoin d'une entité extérieure qui relie tout cela. Dans Newspeak, cette entité est l'IDE. L'IDE voit tout le programme. Il peut câbler les pièces disparates ensemble. Le modèle standard dans Newspeak est que la classe centrale de votre application a un accesseur appelé platform, et l'IDE Newspeak injecte un objet dans cet accesseur qui a des méthodes qui renvoient certaines des nécessités de base de la programmation: une Stringclasse, une Numberclasse, une Arrayclasse, etc.

Si vous souhaitez tester votre application, vous pouvez injecter un platformobjet dont la Fileméthode renvoie une classe avec des méthodes factices. Si vous souhaitez déployer votre application dans le cloud, vous injectez une plate-forme dont la Fileclasse est réellement soutenue par Amazon S3. Les interfaces graphiques multiplateformes fonctionnent en injectant différents cadres d'interface graphique pour différents systèmes d'exploitation. Newspeak a même un compilateur expérimental Newspeak-to-ECMAScript et une infrastructure graphique basée sur HTML qui vous permet de porter une application GUI complète depuis le bureau natif dans le navigateur sans aucun changement, juste en injectant différents éléments GUI.

Si vous souhaitez déployer votre application, l'EDI peut sérialiser l'application en un objet sur disque. (Contrairement à son ancêtre, Smalltalk, Newspeak a un format de sérialisation d'objet hors image. Vous n'avez pas besoin de prendre la totalité de l'image avec vous, précisément parce que toutes les dépendances sont injectées: l'EDI sait exactement quelles parties du système votre application utilise et ce qu'il ne fait pas. Donc, il sérialise exactement le sous-graphe connecté de l'espace objet qui comprend votre application, rien de plus.)

Tout cela fonctionne simplement en poussant à l'extrême l'orientation objet: tout est un appel de méthode virtuel ("message send" dans la terminologie Smalltalk, dont Newspeak est un descendant). Même la recherche de superclasse est un appel de méthode virtuelle! Prenez quelque chose comme

class Foo extends Bar // using Java syntax for familiarity

ou, dans Newspeak:

class Foo = Bar () () : ()

En Java, cela va créer un nom Foodans l'espace de noms global statique, rechercher Bardans l'espace de noms global statique et créer Bar Foola superclasse. Même dans Ruby, qui est beaucoup plus dynamique, cela créera toujours une constante statique dans l'espace de noms global.

Dans Newspeak, la déclaration équivalente signifie: créer une méthode getter nommée Fooet lui faire retourner une classe qui recherche sa superclasse en appelant la méthode nommée Bar. Remarque: ce n'est pas comme Ruby, où vous pouvez mettre n'importe quel code Ruby exécutable comme déclaration de superclasse, mais le code ne sera exécuté qu'une seule fois lorsque la classe sera créée et la valeur de retour de ce code deviendra la superclasse fixe. Non. La méthode Barest appelée pour chaque recherche de méthode!

Cela a de profondes implications:

  • puisqu'un mixin est fondamentalement une classe qui ne connaît pas encore sa superclasse, et dans Newspeak, la superclasse est un appel de méthode virtuelle dynamique, et donc inconnue, chaque classe est automatiquement aussi un mixin. Vous obtenez des mixins gratuitement.
  • puisqu'une classe interne est juste un appel de méthode qui renvoie une classe, vous pouvez remplacer cette méthode dans une sous-classe de la classe externe, donc chaque classe est virtuelle. Vous obtenez des cours virtuels gratuitement:

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

    Newspeak:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • puisque la superclasse est juste un appel de méthode qui renvoie une classe, vous pouvez remplacer cette méthode dans une sous-classe de la classe externe, les classes internes définies dans la superclasse peuvent avoir une superclasse différente dans la sous-classe. Vous obtenez gratuitement l'héritage de la hiérarchie des classes:

    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 = () () : ()
    ) : ()
    
  • et enfin, le plus important pour cette discussion: puisque (en dehors de celles que vous avez définies dans votre classe, évidemment), vous ne pouvez appeler que des méthodes dans vos classes lexiquement englobantes et vos superclasses, une classe la plus externe de niveau supérieur ne peut pas appeler de méthodes du tout, sauf celles qui sont explicitement injectées: une classe de niveau supérieur n'a pas de classe englobante dont elle pourrait appeler les méthodes, et elle ne peut pas avoir une superclasse autre que la classe par défaut, car la déclaration de superclasse est un appel de méthode, et il ne peut évidemment pas aller à la superclasse (il estla superclasse) et il ne peut pas non plus aller à la classe lexicalement car il n'y en a pas. Cela signifie que les classes de niveau supérieur sont complètement encapsulées, elles ne peuvent accéder qu'à ce qu'elles sont explicitement injectées et elles ne reçoivent que ce qu'elles demandent explicitement. En d'autres termes: les classes de niveau supérieur sont des modules. Vous obtenez gratuitement un système complet de modules. En fait, pour être plus précis: les classes de niveau supérieur sont des déclarations de module, ses instances sont des modules. Ainsi, vous obtenez gratuitement un système de modules avec des déclarations de modules paramétriques et des modules de première classe, ce que de nombreux systèmes de modules, même très sophistiqués, ne peuvent pas faire.

Afin de rendre toute cette injection indolore, les déclarations de classe ont une structure inhabituelle: elles consistent en deux déclarations. L'un est le constructeur de classe, qui n'est pas le constructeur qui construit des instances de la classe, mais plutôt le constructeur qui construit l'environnement dans lequel le corps de classe s'exécute. Dans une syntaxe de type Java, cela ressemblerait à ceci:

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 () () : ()
) : ()

Notez que la façon dont un programmeur Newspeak va réellement voir les classes est comme ceci:Newspeak IDE affichant plusieurs classes imbriquées

Mais je ne peux même pas commencer à lui rendre justice. Vous devrez jouer avec vous-même. Gilad Bracha a donné quelques conférences sur divers aspects du système, y compris la modularité. Il a donné un très long exposé (2 heures) , dont la première heure est une introduction approfondie à la langue, y compris l'histoire de la modularité. Le chapitre 2 de la plateforme de programmation Newspeak traite de la modularité. Si vous parcourez Newspeak sur Squeak - A Guide for the Perplexed (aka Newspeak-101) , vous aurez une idée du système. Newspeak by Example est un document en direct (c'est-à-dire qu'il s'exécute à l'intérieur du port Newspeak-on-ECMASCript, chaque ligne de code est modifiable, chaque résultat est inspectable) démontrant la syntaxe de base.

Mais vraiment, vous devez jouer avec. Il est tellement différent de toutes les langues traditionnelles et même de la plupart des langues non traditionnelles qu'il est difficile à expliquer, il doit être expérimenté.

Jörg W Mittag
la source
3
Meh. Interdisez l'utilisation de l'état statique et de l'état global, et vous pourriez dire cela à propos de presque tous les langages de programmation modernes.
Robert Harvey
Curieux, beaucoup de mes contenants d'injection fabriqués à la main sont des usines statiques. Je n'avais jamais pensé à ça comme une mauvaise chose auparavant.
candied_orange
@ Jörg Pouvez-vous sauvegarder cette réponse un peu plus? J'ai googlé "injection de dépendance newspeaklanguage.org" et suis venu vide. La chose la plus proche que j'ai pu trouver était la suivante: news.ycombinator.com/item?id=9620561
candied_orange
@CandiedOrange: J'étais en train d'élargir la réponse, mais le "monde réel" est intervenu. Est-ce mieux?
Jörg W Mittag
3
@ JörgWMittag Merde sacrée! Et bien c'est certainement "plus". Accrochez-vous pendant que j'évalue "mieux". Pourrait avoir besoin de la puissance d'une visite de salle de bain pour passer à travers cela.
candied_orange
7

Le langage de programmation Wake est conçu pour utiliser l'injection de dépendances. Fondamentalement, il a l'équivalent d'un cadre d'injection de dépendances intégré au langage lui-même. Les classes définissent les paramètres qu'elles needet provideet le compilateur connecte tout.

Winston Ewert
la source
6

Ce n'est pas un langage pratiquement utile, mais le système décrit dans cet article a un effet intéressant: il vous permet d'écrire une classe abstraite en utilisant des classes / interfaces abstraites (y compris en les instanciant). Votre classe peut ensuite être concrétisée en substituant une sous-classe de chaque classe abstraite que vous avez utilisée au moment de l'instanciation. Cela supprime le besoin d'injection de dépendance dans au moins des cas simples, par exemple (en utilisant une version hypothétique de Java étendue avec cette fonctionnalité), nous pourrions prendre ce code:

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

et remplacer le Client et son utilisation par:

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

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

Notez que cela simplifie le point d'utilisation d'une dépendance, plutôt que la création. Cela nous permet également d'éviter le modèle Factory (en tant que nouveau, je peux être créé à la demande quand nous le voulons).

Jules
la source
Intéressant. Cela me rappelle les membres de type de Scala, qui peuvent également être remplacés dans les sous-classes et laissés abstraits dans une superclasse. Les membres de type abstrait combinés avec des annotations de type personnel forment la base de l'approche de Scala pour la modularité et l'injection de dépendances. Je ne suis pas du tout surpris que ce document ait été cité à la fois par Martin Odersky , le concepteur de Scala, et Gilad Bracha , le concepteur de Newspeak.
Jörg W Mittag
0

Je ne l'ai pas utilisé, mais le slogan officiel du langage de programmation Plastic est " Que se passe-t-il si vous prenez l'injection de dépendances et que vous le faites cuire dans un langage de programmation? ". Cela semble assez intéressant

B1CL0PS
la source