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使用Traits进行此操作,它带来了一些其他好东西,这些好东西不是整个传播抽象模块类的核心,而不是多个源文件的想法. C#具有部分类,它们提供相同的功能,并且还允许嵌套类.据推测,其他一些语言对嵌套类具有类似的支持,以及在多个文件上拆分类.
这种模式是否出现在C#或任何其他语言的任何地方?我认为许多语言的大型项目都面临着抽象模块要解决的问题.是否有任何理由将“抽象类作为抽象模块”的东西不起作用,因此没有使用?在我看来,这是一个比各种DI框架更清晰的解决方案,它提供了相同的功能.
解决方法
>它是一个封闭的模块,意味着它提供了允许与模块交互的所有接口
>您可以指定模块上的操作
>您可以指定作为模块一部分的类型 – 通常这些类型由上述模块操作
>没有提供实现 – 这是抽象的部分:可以有许多具体的实现,程序中实际使用的将由程序指定和选择,因为它最适合需要
能够使用多个源文件指定模块的功能不是核心要求,但它当然可以派上用场.
在最基本的形式中,模块描述抽象数据类型(例如队列):可用于与数据类型交互的操作以及交互所需的任何辅助类型.
在更复杂的形式中,它可以描述整个子系统(例如,网络).
在命令式语言中,您通常使用接口来实现相同的目的:
>随附
>您可以指定操作
>您可以指定属于该界面的类型
>没有实施
如您所述,如果您的模块具有大型接口(例如,描述子系统),则在一个文件中编写实现丰富接口的类通常是不切实际的.如果该语言不支持将同一个类拆分为单独的源(或更准确地说:将同一类的不同部分从不同的源文件“粘合”在一起),解决方案通常是失去所附的要求并提供一系列指定它们之间交互的接口 – 因此您获得了子系统的API(它是最纯粹的API:它是与子系统交互的接口,尚未实现).
在某些方面,后一种方法可以比封闭类型更通用(在你能用它实现的意义上是通用的):你可以提供来自不同作者的各种子类型(通过接口指定)的实现:只要子类型只依赖于指定的接口进行相互交互,这种混合匹配方法将起作用.
大多数函数式编程语言的优势之一是参数化数据类型,您可以在其中实例化另一个日期类型作为其参数(例如整数队列). Java / C#中的泛型(以及C中的模板)实现了相同的灵活性.当然,基于类型系统的语言之间的确切含义和表达能力可能不同.
整个讨论是独立形式的依赖注入(DI),它试图通过显式提供所需的部分(而不是具有实现选择)来放松类型的具体实现与其支持部分之间的强依赖性,作为用户该类型可能更好地理解这些部分的哪些实现最能实现其目标 – 例如提供测试功能的模拟实现.
DI尝试解决的问题仅限于命令式语言,您也可以在函数式语言中遇到相同的依赖性问题:抽象模块的实现可能会选择使用子类型的特定实现(从而将自身耦合到这些实现)而不是将子类型实现作为参数(这是DI的目标)