我正在阅读Martin的书“Scala编程”中关于抽象模块的部分,以及他的论文Scalable组件抽象:
http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf
我的理解是,通过使模块抽象类而不是对象(或者像Java中那样的经典静态全局模块):
abstract class myModule{
// this is effectively an abstract module, whose concrete
// components can be configured by subclassing and instantiating it
class thing{}
class item{}
object stuff{}
class element{}
object utils{}
}您可以实例化具有不同具体特征的模块的多个子类和实例。这允许您根据具体情况(例如,在测试期间替换数据库组件,或者在开发环境中替换IO组件)以及实例化多个模块,每个模块都具有各自的模块作用域可变状态集。
据我所理解,在一个基本的层面上,唯一困难的要求是您可以拥有嵌套类,这样封闭类就可以充当一个模块。
另一个实际的要求是,您可以将类定义分散到多个文件上,因为在其中包含一组类的模块可能比在单个源文件中接受的代码行更多。
Scala这样做的特点,带来了一些其他的好东西,但不是核心的整个传播抽象模块类的多个源文件的想法。C#具有partial classes,它提供了相同的功能,并且还允许嵌套类。据推测,其他一些语言对嵌套类也有类似的支持,还可以将类拆分到多个文件上。
这种模式在C#或任何其他语言中都会出现吗?我认为许多语言中的大型项目都面临着抽象模块所要解决的问题。这种“抽象类作为抽象模块”的东西不起作用,因此不被使用,有什么原因吗?在我看来,这是一个比提供相同功能的各种DI框架更干净的解决方案。
发布于 2012-06-12 13:39:08
您描述的抽象模块具有以下核心属性:
能够使用多个源文件指定模块的特性不是核心需求,但它肯定会派上用场。
在其最基本的形式中,一个模块描述一个抽象数据类型(例如队列):哪些操作可用于与数据类型交互,以及交互所需的任何辅助类型。
以一种更复杂的形式,它可以描述整个子系统(例如网络)。
在命令式语言中,通常使用接口的目的是相同的:
正如您所提到的,如果您的模块有一个大型接口(例如描述一个子系统),那么在一个文件中编写实现富接口的类通常是不切实际的。如果语言不支持将同一个类分割成单独的源(或者更精确地说:将同一个类的不同部分与不同的源文件“粘合在一起”),那么解决方案通常是失去封闭的需求,并提供一系列接口来指定它们之间的交互--因此您可以为子系统获得一个API (从最简单的意义上讲,它是一个API :它是与子系统交互的接口,还没有实现)。
在某些方面,后一种方法可能比封闭的类型更通用(从您所能实现的意义上来说是泛型的):您可以提供来自不同作者的各种子类型(通过接口指定)的实现:只要子类型仅依赖指定的接口进行交互,这种混合-n匹配方法就能工作。
大多数函数式编程语言的优点之一是参数化数据类型,在这种情况下,您用另一个参数(例如,整数队列)实例化一个dayatype。Java/C#中的Generics (以及C++中的模板)实现了同样的灵活性。当然,根据语言类型系统的不同,语言的确切含义和表达能力也会有所不同。
整个讨论是单独的依赖项注入(DI),它试图通过显式提供所需的部件(而不是有实现选择)来放松类型的具体实现与其支持部件之间的强烈依赖,因为类型的用户可能更好地理解这些部分的实现是最适合实现其目标的--例如,为测试功能提供模拟实现。
DI试图解决的问题只适用于命令式语言,您也可以在函数式语言中遇到同样的依赖问题:抽象模块的实现可能选择使用特定的子类型实现(从而耦合到这些实现),而不是将子类型实现作为参数(这正是DI的目标)。
发布于 2012-06-08 08:39:22
通常比较的是ML模块,其中Scala特性(或抽象类)扮演ML签名的角色,它们的具体实现(通常是Scala对象)扮演ML结构的角色。对ML模块这里的讨论应该使连接变得更加清晰。
Scala和ML之间的模拟是故意的,如果您查看Scala编译器的源代码,就会发现Scala对象通常使用包含"Module“的名称作为一部分。
https://stackoverflow.com/questions/10944264
复制相似问题