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

时间:2014-03-08 19:16:16

标签: 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() {}
}
  • Car延伸Vehicle - Car is-a Vehicle
  • Car实施IMovable - Car can-do IMovable
  • Car需要Engine来创建自己 - Car has-a Engine

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

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

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

2 个答案:

答案 0 :(得分: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;
  }
}

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

因此,相同的类在继承层次结构中出现多次次,每次都使用不同的超类,但始终只是一个

这与多继承不同,其中相同的类在继承层次结构中出现一次,其中多个超类。

这个属性,即类总是只有一个超类,被称为线性化,它解决了传统多重继承所带来的许多问题,这些问题通常与多个这样的事实有关。继承DAG中两个类之间的可能路径,而使用mixin线性化,只有一个继承,因此总是只有一个可能的路径。

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

module M; end

class C < S
  include M
end

当你致电include时,Ruby会

  1. 创建一个新类M',其方法表指针,常量表指针,类变量表指针和实例变量表指针指向M方法表,常量表,类变量表和实例变量表
  2. M'的超类设置为C当前的超类(即S
  3. C的超类设为M'
  4. 这样继承层次结构现在看起来像这样:

    C < M' < S
    

    [注意:实际 include委托给append_features,这真的完成了上述工作,就像Ruby中的其他几乎一样,你实际上可以改变这种行为重写append_features,但那是先进的元魔法。]

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

    再次,查看Enumerable mixin:它为符合以下协议的任何对象提供了通用行为:它必须具有each方法yield所有元素之一 - by-one并返回self。就是这样。这些对象的类之间不需要任何类型的继承关系。所需要的只是他们以适当的方式回复each

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

答案 1 :(得分:0)

我也来自.NET,所以我对你现在有同样的怀疑。简短的回答是,在C#或Java等语言中没有这样的“模块”概念。 Ruby模块的想法是使用可重用的代码来包含或扩展不同类的功能。 This article非常好地解释了 mixin 概念:

  

mixin是一种在多个类之间共享代码的方法   与鸭子打字密切相关。

一旦您学习了 mixin duck typing 概念,您就会完全理解Ruby哲学。来自Sandi Metz的This book详细解释了所有这些OOP概念,但是使用了Java / C#透视图。在阅读之后,所有新的Ruby概念对我来说都是有意义的。