C#或其他语言的Scala风格的抽象模块?

Li *_*oyi 6 c# java architecture dependency-injection scala

我正在阅读Martin Odersky的书"Scala编程",其中有关抽象模块的部分,以及他的论文可扩展组件抽象:

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{}
}
Run Code Online (Sandbox Code Playgroud)

您可以实例化多个子类和具有不同具体特征的模块实例.这使您可以根据具体情况(例如,在测试期间替换数据库组件,或在开发环境中替换IO组件)以及使用它们自己的模块范围的可变状态集实例化多个模块来不同地配置模块.

根据我的理解,在基本层面上,唯一的硬性要求是您可以拥有嵌套类,这样封闭类可以充当模块.

它的另一个实际要求是你可以在多个文件中展开类定义,因为其中包含一堆类的模块可能比单个源文件中的大多数代码行更多.

Scala使用Traits进行此操作,它带来了一些其他好东西,这些好东西不是整个传播抽象模块类的核心,而不是多个源文件的想法.C#具有partial classes提供相同功能的,并且还允许嵌套类.据推测,其他一些语言对嵌套类具有类似的支持,以及在多个文件上拆分类.

这种模式是否出现在C#或任何其他语言的任何地方?我认为许多语言的大型项目都面临着抽象模块要解决的问题.是否有任何理由将"抽象类作为抽象模块"的东西不起作用,因此没有使用?在我看来,这是一个比各种DI框架更清晰的解决方案,它提供了相同的功能.

Mil*_*bin 8

通常的比较是ML模块,其中Scala traits(或抽象类)扮演ML签名的角色,他们的具体实现(通常是Scala对象)扮演ML结构的角色.这里对ML模块的讨论应该使连接合理清晰.

Scala和ML之间的模拟是有意的,如果你看一下Scala编译器的源代码,你会发现Scala对象通常是使用包含"Module"作为一部分的名称来引用的.


Att*_*ila 2

The abstract module you describe has the following core properties:

  • It is an enclosed module, meaning that it provides all the interface that allows interaction with the module
  • You can specify operations on the module
  • You can specify types that are part of the module -- usually these types are operated on by the above mentioned modules
  • No implementation is provided -- this is the abstract part: there can be many concrete implementations and which is actually used in the program will be specified and selected by the program as it best suits the need

The feature to be able to specify the module using more than one source file is not a core requirement, but it can certainly come in handy.

In its most basic form, a module is describing an abstract datatype (e.g. queue): what operations are available to interact with the datatype, and any auxiliary types that are needed for the interaction.

In a more complex form, it can describe a whole subsystem (e.g. networking).

In imperative languages you usually use an interface for the same purpose:

  • It is enclosed
  • You can specify operations
  • You can specify types that are part of the interface
  • No implementation

正如您所提到的,如果您的模块具有大型接口(例如描述子系统),则在一个文件中编写实现丰富接口的类通常是不切实际的。如果该语言不支持将同一类拆分为单独的源(或更准确地说:将不同源文件中的同一类的不同部分“粘合”在一起),解决方案通常是放弃所包含的要求并提供一系列指定它们之间交互的接口——因此您获得了子系统的 API(它是最纯粹意义上的 API:它是与子系统交互的接口,尚未实现)。

在某些方面,后一种方法可能比封闭类型更通用(通用是指您可以用它实现的功能):您可以提供来自不同作者的各种子类型(通过接口指定)的实现:只要子类型仅依赖于指定的接口来相互交互,这种混合匹配方法将起作用。

大多数函数式编程语言的优势之一是参数化数据类型,您可以使用另一个日期类型作为其参数(例如整数队列)来实例化一个日期类型。Java/C# 中的泛型(以及 C++ 中的模板)也实现了相同的灵活性。当然,根据类型系统的不同,确切的含义和表达能力可能会有所不同。

整个讨论是独立于依赖注入(DI)的,它试图通过显式提供所需的部分(而不是让实现选择)来放松类型的具体实现与其支持部分之间的强依赖性,因为该类型可能会更好地理解这些部分的哪种实现最能实现其目标——例如,为测试功能提供模拟实现。

DI 试图解决的问题是命令式语言所独有的,在函数式语言中也可能遇到相同的依赖问题:抽象模块的实现可能会选择使用子类型的特定实现(从而将自身耦合到这些实现)而不是将子类型实现作为参数(这就是 DI 的目标)