3

我是 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 类型。

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

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

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

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

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

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

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

4

4 回答 4

3

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

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

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

于 2009-09-25T02:10:50.880 回答
2

它被称为“鸭子打字” - 我建议先阅读链接的维基百科文章,然后看看你是否还有任何问题。

于 2009-09-25T01:52:59.593 回答
1

实际上,所有现代语言都是类型安全内存安全的,只是并不总是静态类型的


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

碰巧的是,您的大多数担忧都不是太严重:

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

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

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

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

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

于 2009-09-25T01:53:29.267 回答
1

传递的对象,它的引用,乍一看可以认为是未知的,但 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
于 2009-09-25T02:12:48.380 回答