<li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>
    首页»RubyOnRails»嘎嘎叫的小狗——快乐的鸭子类型

    嘎嘎叫的小狗——快乐的鸭子类型

    来源:aqee 发布时间:2013-08-23 阅读次数:

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

      ?#19994;?#19968;次接触Ruby时就知道它是一种“鸭子类型语言”,但我的静态编译型语言的背景知识妨碍了我真正理解鸭子类型的真正含义。理论很简单:如果你设计一个方法,它需要一个‘鸭子’参数,那么你呼叫一声“嘎嘎”,任何以“嘎嘎”回应你的对象都可以传入这个方法——这个对象究竟是什么类型并不重要。很显然,你可以得出这样的结论,如果你写出一个Dog类,它实现了一个叫“嘎嘎”的方法(很奇怪的狗),那么,你可以把这个狗传入上面的那个方法,一点问题都?#25381;小?#38750;常酷吧

    duck_typing

      鸭子类型的强大功能震撼了我,我认识到,它在各种对象间打通了一条重要的沟通途径,强化了API的能力,减少了代码中的干扰。为了?#24471;?#36825;些,让我来展示一些Ruby标准库中的几个例子。

      File.open

      File.open(“path/to/file”)最常见的读取文件的方法:你传入path,这个方法会返回一个能读取文件的对象。你是否注意到,我加粗强调了“path”这个词。这是特意的——这个‘open’函数实际可以接受任何可以扮演路径角色的东西,并不仅仅?#22030;?#24452;字符串。这区别有些微妙,但你会发现我们可以把代码这样写:

    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’)):

    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])是另外一个很好的例子?#26680;?#20250;向索引调用‘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)函数,接收文件描述符参数,并?#31227;?#24403;前的线程,直到有文件可以进行读写操作。这个Ruby函数定义如下:

    select(read_array
    	[, write_array
    	[, error_array
    	[, timeout]]]) → array or nil

      因此,你可以传入一个数据流数组,而“select”函数会一直等到流文件准备好可读或可写。问题是,很多数据流是存储在具有各?#20013;?#20026;特征的特定对象里的(例如一个执?#22411;?#32476;操作的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的标准库里到处都是鸭子类型.

      重构

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

      鸭子类型的黑?#24471;?/h3>

      ?#25381;?#32534;译器为你探路是很危险的。专业的Ruby程序员(1)永远不会忘记有责任测?#36816;?#20204;的代码的各?#20013;?#20026;,并且(2)一定写出整洁的代码,并及时重构。Ruby代码必须要认真写,否者调?#20113;?#26469;就会是一场噩梦。

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

      结论

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

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

      英文原?#27169;?a href="http://victorarias.com.br/2013/08/10/quacking-the-duck.html">Quacking The Dog - Duck typing for happiness

    QQ群:WEB开发者官方群(515171538),验证消息:10000
    微信群:加小编微信 849023636 邀请您加入,验证消息:10000
    提示:更多精彩内容关注微信公众号:全栈开发者中?#27169;╢sder-com)
    网友评论(共1条评论) 正在载入评论......
    理智评论文明上网,拒绝恶意谩骂 发表评论 / 共1条评论
    登录会员中心
    大乐透彩票预测
    <li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>
    <li id="2aw4k"></li>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
  • <div id="2aw4k"><tr id="2aw4k"></tr></div>
    <center id="2aw4k"><small id="2aw4k"></small></center><center id="2aw4k"><small id="2aw4k"></small></center>
    3d试机号700历史统计乐彩网 福利彩票双色球最近100期走势图 黑龙江快乐十分 台湾五分彩怎么玩 体彩31选7开奖结果果 辽宁35选7玩法 幸运飞艇彩票数据接口 体彩江苏7位数18127 七星彩票走势图大全 海南飞鱼游戏彩票控 六十期六肖中特 26选5走势图 老板发2元彩票当年终奖 神气牛牛 15选5走势图