【问题标题】:What's the hierarchical relationship between a Module and a Class? [duplicate]模块和类之间的层次关系是什么? [复制]
【发布时间】:2014-04-12 00:03:48
【问题描述】:

在 .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。 “重复”的答案并没有解决这个问题。

【问题讨论】:

标签: ruby


【解决方案1】:

在 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'

所以继承层次现在看起来像这样:

C < M' < S

[注意:实际上 include 代表 append_features,它确实完成了上述工作,就像 Ruby 中的几乎所有其他内容一样,您实际上可以通过覆盖 append_features 来改变这种行为,但那是高级元魔法。]

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

再次查看Enumerable mixin:它为符合以下协议的任何对象提供通用行为:它必须有一个each 方法,yields 所有元素一个接一个并返回@ 987654347@。而已。这些对象的类之间不需要任何类型的继承关系。只需他们以适当的方式回复each

这让我们想到了您关于接口的问题:那是一个接口。虽然在 Ruby 中,我们通常称它为 protocol。 Ruby 中的协议与 C# 中的接口非常相似,但是有一个关键区别:Ruby 中没有协议之类的东西。协议是潜在的,而不是清单,它们只存在于程序员的头脑中,存在于文档中,而在源代码中,因此它们不能由语言检查。

【讨论】:

    【解决方案2】:

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

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

    一旦您学习了 mixinduck typing 概念,您将完全理解 Ruby 哲学。来自 Sandi Metz 的This book 从 Java/C# 的角度详细解释了所有这些 OOP 概念。读完之后,所有新的 Ruby 概念对我来说都是有意义的。

    【讨论】:

      猜你喜欢
      • 2011-08-23
      • 1970-01-01
      • 2015-06-10
      • 2015-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多