【问题标题】:Point of using Dependency Injection (and for that matter an IoC Container) in LISP在 LISP 中使用依赖注入(以及 IoC 容器)的要点
【发布时间】:2012-01-14 00:55:00
【问题描述】:

我阅读了 ESR 的文章“如何成为一名黑客?”几年前(可以在我的个人资料中找到链接),Eric 建议学习 LISP。好吧,我学习 LISP 已经有一段时间了,我非常喜欢它,所以我决定使用它编写一个 Web 应用程序。

由于我使用 Spring 有一段时间,我认为编写解耦组件并使用 IoC 容器和依赖注入将它们粘合在一起是个好主意。我在 google 上进行了强力搜索,结果发现在 LISP 中没有实现这样的想法。我错过了什么吗?这个概念在 LISP 中是否有很好的实现,或者由于某种我还不清楚的原因使用它没有意义?

【问题讨论】:

  • Common Lisp 是动态类型的,具有全功能的匿名函数,OO 是基于泛型函数的。我在 IoC/DI 模式方面没有足够的经验,无法确定这是一个完整的答案,但它们不是被语言本身所包含吗?
  • 嗯,这是我希望得到答案的问题。 :)
  • @Ramarren,依赖注入仅仅意味着软件构建块不自己寻找它们的依赖,而是“从外部”接收它们。在我看来,这样的设计模式不能被语言特性所包含,当然不能被动态类型或匿名函数所包含。 可以包含的是 IoC 容器,因为完全可以手动将对象图组合在一起。也许在 Lisp 中这很容易,人们根本不需要任何额外的库来为他们做这件事。 (我很高兴在 Objective-C 中手动进行 DI。)

标签: spring dependency-injection inversion-of-control common-lisp


【解决方案1】:

“控制反转”在 Lisp 中被广泛使用。这很简单,因为函数和闭包是一流的对象。

依赖注入是微不足道的。类和函数可以通过符号和头等类来配置。

Common Lisp 中通常不需要用于 IoC 或 DI 的“框架”,因为内置了许多用于配置和参数化应用程序和库的功能。

'first class' 表示某些东西可以存储在变量中、作为参数传递或作为结果返回。

在像 Common Lisp 这样的语言中,函数和类是第一类对象。另外,为了通过后期绑定解耦,您可以使用符号作为它们的名称。 Common Lisp 对象系统知道元类和符号作为类的名称。甚至通用函数和方法也是对象并且具有元类。

如果concurrent-secure-server 是一个类,default-response 是一个函数,你可以这样做:

(make-instance 'web-services
               :server-class 'concurrent-secure-server
               :default-response-function 'default-reponse)

上面使用符号作为类和函数的名称。如果函数获得新版本,Web 服务可能会稍后调用新版本。

或者:

(make-instance 'web-services
               :server-class (find-class 'concurrent-secure-server)
               :default-response-function #'default-reponse)

在上面的例子中,我们传递了类对象和函数对象。

在 Common Lisp 软件模块中可以有全局变量,你可以用正确的信息来设置:

 (defvar *default-server-class* 'concurrent-secure-server)

或者,您可以将它们设置在如下插槽中。

(defclass server-object ()
  ((default-response-function
      :initarg :default-response-function
      :initform *server-default-response-function*)))

(defvar *my-server*
   (make-instance 'server-object
                  :default-response-function 'my-default-response-function))

您甚至可以创建对象,然后在配置阶段更改它们的类。 Common Lisp 对象系统允许您更改类并更新现有对象。

如果你创建一个实例,你可以随心所欲:

  • 你可以通过类
  • 你可以传入参数

像这样:

(let ((my-class 'foo-class)
      (my-args  `(:response-function ',*some-reponse-function)))
  (apply #'make-instance my-class my-args))

有时您会看到在运行时计算此类参数列表的 Lisp 库。

您可以在运行时配置 Lisp 应用程序的另一件事是通过泛型函数。通用函数允许 :before、:after 和 :around 方法——它们甚至允许您自己的自定义调用方案。因此,通过使用您自己的从其他类和 mixin 类继承的类,可以重新配置泛型函数。这就像您内置了面向切面编程的基本机制。

对于那些对这些更高级的面向对象概念感兴趣的人,Xerox PARC 提供了一些文献,其中在创建 CLOS 时已经研究了这些问题。当时它被称为“开放式实现”:

http://www2.parc.com/csl/groups/sda/publications.shtml

在开放式实施方法中,模块允许其客户单独控制模块自己的实施策略。这允许客户端定制模块的实现策略以更好地满足他们的需求,有效地使模块更可重用,客户端代码更简单。该控件通过精心设计的辅助界面提供给客户端。

你不需要的东西:XML。 Common Lisp 是它自己的配置语言。例如,可以通过宏来编写扩展以简化配置。可以通过LOAD 轻松加载这些配置。

【讨论】:

【解决方案2】:

实际上,IoC 是大多数 Web 框架的构建原则,不仅在 Java 或 Lisp 中。 考虑到 DI,正如 Rammaren 所指出的,它是动态语言(如 Lisp)中的一种隐含模式。如果您比较 SpringRestas(受良好支持的 CL 网络框架之一)中的 Hello World 应用程序,您可以自己看到这一点。你会看到,除了在 Lisp 中不需要花哨的类型/类/接口声明之外,还有相同的模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-19
    • 1970-01-01
    相关资源
    最近更新 更多