【问题标题】:type of object references in rubyruby 中的对象引用类型
【发布时间】:2009-09-25 01:48:35
【问题描述】:

我是 Ruby 新手,目前正在尝试我用作指南的 Ruby 书中的一些示例:

    class Account
attr_accessor :balance
def initialize(balance)
  @balance = balance
end  
end

class Transaction
def initialize(account_a, account_b)
@account_a = account_a
@account_b = account_b  
end  

def debit(account,amount)
  account.balance -= amount
end
    def credit(account,amount)
        account.balance += amount
    end

  def transfer(amount)
      debit(@account_a, amount)
      credit(@account_b, amount)    
  end

end

savings = Account.new(100)
checking = Account.new(200)
trans = Transaction.new(checking, savings)
trans.transfer(60)

puts savings.balance
puts checking.balance

这是一个非常简单的示例,在同一个脚本文件中包含两个类。我对传递给 credit 和 debit 方法的参数类型感到困惑。来自 Java,我仍然在考虑类型,所以很明显,我传递给的 account 变量的类型,比如 debit 方法,必须是 Account 类型。

由于 ruby​​ 是动态类型的并且不检查类型,我如何安全地对我传递的参数进行操作,并通过以下方式定义方法的其余部分: account.balance -+ amount ?

我想了解,如果我将对 Account 以外的对象的引用传递给 debit 方法,会有什么样的安全性?

当定义以下方法的主体时,它使用给定的参数帐户。现在,我想我在重复自己,因为我仍然无法理解这个想法......我该如何接受参数 account (可以是任何类型,因为没有人在检查)和通过使用点运算符构建一些逻辑,询问其实例变量,或调用其其他方法,并对可能或可能不是正确种类(或类型)的对象执行计算?当然,隐含地,我希望它是 Account 类型。

def credit(account,amount)
        account.balance += amount
    end

另外,如果我在不同的文件中声明这两个类,同一个示例将如何工作?

对于新手问题,我提前致以诚挚的歉意,我只是发现我很难将我的思想包裹在动态类型上——或者更好的是,没有类型检查。这本书要么在这方面有点模糊,要么我无法通过仅用 Java 思考来动摇我的视野。

任何实用的解释将不胜感激。

【问题讨论】:

  • 不需要为这个问题道歉。这是一个很好的问题,也是一个有效的担忧。 Ruby 消除了您在其他语言中获得的大量编译时保证,以换取运行时更大的灵活性。

标签: java ruby variables dynamic-typing


【解决方案1】:

Ruby 中没有任何类型安全性。对 Ruby 而言,重要的是对象是否可以响应它接收到的消息。你可以传入一些完全无意义的东西,此时 Ruby 不会做任何事情来阻止你。但是,如果您传入一个不响应您发送的消息的对象(在这种情况下是 + 和 -),当您的代码尝试发送无效消息时,您将收到 NoMethodError。

一般来说,解决这个问题的方法是:不要传入错误的类型。它知道这听起来很弱,但这几乎是你需要做的——确保你传递的是正确的东西。为你的程序编写测试以确保你正在做你想做的事情。 Ruby 在单元测试方面非常重要。如果你真的担心一个参数是正确的,你可以明确地检查它的类(raise 'WTF?' unless object.class == String)或者你可以尝试将它转换为正确的类(通过定义一个to_foo-type 方法) .

作为回报,您基本上可以从关心类型中解放出来。只要对象响应您发送的消息,您传递的内容就无关紧要。这使得模拟和代理之类的事情变得非常容易。

【讨论】:

  • 所以,如果我这样想可能会对我有所帮助:在方法定义期间 - 要求参数在方法主体中执行各种操作,就好像它可以正常工作一样。当客户端调用该方法时 - 它可以传递任何参数(看起来像鸭子),只要它响应上面定义的方法主体中的任何内容(可能像鸭子一样嘎嘎)
  • Chuck:我确定这就是您的意思,但以防万一:当然,您在 Ruby 中没有任何 编译时间 类型安全性。但就像所有现代语言一样,它最终是非常强类型的。它并不挑剔,唯一的检查是“可以调用方法吗”,但是没有办法绕过该检查,除了捕获任何产生的异常并快速定义方法。 :-)
  • “你在 Ruby 中没有任何类型安全”的说法在技术上是不正确的。实际上,所有现代语言都是强类型和类型安全的。很容易忘记实际上不是类型安全的语言是什么样的:C、Fortran 和汇编就是三个例子。
  • @DigitalRoss:甚至没有基于类型的检查来查看是否可以调用该方法。如果一个对象没有处理特定消息的方法,它只会使用默认方法method_missing。您可以在运行时响应完全任意的方法而不会捕获任何异常。至于类型安全:该术语(我认识的大多数人都使用它)意味着无法在预期另一种类型的地方找到一种类型。 Ruby中是这种情况吗?绝对不。除了显式测试之外,甚至没有办法说您期望一个特定的类。
【解决方案2】:

它叫做"duck typing" - 我建议先阅读链接的维基百科文章,然后看看你之后是否还有任何问题。

【讨论】:

  • 所以基本上,只要该方法的调用者传递对适当对象的引用,我就可以在传递给该方法的参数上按我想要的方式操作,假设它可以正常工作。 . 或者一个可以处理方法要求它做什么的对象?
【解决方案3】:

传递的对象,它的引用,乍一看可以认为是未知的,但是Ruby是强类型的;此外,它还具有使用“鸭子打字”的功能。我会举几个例子。

让我们以财务账户为例。

您可以使用 case 语句来确定对象的类型:

class Account
  attr :balance # add ", true" to make it writeable
  def initialize(balance)
    case balance
      when String: @balance = balance.to_f
      when Fixnum, Float: @balance = balance
    else
      raise TypeError, "Can't initialize an account with a balance of type #{obj.class}."
    end
  end
end

另一种选择,当您要进行一个测试时,对对象进行is_a? 测试,例如obj.is_a?(Fixnum) 将返回真/假,判断它是否为整数。

您可以使用断言来强制执行类型:

class Account
  attr :balance # add ", true" to make it writeable
  def initialize(balance)
    assert_eql balance.class, Float
    @balance = balance
  end
end

我建议您对 Transaction 类使用 if 语句并结合引发异常:

class Transaction
  def initialize(account_a, account_b)
    raise ParameterError, "Both parameters must be accounts!" unless account_a.is_a?(Account) && account_b.is_a?(Account)
    @account_a = account_a
    @account_b = account_b  
  end

  def transfer(amount)
    debit(@account_a, amount)
    credit(@account_b, amount)    
  end

  private

  def debit(account,amount)
    account.balance -= amount
  end

  def credit(account,amount)
    account.balance += amount
  end
end

【讨论】:

    【解决方案4】:

    实际上,所有现代语言都是type-safememory-safe,只是并不总是静态类型


    这些都是很好的问题,而且它确实是动态与静态类型范式的核心。

    事实上,你的大部分担忧都不是太严重:

    Ruby 是类型安全的,至少根据它自己的定义,它绝对是内存安全的。它确实检查每个操作的类型。 (有点像参数传递这样的操作没有类型限制要检查。)

    它确实有很多自动转换,但它仍然是类型安全的。

    它确实有动态类型,但它仍然是类型安全的。

    确实,方法参数没有“错误”类型。使用该参数时将进行检查。也无需检查您收到的类型......当您使用它时,Ruby 会为您检查。你只是在运行时而不是编译时得到错误,但无论如何你都要进行单元测试,对吧? :-) 查找“Duck typing”很有用,因为兼容类型的定义比 Ruby 中的通常范围更广,但仍有定义并已检查。

    对于一个非常大的程序,传统观点认为像 Java 和 Scala 这样的静态类型语言是更安全的选择,因为它们可以放心地重构,并且您可以获得额外的编译时检查。然而,传统智慧受到许多人的质疑,因为它涉及将一组复杂的权衡减少到一个 1 位的结论。

    【讨论】:

    • 什么?是什么让您认为 Ruby 会检查类型?只要传递的对象响应您发送的消息,Ruby 根本不关心它是什么类型。
    • 我想最让我困惑的是,现在我正在尝试过渡到 ruby​​,当我无法保证该方法的客户端确实会通过时,通过点符号调用参数上的方法一个“正确”的参数..
    • Chuck:我想知道提问者在想什么类型的错误。 C 风格的分段错误 错误?我的意思是,如果一个对象响应发送它已经检查了它的类型并且它通过了,否则,类型检查会导致异常。我认为我们都已经习惯了现代语言中的强类型,以至于我们认为这是理所当然的。有一天……
    • 这是一个非常广泛的回复声明。 Tcl呢? Perl?
    • 我对自己的措辞表示怀疑,尽管上周我使用了“类型安全”并且有人抱怨这一点,尽管“类型安全”与真实语言一起使用并没有甚至没有一个精确的定义。看起来我真正想要的术语是“内存安全”,这意味着永远不能使用任意位模式。但 Ruby 似乎很容易归类为“类型安全”。 (只有正式的定义是精确的,但即使是 Java 也不符合那个无用的实践标准。)
    猜你喜欢
    • 2013-03-24
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 2013-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多