2008-10-16 55 views
5

希望我没有误解“duck typing”的含义,但是从我读过的内容来看,这意味着我应该根据对象如何响应方法而不是它是什么类型/类来编写代码。我可以用鸭子打字改进这种方法吗?

下面的代码:

def convert_hash(hash) 
    if hash.keys.all? { |k| k.is_a?(Integer) } 
    return hash 
    elsif hash.keys.all? { |k| k.is_a?(Property) } 
    new_hash = {} 
    hash.each_pair {|k,v| new_hash[k.id] = v} 
    return new_hash 
    else 
    raise "Custom attribute keys should be ID's or Property objects" 
    end 
end 

我要的是确保我最终有一个哈希密钥是代表一个ActiveRecord对象的ID的整数。我并不特别喜欢用all?两次遍历散列键来确定是否需要获取ID。

当然,我会接受任何其他建议,以改善这个代码,以及:)

+0

甚至从来没有听说过“鸭打字”的。你在哪里遇到过这个问题? – 2008-10-16 17:27:00

+0

@布莱恩,http://en.wikipedia.org/wiki/Duck_typing – 2008-10-16 17:29:41

回答

11

你怎么写这种方法应该取决于您是否希望正常程序执行过程中被抛出异常。如果你想要一个可读的异常消息,因为最终用户可能会看到它,那么手动抛出一个是有道理的。否则,我只是做这样的事情:

def convert(hash) 
    new_hash = {} 
    hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v } 
    return new_hash 
end 

这将完成同样的事情,你仍然会得到一个异常,如果数组键没有一个ID字段。更好的是,这会使用更多的鸭子打字,因为现在任何具有id字段的东西都是可以接受的,这比明确地检查某个属性是好的。这使得你的代码更加灵活,特别是在单元测试时。

我们仍然对整数对象有明确的检查,但这种偶然的特殊情况通常是可以接受的,尤其是在检查内置数据类型时。

3

鸭子打字实际上只是多态的细微版本。在Java这样的静态类型语言中,你必须创建一个明确的接口,告诉编译器一个特定变量可以接受的所有方法。使用像Ruby这样的动态语言,接口仍然存在抽象意义,它们只是隐含的。

问题是,您将两种不同的数据结构接受为一种方法。使鸭子打字工作的方法是要求传递给你的方法的所有对象服从同一个契约(即它总是一个整数到[Foo]对象的散列)。将一个带有属性键的散列转换为正确的结构应该是客户代码的工作。这可以通过一个简单的包装类或由您的elseif子句构成的转换函数轻松完成。

底线是由调用该方法的人来确定他的参数都会让你的方法期望他们嘎嘎的方式嘎嘎。如果他们不这样做,他就是那个需要弄清楚如何像鸭子一样制造火鸡嘎嘎声的人,而不是你。

0

我想要的是确保我最终得到一个散列,其中的键是一个表示ActiveRecord对象ID的整数。

您应该在创建/插入散列时检查。你可以尝试这样的事:

 
h = {} 
def h.put obj 
    self[obj.id]=obj 
end 

也许

 
h = {} 
def h.[]= key, value 
    raise "hell" unless key == value.id 
    super 
end 
相关问题