免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 17322 | 回复: 2

嘎嘎叫的小狗——快乐的鸭子类型 [复制链接]

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:55:28
发表于 2013-08-23 09:32 |显示全部楼层
  

  
鸭子类型是我目前在Ruby语言里最喜欢的一个“功能特征”,主要是因为它能让我们轻松的写出漂亮的代码——毕竟,你无需再担心类型:你可以把精力全部集中到你想发送的消息上,以及你需要打交道的对象能发挥的功能上。

我第一次接触Ruby时就知道它是一种“鸭子类型语言”,但我的静态编译型语言的背景知识妨碍了我真正理解鸭子类型的真正含义。理论很简单:如果你设计一个方法,它需要一个‘鸭子’参数,那么你呼叫一声“嘎嘎”,任何以“嘎嘎”回应你的对象都可以传入这个方法——这个对象究竟是什么类型并不重要。很显然,你可以得出这样的结论,如果你写出一个Dog类,它实现了一个叫“嘎嘎”的方法(很奇怪的狗),那么,你可以把这个狗传入上面的那个方法,一点问题都没有。非常酷吧,鸭子类型的强大功能震撼了我,我认识到,它在各种对象间打通了一条重要的沟通途径,强化了API的能力,减少了代码中的干扰。为了说明这些,让我来展示一些Ruby标准库中的几个例子。
File.open
File.open(“path/to/file&rdquo最常见的读取文件的方法:你传入path,这个方法会返回一个能读取文件的对象。你是否注意到,我加粗强调了“path”这个词。这是特意的——这个‘open’函数实际可以接受任何可以扮演路径角色的东西,并不仅仅指路径字符串。这区别有些微妙,但你会发现我们可以把代码这样写:

class VimConfig
  # ... behavior ... #        
  def to_path
    "~/.vimrc"
  end
end
config = VimConfig.new
config_file = File.open config
   很帅,不是吗?Ruby的File API在使用它的参数前会进行转化,转化的一种途径是通过‘to_path’方法。如果你感到奇怪,下面是实现它的C语言代码(‘rb_f_open’ 调用 ‘FilePathValue’,后者最终调用 ‘rb_get_path_check_to_string&rsquo):

static VALUE
rb_f_open(int argc, VALUE *argv)
{
    ID to_open = 0;
    int redirect = FALSE;
    if (argc >= 1) {
  CONST_ID(to_open, "to_open";
        if (rb_respond_to(argv[0], to_open)) {
            redirect = TRUE;
        }
        else {
            VALUE tmp = argv[0];
            FilePathValue(tmp);
            if (NIL_P(tmp)) {
                redirect = TRUE;
            }
            else {
                VALUE cmd = check_pipe_command(tmp);
                if (!NIL_P(cmd)) {
                    argv[0] = cmd;
                    return rb_io_s_popen(argc, argv, rb_cIO);
                }
            }
        }
    }
    if (redirect) {
        VALUE io = rb_funcall2(argv[0], to_open, argc-1, argv+1);
        if (rb_block_given_p()) {
            return rb_ensure(rb_yield, io, io_close, io);
        }
        return io;
    }
    return rb_io_s_open(argc, argv, rb_cFile);
}
VALUE
rb_get_path_check_to_string(VALUE obj, int level)
{
    VALUE tmp;
    ID to_path;
    if (insecure_obj_p(obj, level)) {
        rb_insecure_operation();
    }
    if (RB_TYPE_P(obj, T_STRING)) {
        return obj;
    }
    CONST_ID(to_path, "to_path";
    //to_path call!
    tmp = rb_check_funcall(obj, to_path, 0, 0);
    if (tmp == Qundef) {
        tmp = obj;
    }
    StringValue(tmp);
    return tmp;
}
数组索引
数组索引(a_array[index])是另外一个很好的例子:它会向索引调用‘to_int’方法,所以,任何能响应to_int方法的对象都可以当作索引。这让我们可以这样写:
class PodiumPosition
  # .. behavior .. #
  def to_int
    @race_position
  end
end
position = PodiumPosition.new(1)
prizes = [ "orange", "apple", "corn" ]
puts "Congrats, you won #{prizes[position]}"
IO.select
我是通过IO.select API才第一次发现了Ruby的强大。这个API会调用系统select(2)函数,接收文件描述符参数,并挂起当前的线程,直到有文件可以进行读写操作。这个Ruby函数定义如下:
select(read_array
        [, write_array
        [, error_array
        [, timeout]]]) → array or nil
因此,你可以传入一个数据流数组,而“select”函数会一直等到流文件准备好可读或可写。问题是,很多数据流是存储在具有各种行为特征的特定对象里的(例如一个执行网络操作的Connection类),这些对象里的IO接口通常经过了二次封装,外界无法直接访问。根本不可能通过重构内核代码来适应‘select’ API。打破它的封装吗?很显然不行!这时‘to_io’方法就成了救星!
class Connection
  # .. rest of the class .. #
  def accept_connection(io)
    @io = io
    # new connection code
  end
  def to_io
    @io
  end
end
class Reactor
  # array_of_connections_to_read is an array of instances of the above Connection class
  # array_of_connections_to_write is an array of instances of the above Connection class
  def tick
    to_read, to_write = IO.select(array_of_connections_to_read, array_of_connections_to_write)
  end
end
你可以看到,Ruby的标准库里到处都是鸭子类型

.
重构
最明显,也是最值得一提的鸭子类型的好处是,它让重构变得更容易:“用多型替换条件判断”和 “Replace Type Code with Strategy/State”的重构原则,当你不需要考虑类型、只关心行为时,这些都变得极其简单和容易实现。

鸭子类型的黑暗面
没有编译器为你探路是很危险的。专业的Ruby程序员(1)永远不会忘记有责任测试它们的代码的各种行为,并且(2)一定写出整洁的代码,并及时重构。Ruby代码必须要认真写,否者调试起来就会是一场噩梦。

同时,动态语言一般最合适的是开发小型或中型软件。我的经验告诉我,当系统变得复杂时,最好把它拆分成小的应用,如果是用动态语言开发的,那这种做法更加重要——一个reddit的网友说需要在一个10万行的程序了修改一个函数的名称,我只能说,这很难实现。修改公开的接口,这很难很难。有时最好把它标注为‘废弃’就行了。

结论
动态语言能漂亮的解决你的问题,但需要有很好的设计,Ruby的标准库里鸭子类型为我们提供了方便的途径。它是一个很好的例子,向我们展示了一个Ruby程序员该如何的编程:按对象的行为——而不是按对象的类型——来接收参数。

我希望这篇文章给那些仍然不明白像Ruby这样的语言的强大之处的人带来新的认识。我推荐阅读下面几本书来进一步的学习:

[英文原文:Quacking The Dog - Duck typing for happiness]

转自:http://www.aqee.net/quacking-the-dog-duck-typing-for-happiness/


本文来自ChinaUnix新闻频道,如果查看原文请点:http://news.chinaunix.net/opensource/2013/0823/2907892.shtml

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
发表于 2013-08-29 04:23 |显示全部楼层
这就非常酷了。。

论坛徽章:
0
发表于 2013-10-05 13:51 |显示全部楼层
这鸭子类型
有些微妙
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP