翻译 Playing with Constants, Methods, and Superclasses

alex_marmot · 2015年04月24日 · 最后由 alex_marmot 回复于 2015年05月15日 · 1942 次阅读

玩转常量,方法和父类

本文译自 http://weblog.jamisbuck.org/2015/3/24/playing-with-constants-methods-and-superclasses.html?utm_source=rubyweekly&utm_medium=email 翻得不好,不要打我。。

虽在"实用性存疑的 ruby 技巧"下发布此文。但是哪怕是一些实用性可疑的技巧有时也能激发出一些意想不到的创造性解决方案。因此, 我发表了这些几年前在 Why the Lucky Stiff’s Camping web microframework 中看到的内容 (感谢 @vinbarnes 帮助我想起这事)

我们从观察下面的 Ruby 特性开始(可能是你没有预料的特性)。你知道你能够同时拥有同名的常量和方法吗?而且他们不会冲突哦。

示例:

Sum = 0

def Sum(*args)
  args.inject(Sum) { |s,i| s + i }
end

p Sum      #-> 0
p Sum(1)   #-> 1
p Sum(1,2) #-> 3

看见没?第一次我们调用 Sum,实际显示的是 Sum 这个常量的值。然而我们一开始在调用中添加参数就意味着方法调用。

没有错误,没有警告,没有符号遮盖其他符号,唯有疯狂的 Ruby 乐趣。

下一个小技巧(看似与前面的技巧无关):你是否知道当你定义一个类时,你在定义父类的位置所写的其实是一个表达式?我们通常在那儿放一个常量, 但是它可以是任何东西(只要最后能被作为类处理)。

class Shape
end

circle_is_shape = true
class Circle < (circle_is_shape ? Shape : Object)
end

p Circle.superclass #-> Shape

我知道你在想什么。”哇!这太酷了!但为什么会有人想这么干呢?”

我很高兴你问了,因为这会引入今天的第三个 Ruby 小技巧。

你大概已经知道定义一个类等同于创建一个新的 Class 对象并将之赋值给一个常量,对吗?

# This:
class Shape
end

# is the same as this:
Shape = Class.new

这意味着我们的类名仅仅是个常量...而且我们已经发现我们可以拥有同名的方法。更进一步的,我们也看到定义类时父类所用的表达式完全可以是任何表达式。 我们甚至能够在这儿进行方法调用。 看:

class Shape
end

def Shape(which)
  require "shapes/#{which}"
  Shape.const_get(which.to_s.capitalize)
end

class Square < Shape
end

class Circle < Shape(:ellipse)
end

于是乎,我们有了一个同时被声明为常量(我们的 Shape 基类)和 Shape 方法。Shape 方法可以通过传入参数来导入外部文件中假设的类从而实现了和 Shape 基类类似的作用。

然后,你看见像 Square 类所体现的那样,我们依然能够跟平常一样声明 Shape 类的子类。但是,我们现在可以耍花样了。看见没?Circle 是任何由我们的 Shape 方法返回的类的子类。

所以呢?很好,就某一点而言,这意味着你能够做诸如“数据驱动”(data-driven)的类结构(通过外部配置文件来让你指定(比如说)所需要的样条类型,或者星星的方位和顶点数)之类的疯狂事。

require 'yaml'
config = YAML.load_file('definitions.yml')

class Spline < Shape(config[:spline])
end

class Star < Shape(config[:star])
end

如我在开头所说,也许这没有任何的实际使用价值。一定有其他方式来达到同样的目的(也许黑魔法更少)。但你不得不要承认这样思考是件很有趣的事。

这个很牛逼阿。

#1 楼 @stardiviner 是啊,所以翻译出来 给大家看看。

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