模块和类之间的层次关系是什么?

Dan*_*try -2 ruby

在.NET中,我们有类型interfaceclass.这两种类型之间存在一些关系.在这个例子中,我们拥有.NET中的所有关系:

class Vehicle
{
}

interface IMovable
{
   void Move();
}

class Engine
{
}

class Car : Vehicle, IMovable
{
   private readonly Engine _engine;
   public Car(Engine engine) { _engine = engine; }

   public void Move() {}
}
Run Code Online (Sandbox Code Playgroud)
  • Car延伸Vehicle- aCar is-a Vehicle
  • Car工具IMovable- aCar can-do IMovable
  • Car需要Engine创造自己 - aCar has-a Engine

但我不了解Ruby的模块,因为它们具有功能,但它们不是接口(因为它们定义了方法体,因此它们更类似于子类),但它们不是超类(因为多重继承不是在Ruby中完成的) ).

有人可以帮我理解Ruby中模块的用法,并可能对.NET进行类比吗?

编辑:看到3票因重复而关闭,我必须澄清. 我问的是类和模块之间的关系是什么(假设实现接口的类是can-do在.NET中)."重复"答案并未解决该问题.

Jör*_*tag 5

模块可用于Ruby中的两件事:作为常量的命名空间和mixins.

命名空间的用法与C#命名空间非常相似:模块可以包含常量(并且所有常量都包含在模块中,即使您没有明确地看到它们,在这种情况下它们属于它们Object),这些常量被命名为该模块.所以,Foo::BARBaz::BAR是即使它们具有相同的名称的两个不同的常数.

Mixins有点棘手.查看mixin的一种方法是,mixin是一个不知道其超类(或由其超类参数化的类)的类.好的,这听起来有点令人费解.我们来看一个例子.

想象一下,这样的事情在C#中是可能的:

interface IEachable<E> {
  IEachable<E> Each(Action<E> block);
}

class Enumerable<S, E> : S where S : IEachable<E> {
  List<T> Map(Func<E, T> block) {
    var res = new List<T>();
    this.Each(e => {†res.Add(block(e));});
    return res;
  }
}
Run Code Online (Sandbox Code Playgroud)

所以,你有一个Enumerable继承自其类型参数S(超类)的泛型类.这实际上是Enumerable对Ruby中mixin 的非常准确的描述.每次将泛型类实例化为具体类型时,它都以不同的超类结束.

因此,同一班级出现多重继承层次结构中的时间,用每一次不同的超类,但始终只是一个.

这与多继承不同,在多继承中,同一个类在继承层次结构中出现一次,具有多个超类.

这个类总是只有一个超类的属性称为线性化,它解决了传统多重继承所带来的许多问题,这通常与继承DAG中两个类之间存在多个可能路径的事实有关,而对于mixin线性化,只有一个继承,所以总有一条可能的路径.

特别是,当您将模块混合MC具有超类的类时,会发生这种情况S:

module M; end

class C < S
  include M
end
Run Code Online (Sandbox Code Playgroud)

当你打电话时include,Ruby会

  1. 创建一个新类,M'其方法表指针,常量表指针,类变量表指针和实例变量表指针指向M方法表,常量表,类变量表和实例变量表
  2. M'将超类设置为C当前的超类(即S)
  3. C将超类设置为M'

这样继承层次结构现在看起来像这样:

C < M' < S
Run Code Online (Sandbox Code Playgroud)

[注意:实际上 include委托append_features,它确实完成了上述工作,就像Ruby中的其他几乎一样,你可以通过覆盖实际改变这种行为append_features,但那是先进的元魔法.

那么,拥有一个可以在继承层次结构中的多个位置使用的类的实际意义是什么?好吧,您可以使用它来实现不相关类的常见行为.

再看一下Enumerablemixin:它为符合以下协议的任何对象提供了通用行为:它必须有一个each方法,yield所有元素都是一个接一个并返回self.而已.这些对象的类之间不需要任何类型的继承关系.所需要的只是他们以each适当的方式回应.

这引出了我们关于接口的问题:这是一个接口.虽然在Ruby中,我们通常将其称为协议.Ruby中的协议非常类似于C#中的接口,但是,有一个至关重要的区别:Ruby中没有协议这样的东西.协议是潜在的,不是明显的,它们只存在于程序员的头脑中,文档中,而不存在于源代码中,因此它们不能被语言检查.