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

suffering · 2012年02月22日 · 最后由 torvaldsdb 回复于 2018年07月23日 · 11971 次阅读

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

#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/ 这里面的探讨很深入,有助于大家进一步理解. # 在一个分享帖子里作为评论发出过去一次,不过没人理,只好开个新贴了:)

MetaProgramming is all about self, scope and definee.

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

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

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

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

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

草泥马 我都不想骂你了 发帖子 不能有点儿责任感 草泥马 误人子弟 错误百出 关键地方 出错 日尼玛

呃, 没起到 5 年前的贴子都会被人拉出来鞭尸。

这帖子还吵起来了

class A
  define_singleton_method :loudly do |message|
    puts message.upcase
  end
end

A.loudly "my message"
需要 登录 后方可回复, 如果你还没有账号请 注册新账号