瞎扯淡 定义一个类方法而已,五种途径?

suffering · 发布于 2012年2月22日 · 最后由 u1442016572 回复于 2015年12月03日 · 6707 次阅读
709

这让人有点蛋疼,定义一个类方法,五种途径:

#1
class Person
  def self.species
    "Homo Sapien"
  end
end
#2
class Person
  def Person.species
    "Homo Sapien"
  end
end
#3
class Person
  class << self
    def species
      "Homo Sapien"
    end
  end
end
#4 
class << Person
  def species
    "Homo Sapien"
  end
end
#5
Person.instance_eval do
  def species
    "Homo Sapien"
  end
end

以上引自代码示例引自http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/,略有改动. 定义一个类方法而已,为什么要有这么多种方法存在? 如果再加上一种变态的class_eval的示例:

#6
class Foo
end
metaclass = (class << Foo; self; end)
metaclass.class_eval do
    def species
      "Homo Sapien"
    end
  end
end

.....那就是6种方法了. 而后,这数种方法,在定义方法的范围,self指代不同的对象,有的指向Foo类,有的则指向Foo类的metaclass.有的打开了一个新的scope(可以调用外面的变量),而有的则不可以. 用yehada katg的话来说:'At this point in other articles on this topic, you’re probably struggling to keep all of the details in your head; it seems as though there are so many rules.' 这么多规则让人怎么记它 !让人怎么不弄乱它! 一项操作,弄6种(可能更多)solution出来,有必要吗? 值得吗? 事实上,有必要的.因为这数种方法虽然在效果上看起来没有区别(不就是定义了个类方法吗!).但实际上这中间的细微差异(self的不同,scope的不同)是为不同的要求而特意设计的.比如说,元编程. 这里举个例子:

name = "foo"
var = "bar"
metaclass = (class << String; self; end)
metaclass.class_eval do
  define_method(name) do
    puts var
  end
end
String.foo # bar

这里,因为class_eval开了一个新的scope,所以,可以直接调用之前调用之外的变量name与var.而这样做却是不行的:

name = "foo"
var = "bar"
class Foo
  define_method(name) do
    puts var
  end
end
Foo.foo # bar

这样也不行:

name = "foo"
var = "bar"
class Foo
  class << self
    define_method(name) do
      puts var
    end
  end
end
Foo.foo # bar

因为class Foo, class<<Foo, class Foo;def self.bar;end;end, 以及class Foo;def Foo.bar;end;end这几种方式都打开了新的scope(作用域).直接的结果是,对外部环境的scope调用不着.

哪一天你需要这样用元编程,但是不知道如果控制作用域, what are you going to do?定义一个动态存取的实例变量?或者干脆用类变量?

事实上,对这些细节的使用,远远不只元编程这么个例子这么简单。在开发的过程中,你永远不知道下一刻你会遇到多诡异的要求。当然,大部分情况下你可以想出办法来解决这些问题。但是,很多时候,最直接有效的解决办法可能就直接隐藏在这些小细节里。引用乔帮主的一句话:you can't connect the dots looking forward;you can only connect them looking backwards

扯了这么多,只是要分享一点心得:多种途径实现一种方法,并不是给你的大脑加负的.而是为了实现强大的功能而特意设计的.多途径得出的结果,看似效果相同,实际上,却隐藏了许多细微的差别.而这种设计的目的,就是为了让我们能根据不同的需要,利用这些差异更便利地得到自己想要的.

最后,再次推荐一下:http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/ 这里面的探讨很深入,有助于大家进一步理解. #在一个分享帖子里作为评论发出过去一次,不过没人理,只好开个新贴了:)

共收到 7 条回复
8
hooopo · #1 · 2012年2月22日

MetaProgramming is all about self, scope and definee.

709
suffering · #2 · 2012年2月22日

泪流满面啊,发了多少个扑街帖啊`~~

270
hysios · #3 · 2012年2月22日

ruby 的设计原则是灵活, 你在设计与实现某种功能的时候,应该考虑的是受限语言上的限制,而ruby设计目标之一就是最大化的灵活,

146
skandhas · #4 · 2012年2月22日

记得大胡子的这篇文章有一处错误被 玉桂姐 给指了出来。呵呵

8
hooopo · #5 · 2012年2月22日

#4楼 @skandhas 我是从玉桂姐的博客发现大胡子这篇的...

146
skandhas · #6 · 2012年2月22日

#5楼 @hooopo 偶也是 :>

21049
u1442016572 · #7 · 2015年12月03日

这里,因为class_eval开了一个新的scope, 这里应该没有开新的scope吧。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册