我对JVM Web应用程序的开发相对较新,对Scala开发来说仍然相对较新。我看到这种方法有明显的好处,但我需要在某种程度上说服我的团队其他人像我一样喜欢它。
我目前在Scalatra框架之上编写了一个内容管理系统。我很快发现我的设计非常愚蠢,因为它看起来像这样(示意性地):
class MyServlet extends MyStack with Plugin1 with Plugin2 with Plugin3 //etc.
trait Plugin1 { self: MyServlet => /*...*/ }
trait Plugin2 { self: MyServlet with Plugin1 => /*...*/ }
// etc.
简单地分解特征中的功能,"解决"通过自我类型的依赖关系,并从这些特征组成主类。除了两件事之外,它的工作原理非常好:没有代码隔离(一个人必须知道其他特征),现在我的主要问题是编译的速度(并看到更改生效)。
我现在正在使用SBT增量编译。就像这样,当我对链中早期出现的某些特性进行一些修改时,很可能许多类失效并且编译需要很长时间(对于我仍然非常小的应用程序,容器重启通常需要大约一分钟)。我的同事们习惯于使用无类型的脚本语言 - 能够立即看到他们的变化。
我即将以这样的方式重写我的应用程序,至少可以显着减轻这个问题。我想我应该做的第一件事是使应用程序模块更加独立(可能使用依赖注入)。但我正在考虑进一步采用它并允许模块在运行时换成新版本(即使没有重新启动容器)。我知道有JRebel,但我现在想坚持使用免费的解决方案。
我想出了一个解决方案的想法,但我不确定它是否可行。我的Web应用程序中的模块通常会挂接某些路由,并为应用程序的其余部分提供一些公共接口。 (让我们假设接口MyModule
和实现MyModuleImpl
)
def use[T]: Usage[T]
然后,人们可以使用use[MyModule]
来获得其依赖性(与DI非常相似的概念)。然而,与DI相比,它不会直接注入MyModuleImpl
的实例,而是将其包装在Usage[T]
内。此类可能如下所示(最简单的形式):
class Usage[T] {
def get: T
}
我希望get
方法返回"最新的" T
的实例。可以为实现接口T
的任何类提供新的源代码 - 如果可以针对当前加载的T
编译它,则Usage[T]
的{{1}}实例获取实例T
的新实现以及对T
的任何后续调用都将返回此实例。 (因此任何模块都将透明地使用get
的新版本)
这是否可以实现这样的系统(理想情况下,在一些现有的图书馆如Scaldi的帮助下进行DI部分)或者它更像是一个无法实现的乌托邦?或者你建议我加快开发周期?