【问题标题】:Difference between a class and a module类和模块的区别
【发布时间】:2010-09-14 04:04:43
【问题描述】:

我来自 Java,现在我更多地使用 Ruby。

我不熟悉的一个语言功能是module。我想知道module 到底是什么,你什么时候使用它,为什么使用module 而不是class

【问题讨论】:

标签: ruby class module


【解决方案1】:

首先,一些尚未提及的相似之处。 Ruby 支持开放类,但模块也是开放的。毕竟,Class 继承自 Class 继承链中的 Module,因此 Class 和 Module 确实有一些相似的行为。

但是您需要问自己在编程语言中同时拥有类和模块的目的是什么?一个类旨在成为创建实例的蓝图,每个实例都是蓝图的实现变体。实例只是蓝图(类)的已实现变体。很自然,类的作用就是创建对象。此外,由于我们有时希望一个蓝图派生自另一个蓝图,因此类旨在支持继承。

模块不能被实例化,不能创建对象,也不支持继承。所以请记住一个模块不会从另一个模块继承!

那么,在语言中使用模块有什么意义呢?模块的一个明显用途是创建命名空间,您也会注意到其他语言的这一点。同样,Ruby 的酷之处在于可以重新打开模块(就像类一样)。当你想在不同的 Ruby 文件中重用命名空间时,这是一个很大的用法:

module Apple
  def a
    puts 'a'
  end
end

module Apple 
  def b
    puts 'b'
  end
end
 
class Fruit
  include Apple
end
 
 > f = Fruit.new
 => #<Fruit:0x007fe90c527c98> 
 > f.a
 => a
 > f.b
 => b

但是模块之间没有继承关系:

module Apple
  module Green
    def green
      puts 'green'
    end
  end
end
 
class Fruit
  include Apple
end

> f = Fruit.new
 => #<Fruit:0x007fe90c462420> 
> f.green
NoMethodError: undefined method `green' for #<Fruit:0x007fe90c462420>

Apple 模块没有从 Green 模块继承任何方法,当我们在 Fruit 类中包含 Apple 时,Apple 模块的方法被添加到 Apple 实例的祖先链中,但不是 Green 模块的方法,甚至虽然 Green 模块是在 Apple 模块中定义的。

那么我们如何获得绿色方法呢?您必须将其明确包含在您的课程中:

class Fruit
  include Apple::Green
end
 => Fruit 
 > f.green
=> green

但是 Ruby 对模块还有另一个重要用途。这是 Mixin 工具,我在另一个关于 SO 的答案中对此进行了描述。但总而言之,mixin 允许您将方法定义到对象的继承链中。通过mixins,可以给对象实例的继承链(include)或者self的singleton_class(extend)添加方法。

【讨论】:

    【解决方案2】:

    命名空间:模块是命名空间...在 java 中不存在 ;)

    我也从 Java 和 python 切换到 Ruby,我记得有完全相同的问题...

    所以最简单的答案是模块是一个命名空间,它在 Java 中不存在。在 java 中,最接近命名空间的思维方式是 package

    所以 ruby​​ 中的模块就像 java 中的那样:
    类? 没有
    接口? 没有
    抽象类? 否
    包裹? 是(也许)

    java 中类中的静态方法:与 ruby​​ 中模块中的方法相同

    在java中最小的单位是一个类,你不能在一个类之外有一个函数。然而在 ruby​​ 中这是可能的(比如 python)。

    那么模块中包含什么?
    类、方法、常量。模块在该名称空间下保护它们。

    无实例:模块不能用于创建实例

    混合插件:有时继承模型对类不好,但在功能方面希望将一组类/方法/常量组合在一起

    关于 ruby​​ 中模块的规则:
    - 模块名称为 UpperCamelCase
    - 模块中的常量全部大写(此规则对所有 ruby​​ 常量都相同,并非特定于模块)
    - 访问方法:使用 .运营商
    - 访问常量:使用 :: 符号

    模块的简单示例:

    module MySampleModule
      CONST1 = "some constant"
    
      def self.method_one(arg1)
        arg1 + 2
      end
    end
    

    如何在模块中使用方法:

    puts MySampleModule.method_one(1) # prints: 3
    

    如何使用模块的常量:

    puts MySampleModule::CONST1 # prints: some constant
    

    关于模块的其他一些约定:
    在一个文件中使用一个模块(如 ruby​​ 类,每个 ruby​​ 文件一个类)

    【讨论】:

    • “- 访问方法:使用 .运算符 - 访问常量:使用 :: 符号”只有这个答案提到了这个!
    【解决方案3】:

    当你定义一个类时,你定义了一个数据类型的蓝图。 类保存数据,具有与该数据交互并用于实例化对象的方法。

    模块

    • 模块是将方法、类和常量组合在一起的一种方式。

    • 模块为您带来两大好处:

      => 模块提供命名空间并防止名称冲突。 命名空间有助于避免与其他人编写的同名函数和类发生冲突。

      => 模块实现了 mixin 工具。

    (包括 Klazz 中的 Module 使 Klazz 实例可以访问 Module 方法。 )

    (使用 Mod 扩展 Klazz,使 Klazz 类可以访问 Mods 方法。)

    【讨论】:

      【解决方案4】:

      底线:模块是静态/实用程序类和 mixin 之间的交叉。

      Mixins 是“部分”实现的可重用部分,可以以混合和匹配的方式组合(或组合),以帮助编写新类。当然,这些类还可以有自己的状态和/或代码。

      【讨论】:

        【解决方案5】:
        ╔═══════════════╦═══════════════════════════╦═════════════════════════════════╗
        ║               ║ class                     ║ module                          ║
        ╠═══════════════╬═══════════════════════════╬═════════════════════════════════╣
        ║ instantiation ║ can be instantiated       ║ can *not* be instantiated       ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ usage         ║ object creation           ║ mixin facility. provide         ║
        ║               ║                           ║   a namespace.                  ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ superclass    ║ module                    ║ object                          ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ methods       ║ class methods and         ║ module methods and              ║
        ║               ║   instance methods        ║   instance methods              ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ inheritance   ║ inherits behaviour and can║ No inheritance                  ║
        ║               ║   be base for inheritance ║                                 ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ inclusion     ║ cannot be included        ║ can be included in classes and  ║
        ║               ║                           ║   modules by using the include  ║
        ║               ║                           ║   command (includes all         ║
        ║               ║                           ║   instance methods as instance  ║
        ║               ║                           ║   methods in a class/module)    ║
        ╟───────────────╫───────────────────────────╫─────────────────────────────────╢
        ║ extension     ║ can not extend with       ║ module can extend instance by   ║
        ║               ║   extend command          ║   using extend command (extends ║
        ║               ║   (only with inheritance) ║   given instance with singleton ║
        ║               ║                           ║   methods from module)          ║
        ╚═══════════════╩═══════════════════════════╩═════════════════════════════════╝
        

        【讨论】:

        • 我得到了层次结构,类 -> 模块 -> 对象 -> BasicObject。酷!!
        • 当类和模块都支持类变量时,为什么“模块由”省略变量?请参阅stackoverflow.com/questions/5690458/… 的已接受答案
        • 所有这些答案中有很多图表。一个运行中的小例子:rubyfiddle.com/riddles/06081
        • 一个模块如何“没有被实例化”却有实例方法?
        • @devius as iamnotmaynard 已经评论了另一个答案:模块可以将实例方法添加到包含类。
        【解决方案6】:

        基本上,模块不能被实例化。当一个类包含一个模块时,会生成一个代理超类,它提供对所有模块方法以及类方法的访问。

        一个模块可以包含在多个类中。模块不能被继承,但这种“mixin”模型提供了一种有用的“多重继承”类型。 OO 纯粹主义者会不同意这种说法,但不要让纯粹性妨碍完成工作。


        (此答案最初链接到 http://www.rubycentral.com/pickaxe/classes.html,但该链接及其域不再有效。)

        【讨论】:

        • 是的,这就是它的工作原理。因此,模块无法与 Java 的“静态”类相比;代理超类(有人称其为“元类”)成为模块方法调度消息的接收者,这使得 更类似于 Java 中的静态类,并且它的方法像静态方法一样工作。然而,对于 Ruby 的类也是如此,它可以通过extending 类来采用类似“静态”的方法。 Ruby 实际上根本不区分“实例”和“类/静态”方法,只区分它们的接收者。
        【解决方案7】:

        我很惊讶还没有人这么说。

        由于提问者来自 Java 背景(我也是),所以这里有一个类比。

        类就像 Java 类。

        模块就像 Java 静态类。想想 Java 中的 Math 类。您不需要实例化它,而是重用静态类中的方法(例如Math.random())。

        【讨论】:

        • 但是模块也可以为包含类添加实例方法,而Java中的静态类不能。
        • 这句话来自于沉重的 C# 背景也是正确的。
        • 这并不完全正确;模块没有静态方法,它们只有方法。模块可以“扩展自己”(语法实际上是extend self),使其方法可用于self 的元类。这使得在Math 模块上调度类似random() 的方法成为可能。但就其本质而言,模块的方法不能在模块自己的self 上调用。这与 Ruby 的 self 概念、它的元类以及方法查找的工作原理有关。查看“元编程 Ruby”——Paolo Perlotta 了解详情。
        • 我会说模块更类似于带有方法的接口(Java 8 接口默认实现),但不能像 java 接口那样从另一个继承
        • 这个答案怎么有这么多票?顺便说一句,在 1 个月前用更好的话说:stackoverflow.com/a/17027346/986862
        【解决方案8】:

        Module 在 Ruby 中,在一定程度上对应于 Java 抽象类——具有实例方法,类可以从它继承(通过 include,Ruby 人称它为“mixin” ),但没有实例。还有其他一些细微的差别,但这些信息足以让您入门。

        【讨论】:

          【解决方案9】:

          第一个答案很好,给出了一些结构性的答案,但另一种方法是考虑你在做什么。模块是关于提供可以跨多个类使用的方法——将它们视为“库”(正如您在 Rails 应用程序中看到的那样)。类是关于对象的;模块是关于功能的。

          例如,身份验证和授权系统就是很好的模块示例。身份验证系统跨多个应用程序级类工作(用户经过身份验证,会话管理身份验证,许多其他类将根据身份验证状态采取不同的行为),因此身份验证系统充当共享 API。

          当您在多个应用程序之间共享方法时,您也可以使用模块(同样,库模型在这里很好)。

          【讨论】:

          • 模块和java中的Interfaces一样吗?
          • @Caffeine 并不是因为 Ruby 模块实际上包含实现,而 Java 中的接口是抽象的
          • 不,模块和 Java 包/JAR 是完全不同的野兽。
          • 我更像是带有方法实现的抽象类。
          • 实际上,@Chole 发现了模块的优点之一:命名空间。因此,虽然 Modules 不能直接等同于 Java 中的包,但它可以用于实现类似的功能:blog.rubybestpractices.com/posts/gregory/…
          猜你喜欢
          • 2017-08-28
          • 2013-01-11
          • 1970-01-01
          • 2017-05-10
          • 2015-01-11
          • 1970-01-01
          • 2015-09-29
          • 1970-01-01
          相关资源
          最近更新 更多