是否存在专门为依赖注入设计的编程语言?


21

许多通用编程语言都足够灵活,可以允许您支持依赖项注入。即使没有库或框架支持。但是,即使某种语言的图灵能力足以解决任何编程问题,该语言也会做出会影响其中易事和难事的选择。

是否有专门设计用来简化依赖注入的语言,反之,使得创建隐藏的依赖变得困难?

澄清:

由于某些语言(看着您的Java)的限制,许多人将连接和构造方面的帮助作为依赖项注入的一部分。在这里,我只打算为DI设计一种语言,以表示依赖关系不容易隐藏在副作用中。在配置系统上也有约定,只会增加一些影响。

我不是在寻找语言建议。这是一个历史问题。是否有任何语言作者明确提出要这样做?


1
密切相关(如果不是完全重复的话):如何将依赖项注入集成到语言中?
罗伯特·哈维

呜!3K!现在,我可以看着他们投票结束这个问题。:)感谢您的投票。
candied_orange

1
无论如何,您都应该阅读该帖子。“存在”是一个没有那么有趣的问题,除非您将其扩展为“他们是如何做到的?”之类的东西。
罗伯特·哈维

1
Haskell会计数吗?使用curring和高阶函数,您可以解决DI通常用OOP语言解决的大多数问题,并且由于其对纯度的严格要求,您被迫将IO等副作用分开,并且函数无法神奇地想到尚未作为参数传递的某些事物。您对配置没有任何约定,但是另一方面,由于我已经注意到,大多数团队在中型项目和大型项目上都无法信任它,因此即使在如今的OOP代码中,我也逐渐远离它。
wasatz

1
@wasatz:是的,Haskell很重要。实际上,大多数“软件模式”只是解决编程语言缺陷的解决方法。
罗伯特·哈维

Answers:


21

是的,确实有。有点。

Newspeak没有静态,也没有全局。这意味着访问依赖项的唯一可能方法是显式注入依赖项。显然,这意味着该语言,或更准确地说,对于Newspeak,IDE 需要使依赖注入变得容易,否则该语言将无法使用。

因此,语言不是为DI设计的,而是DI的必要性是语言设计的结果。

如果没有静态状态,也没有全局状态,那么您不能仅仅“伸出”手以太并拔出某些东西。例如,在Java中,包结构是静态的。我可以说java.lang.String,我自己String上课。这在Newspeak中是不可能的。您使用的所有内容都必须明确提供给您,否则您将无法使用。因此,所有都是依赖,每个依赖都是显式的。

你想要一个字符串吗?好吧,您必须首先要求stdlib对象将String课程交给您。哦,但是您如何访问stdlib?好吧,您必须首先要求platformstdlib物件交给您。哦,但是您如何访问platform?好吧,您必须首先请别人将platform物体交给您。哦,可是您如何联系那个人呢?好吧,您必须首先请另一个人将您的物品交给您。

这会走到兔子洞多远?递归在哪里停止?一路走,实际上。它并没有停止。那么,如何在Newspeak中编写程序?好吧,严格来说,你不能!

您需要一些外部实体将它们联系在一起。在Newspeak中,该实体是IDE。IDE会看到整个程序。它可以将不同的部分连接在一起。Newspeak的标准模式是您的应用程序的中心类有一个名为的访问器platform,Newspeak IDE将一个对象注入该访问器,该对象具有的方法可以返回一些编程的基本需求:一个String类,一个Number类,一个Array类,等等。

如果要测试您的应用程序,则可以注入一个platform对象,该对象的File方法返回带有伪方法的类。如果您想将应用程序部署到云中,请注入一个平台,该平台的File类实际上由Amazon S3支持。跨平台的GUI通过为不同的OS注入不同的GUI框架来工作。Newspeak甚至具有实验性的Newspeak-to-ECMAScript编译器和HTML支持的GUI框架,通过注入不同的GUI元素,您无需更改即可将功能齐全的GUI应用程序从本机桌面移植到浏览器中。

如果要部署应用程序,IDE可以将应用程序序列化为磁盘对象。(不像它的祖先,Smalltalk中,新话有出的图像的对象序列化格式,您不必采取整个图像用你,正是因为所有依赖注入:在IDE知道究竟该系统应用程序的部分因此,它精确地序列化了组成应用程序的对象空间的连接子图,仅此而已。)

所有这些简单地通过将面向对象的方法发挥到极致:一切都是虚拟方法调用(Smalltalk术语中的“消息发送”,Newspeak是其后代)。甚至超类查询也是一个虚拟方法调用!采取类似

class Foo extends Bar // using Java syntax for familiarity

或者,在Newspeak中:

class Foo = Bar () () : ()

在Java中,这将Foo在静态全局名称空间中创建一个名称,并Bar在静态全局名称空间中进行查找,并使其成为Bar Foo超类。即使在动态性更高的Ruby中,它仍将在全局名称空间中创建一个静态常量。

在Newspeak中,等效声明意味着:创建一个名为的getter方法,Foo并通过调用名为的方法使它返回一个查找其超类的类Bar。注意:这与Ruby不同,您可以在其中放置任何可执行的Ruby代码作为超类声明,但是该代码仅在创建类时执行一次,并且该代码的返回值成为固定的超类。否。每次查找单个方法都会Bar调用该方法!

这具有深远的意义:

  • 因为mixin基本上是尚不知道其超类的类,并且在Newspeak中,超类是动态的虚拟方法调用,因此未知,每个类自动也是mixin。您可以免费获得mixins。
  • 由于内部类只是返回一个类的方法调用,因此您可以在外部类的子类中覆盖该方法,因此每个类都是虚拟的。您可以免费获得虚拟类:

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

    新闻发言人:

    class Outer = () (
      class Inner = () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Inner = () () : ()
    ) : ()
    
  • 由于超类只是一个返回类的方法调用,因此您可以在外部类的子类中覆盖该方法,因此在超类中定义的内部类在子类中可以具有不同的超类。您可以免费获得类层次结构继承:

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

    新闻发言人:

    class Outer = () (
      class MyCoolArray = Array () () : ()
    ) : ()
    
    class Sub = Outer () (
      class Array = () () : ()
    ) : ()
    
  • 最后,对于本次讨论而言,最重要的是:由于(显然,除了您在类中定义的那些),您只能在词汇上封闭的类和父类(最顶层的最外层类)中调用方法不能调用任何方法只是明确注入的:一个顶级类不具有封闭类,它的方法也可以打电话,也不能有非默认的其他超类,因为超类声明是一个方法调用,它显然不能转到超类(它父类),它也不能进入词汇上封闭的类,因为没有任何类。这意味着顶级类被完全封装,它们只能访问它们显式注入的内容,而它们仅被注入显式要求的内容。换句话说:顶级类是模块。您可以免费获得整个模块系统。实际上,更准确地说:顶级类是模块声明,其实例是模块。因此,您将免费获得带有参数化模块声明和一流模块的模块系统,这是许多甚至非常复杂的模块系统都无法做到的。

为了使所有这些注入变得轻松,类声明具有不寻常的结构:它们由两个声明组成。一个是类构造函数,它不是构造类实例的构造函数,而是构造类主体运行环境的构造函数。用类似Java的语法,看起来像这样:

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

新闻发言人:

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

请注意,Newspeak程序员实际查看类的方式是这样的:Newspeak IDE显示多个嵌套类

不过,我什至无法开始这样做。您必须自己玩。吉拉德·布拉查(Gilad Bracha)就系统的各个方面(包括模块化)进行了几次演讲他进行了很长时间的演讲(2小时),其中的第一小时是对语言的全面介绍,包括模块化的故事。Newspeak编程平台的第2章介绍了模块化。如果您在Squeak上浏览Newspeak –《困惑的指南》(又名Newspeak-101),就会对系统有所了解。Newspeak by Example是一个实时文档(例如,它在ECMASCript的Newspeak上运行,每行代码都是可编辑的,每个结果都是可检查的),展示了基本语法。

但实际上,您必须尝试一下。它与所有主流甚至大多数非主流语言都有很大的不同,以至于难以解释,必须要有经验。


3
嗯 禁止使用静态和全局状态,几乎所有现代编程语言都可以这样说。
罗伯特·哈维

很好奇,我的许多手工注射容器都是静态工厂。以前没有想到这是一件坏事。
candied_orange

@Jörg有什么办法可以备份这个答案呢?我已经用Google搜索“ newspeaklanguage.org依赖项注入”,结果空白。我能找到的最接近的东西是:news.ycombinator.com/item?id=9620561
candied_orange

@CandiedOrange:我当时正在扩大答案,但是后来“现实世界”介入了。那个更好吗?
约尔格W¯¯米塔格

3
@JörgWMittag废话!好吧,肯定是“更多”。请稍等,我评估为“更好”。可能需要洗手间的力量才能解决这个问题。
candied_orange

7

Wake编程语言旨在使用依赖项注入。基本上,它等效于语言本身中引入的依赖项注入框架。类定义它们的参数needprovide然后编译器将所有内容挂钩。


6

它不是一种实用的语言,但是本文描述的系统具有一个有趣的效果:它允许您使用抽象类/接口(包括实例化它们)编写一个抽象类,然后可以通过替换的子类来使您的类具体化。您在实例化点使用的每个抽象类。这样做至少在简单的情况下消除了依赖注入的需要,例如(使用具有此功能的虚拟Java扩展版本),我们可以采用以下代码:

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

并将Client及其用法替换为:

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

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

请注意,这简化了依赖性的使用点,而不是创建。这也使我们能够避免使用Factory模式(因为可以根据需要随时创建新的I)。


有趣。这让我想起了Scala的类型成员,它也可以在子类中重写,并在超类中保留抽象。抽象类型成员与自我类型注释相结合,构成了Scala的模块化和依赖注入方法的基础。Scala的设计师Martin Odersky和Newspeak的设计师Gilad Bracha都引用了这篇论文,我一点也不感到惊讶。
约尔格W¯¯米塔格

0

我没有使用过它,但是Plastic编程语言的官方标语是“ 如果进行依赖注入并将其烘焙到编程语言中会发生什么? ”。看起来很有趣

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.