Existe uma linguagem de programação projetada especificamente para injeção de dependência?


21

Muitas linguagens de programação gerais são flexíveis o suficiente para permitir o suporte à injeção de dependência. Mesmo sem suporte de biblioteca ou estrutura. Mas mesmo que uma linguagem seja Turing completa o suficiente para resolver qualquer problema de programação, uma linguagem faz escolhas que afetam o que é fácil e o que é difícil de fazer nelas.

Existe algum idioma que foi projetado especificamente para facilitar a injeção de dependência e, inversamente, dificultar a criação de dependências ocultas?

Esclarecimento:

Devido às limitações de algumas linguagens (olhando para o Java), muitas pessoas consideram a assistência com fiação e construção como parte da injeção de dependência. Aqui, pretendo apenas que uma linguagem projetada para DI signifique que as dependências não são facilmente ocultas em efeitos colaterais. Ter uma convenção sobre o sistema de configuração também seria apenas um molho.

Não estou procurando uma recomendação de idioma. É uma questão histórica. Algum autor de idioma já se dispôs explicitamente a fazer isso?


1
Fortemente relacionado, se não totalmente duplicado: como a injeção de dependência pode ser integrada ao idioma?
Robert Harvey

Whee! 3K! Agora eu posso vê-los votar para encerrar esta pergunta. :) Obrigado pelos votos.
Candied_orange 25/05

1
Você deve ler esse post de qualquer maneira. "Existe" é uma pergunta muito menos interessante, a menos que você a expanda para algo como "como eles fizeram isso?"
Robert Harvey

1
Haskell contaria? Com funções de curry e de ordem superior, você pode resolver a maioria dos problemas que o DI geralmente soluciona em linguagens OOP e, com seu rigor na pureza, é forçado a separar efeitos colaterais como IO etc. Você não recebe nenhuma convenção sobre a configuração, mas, por outro lado, estou me afastando lentamente disso mesmo no meu código OOP hoje em dia, pois notei que a maioria das equipes não pode ser confiável em projetos de tamanho médio e maiores.
wasatz 26/05

1
@wasatz: Sim, Haskell conta. A maioria dos "padrões de software" são realmente apenas soluções alternativas para falhas na linguagem de programação.
Robert Harvey

Respostas:


21

Sim, de fato existe. Tipo de.

O Newspeak não tem estado estático nem estado global. Isso significa que a única maneira possível de obter acesso a uma dependência é injetá-la explicitamente. Obviamente, isso significa que a linguagem, ou no caso do Newspeak, mais precisamente, o IDE precisa facilitar a injeção de dependência, caso contrário, a linguagem será inutilizável.

Portanto, a linguagem não foi projetada para DI, mas sua necessidade é uma conseqüência do design da linguagem.

Se não houver um estado estático e nenhum estado global, você não pode simplesmente "alcançar" o éter e extrair algo. Por exemplo, em Java, a estrutura do pacote é estado estático. Eu posso apenas dizer java.lang.Stringe eu tenho a Stringclasse. Isso não é possível no Newspeak. Tudo o que você trabalha tem que ser explicitamente fornecido a você, caso contrário, você simplesmente não conseguirá. Portanto, tudo é uma dependência e toda dependência é explícita.

Você quer uma corda? Bem, você deve primeiro pedir ao stdlibobjeto que lhe entregue a Stringclasse. Ah, mas como você tem acesso ao stdlib? Bem, você deve primeiro pedir a platformmão para entregar o stdlibobjeto. Ah, mas como você tem acesso ao platform? Bem, você precisa primeiro pedir a alguém para lhe entregar o platformobjeto. Ah, mas como você tem acesso a alguém que mente? Bem, você precisa primeiro pedir a outra pessoa para lhe entregar o objeto.

Até onde fica a toca do coelho? Onde a recursão pára? Todo o caminho, na verdade. Isso não para. Então, como você pode escrever um programa no Newspeak? Bem, estritamente falando, você não pode!

Você precisa de alguma entidade externa que amarre tudo isso. No Newspeak, essa entidade é o IDE. O IDE vê o programa inteiro. Pode conectar as peças díspares. O padrão padrão no Newspeak é que a classe central do seu aplicativo tenha um acessador chamado platforme o Newspeak IDE injeta um objeto nesse acessador que possui métodos que retornam algumas das necessidades básicas da programação: uma Stringclasse, uma Numberclasse, uma Arrayclasse, e assim por diante.

Se você quiser testar seu aplicativo, poderá injetar um platformobjeto cujo Filemétodo retorne uma classe com métodos fictícios. Se você deseja implantar seu aplicativo na nuvem, injeta uma plataforma cuja Fileclasse é realmente apoiada pelo Amazon S3. As GUIs de plataforma cruzada funcionam injetando diferentes estruturas de GUI para diferentes sistemas operacionais. O Newspeak ainda possui um compilador experimental Newspeak-to-ECMAScript e uma estrutura de GUI suportada por HTML que permite transportar um aplicativo de GUI com todos os recursos da área de trabalho nativa para o navegador sem alterações, apenas injetando diferentes elementos da GUI.

Se você deseja implantar seu aplicativo, o IDE pode serializar o aplicativo em um objeto em disco. (Diferentemente de seu ancestral, Smalltalk, o Newspeak possui um formato de serialização de objeto fora de imagem. Você não precisa levar a imagem inteira com você, precisamente porque todas as dependências foram injetadas: o IDE sabe exatamente quais partes do sistema seu aplicativo usa e que não usa. Portanto, serializa exatamente o subgrafo conectado do espaço de objeto que compreende seu aplicativo, nada mais.)

Tudo isso funciona simplesmente levando a orientação ao objeto ao extremo: tudo é uma chamada de método virtual ("envio de mensagem" na terminologia Smalltalk, da qual o Newspeak é um descendente). Até a pesquisa da superclasse é uma chamada de método virtual! Tome algo como

class Foo extends Bar // using Java syntax for familiarity

ou, no Newspeak:

class Foo = Bar () () : ()

Em Java, isso criará um nome Foono espaço para nome global estático e procurará Barno espaço para nome global estático e criará a Bar Foosuperclasse. Mesmo no Ruby, que é muito mais dinâmico, isso ainda criará uma constante estática no espaço para nome global.

No Newspeak, a declaração equivalente significa: crie um método getter chamado Fooe faça com que ele retorne uma classe que consulta sua superclasse chamando o método chamado Bar. Nota: não é como Ruby, onde você pode colocar qualquer código Ruby executável como a declaração da superclasse, mas o código será executado apenas uma vez quando a classe for criada e o valor de retorno desse código se tornar a superclasse fixa. Não. O método Baré chamado para todas as pesquisas de método!

Isso tem implicações profundas:

  • Como um mixin é basicamente uma classe que ainda não conhece sua superclasse, e no Newspeak, a superclasse é uma chamada de método virtual dinâmica e, portanto, desconhecida, toda classe também é automaticamente um mixin. Você ganha mixins de graça.
  • como uma classe interna é apenas uma chamada de método que retorna uma classe, você pode substituir esse método em uma subclasse da classe externa, para que cada classe seja virtual. Você recebe aulas virtuais de graça:

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

    Newspeak:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • como a superclasse é apenas uma chamada de método que retorna uma classe, você pode substituir esse método em uma subclasse da classe externa, as classes internas definidas na superclasse podem ter uma superclasse diferente na subclasse. Você recebe a herança da hierarquia de classes 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`!!!
    }
    

    Newspeak:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • e, finalmente, o mais importante para esta discussão: como (além dos que você definiu em sua classe, obviamente), você só pode chamar métodos em suas classes lexicamente anexas e em suas superclasses, uma classe externa de nível superior não pode chamar nenhum método , exceto os injetados explicitamente: uma classe de nível superior não possui uma classe envolvente cujos métodos poderia chamar e não pode ter uma superclasse diferente da padrão, porque a declaração da superclasse é chamada de método, e obviamente não pode ir para a superclasse ( éa superclasse) e também não pode ir para a classe anexa lexicamente, porque não há nenhuma. O que isso significa é que as classes de nível superior são completamente encapsuladas, elas só podem acessar o que são explicitamente injetadas e apenas o que pedem explicitamente. Em outras palavras: as classes de nível superior são módulos. Você recebe todo um sistema de módulos gratuitamente. De fato, para ser mais preciso: as classes de nível superior são declarações de módulo, suas instâncias são módulos. Portanto, você obtém um sistema de módulo com declarações paramétricas e módulos de primeira classe gratuitamente, algo que muitos sistemas de módulos, mesmo muito sofisticados, não podem fazer.

Para tornar toda essa injeção indolor, as declarações de classe têm uma estrutura incomum: elas consistem em duas declarações. Um é o construtor da classe, que não é o construtor que constrói instâncias da classe, mas o construtor que constrói o ambiente em que o corpo da classe é executado. Em uma sintaxe semelhante a Java, seria algo como isto:

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

Observe que a maneira como um programador de Newspeak realmente verá as classes é assim:IDE Newspeak exibindo várias classes aninhadas

Eu não posso nem começar a fazer justiça, no entanto. Você terá que brincar com você mesmo. Gilad Bracha deu algumas palestras sobre vários aspectos do sistema, incluindo a modularidade. Ele fez uma palestra muito longa (duas horas) , cuja primeira hora é uma introdução completa ao idioma, incluindo a história da modularidade. O capítulo 2 da plataforma de programação Newspeak cobre a modularidade. Se você der uma olhada no Newspeak no Squeak - um guia para os perplexos (também conhecido como Newspeak-101) , terá uma ideia do sistema. O Newspeak por exemplo é um documento ativo (ou seja, está sendo executado dentro da porta Newspeak-on-ECMASCript, todas as linhas de código são editáveis, todos os resultados são inspecionáveis) demonstrando a sintaxe básica.

Mas realmente, você tem que brincar com isso. É tão diferente de todas as linguagens convencionais e até da maioria das linguagens não convencionais que é difícil de explicar que precisa ser experimentado.


3
Meh. Proibir o uso de estado estático e estado global, e você poderia dizer isso sobre quase qualquer linguagem de programação moderna.
Robert Harvey

Curioso, muitos dos meus recipientes de injeção feitos à mão são fábricas estáticas. Não tinha pensado nisso como uma coisa ruim antes.
Candied_orange 25/05

@ Jörg De alguma maneira você poderia fazer backup dessa resposta um pouco mais? Pesquisei no Google "injeção de dependência do jornaleaklanguage.org" e vim vazio. Mais próxima coisa que eu poderia encontrar era esta: news.ycombinator.com/item?id=9620561
candied_orange

@ CandiedOrange: eu estava no processo de expandir a resposta, mas o "mundo real" interferiu. Isto é melhor?
Jörg W Mittag

3
@ JörgWMittag Puta merda! Bem, é certamente "mais". Espere enquanto eu avalio "melhor". Pode precisar do poder de uma visita ao banheiro para superar isso.
Candied_orange 26/05

7

A linguagem Wake Programming foi projetada para usar injeção de dependência. Basicamente, ele tem o equivalente a uma estrutura de injeção de dependência inserida na própria linguagem. As classes definem os parâmetros que neede providee os ganchos do compilador tudo.


6

Não é uma linguagem praticamente útil, mas o sistema descrito neste artigo tem um efeito interessante: permite escrever uma classe abstrata usando classes / interfaces abstratas (incluindo instanciando-as). Sua classe pode ser concretizada substituindo uma subclasse de cada classe abstrata que você usou no momento da instanciação. Isso elimina a necessidade de injeção de dependência em pelo menos casos simples, por exemplo (usando uma versão hipotética do Java estendida com esse recurso), podemos usar este código:

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 substitua o Cliente e seu uso por:

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

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

Observe que isso simplifica o ponto de uso de uma dependência, em vez de criação. Também nos permite evitar o padrão de fábrica (como um novo, posso ser criado sob demanda sempre que quisermos).


Interessante. Isso me lembra os Membros Tipo de Scala, que também podem ser substituídos nas subclasses e deixados abstratos em uma superclasse. Resumo Os membros do tipo combinados com as anotações de tipo próprio formam a base da abordagem da Scala para modularidade e injeção de dependência. Não estou surpreso que este artigo tenha sido citado por Martin Odersky , o designer da Scala, e Gilad Bracha , o designer do Newspeak.
Jörg W Mittag

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.