Chinaunix首页 | 论坛 | 博客
  • 博客访问: 398969
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: Python/Ruby

2012-08-02 22:52:32

C.1 法术集

数组参数

把一组参数压入到一个数组中,perl 语言中也有类似用法。


  1. def my_method(*args)
  2.     args.map {|arg| arg.reverse }
  3. end
  4. my_method('abc' , 'xyz' , '123' ) # => ["cba", "zyx", "321"]


环绕别名

从一个重新定义的方法中调用原始的、被重命名的版本。

三个基本步骤:


  1. 通过 alias 对原有方法定义一个别名
  2. 覆写原有方法
  3. 在该方法中调用别名方法
通过此方式可以改写原来方法,又不破坏原有功能。



  1. class String
  2.     alias :old_reverse :reverse
  3.     def reverse
  4.         "x#{old_reverse}x"
  5.     end
  6. end

  7. "abc".reverse # => "xcbax"


白板

移除一个对象中的所有方法,以便通过 method_missing 添加幽灵方法。主要目的避免原有类中的方法同新增方法产生冲突。注意以 __ 开头的方法不能移除,比如 __send__ 等。


  1. class C
  2.     def method_missing(name, *args)
  3.         "a Ghost Method"
  4.     end
  5. end

  6. obj = C.new
  7. obj.to_s # => "#"
  8. class C
  9.     instance_methods.each do |m|
  10.         undef_method m unless m.to_s =~ /method_missing|respond_to?|^__/
  11.     end
  12. end
  13. obj.to_s # => "a Ghost Method"


类扩展

通过向 eigenclass 中混入模块来定义类方法(是对象扩展)。

扩展的方法存在于 eigenclass 类中,对类来说就是类方法,对对象实例来说就是单件方法。

提示:一个类,如 class C 具有双重身份。本身是个类,同时又是 Class 类的一个实例。类混入实际上是针对他作为 Class 类的一个实例对象的身份来进行的。

因此类扩展的方式一样适用于对象实例的扩展,那就是对象扩展了。


  1. class C; end
  2. module M
  3.     def my_method
  4.         'a class method'
  5.     end
  6. end

  7. class << C
  8.     include M
  9. end
  10. C.my_method # => "a class method"


类扩展混入

使一个模块可以通过钩子方法扩展它的包含者。

同上面基本类似,差别主要有:


  1. 通过 extend 方法,避免手工打开 eigenclass (即class << C; end)操作。
  2. 通过 included 钩子方法触发。
  3. 可以同时添加实例方法跟类方法(这个例子没有演示)
基本编写方式:
  1. 定义一个模块,如 MyMixin
  2. 在 MyMixin 中定义一个内部模块,通常叫 ClassMethods ,并定义一些方法,这些方法会成为包含者的类方法。
  3. 覆写 MyMixin#included() 方法,extend ClassMethods。



  1. module M
  2.     def self.included(base)
  3.         base.extend(ClassMethods)
  4.     end
  5.     module ClassMethods
  6.         def my_method
  7.             'a class method'
  8.         end
  9.     end
  10. end
  11. class C
  12.     include M
  13. end
  14. C.my_method # => "a class method"


类实例变量

在一个 Class 对象的实例变量中存储类级别的状态

核心提示:


  1. 这里的 class C 要当做Class 类的 一个实例对象看待。普通实例对象如何创建实例变量,类实例对象就如何创建实例变量。
  2. class ... end 实际上是在运行一段代码,不要用常规的关键字理解。
  3. 访问类实例变量,只能通过类方法(因为其 self 就是类名),或者加上类名前缀。
想一想如果我们运行时类名动态变化,如何处理,显然我们还有 eval 工具组(使用 instance_eval ,class_eval,eval 均可)



  1. class C
  2.     @my_class_instance_variable = "some value"
  3.     def self.class_attribute
  4.         @my_class_instance_variable
  5.     end
  6. end
  7. C.class_attribute # => "some value"


类宏

在类定义中使用一个类方法。

就是一个伪装成关键字的类方法。如 attr_accessor :a , :b ;类宏一般结合类扩展混入技术进行。

  1. class C; end
  2. class << C
  3.     def my_macro(arg)
  4.         "my_macro(#{arg}) called"
  5.     end
  6. end
  7. class C
  8.     my_macro :x # => "my_macro(x) called"
  9. end


洁净室

使用对象作为执行块的上下文环境

实际上就是通过 instance_eval 限定执行块的作用域。


  1. class CleanRoom
  2.     def a_useful_method(x); x * 2; end
  3. end
  4. CleanRoom.new.instance_eval { a_useful_method(3) } # => 6


代码处理器

处理从外部获得的字符串代码


  1. File.readlines("a_file_containing_lines_of_ruby.txt" ).each do |line|
  2.     puts "#{line.chomp} ==> #{eval(line)}"
  3. end
  4. # >> 1 + 1 ==> 2
  5. # >> 3 * 2 ==> 6
  6. # >> Math.log10(100) ==> 2.0


上下文探针

执行块来获取对象上下文中的信息。

其实就是通过 instance_eval 将对象内部的作用域暴露出来。


  1. class C
  2.     def initialize
  3.         @x = "a private instance variable"
  4.     end
  5. end
  6. obj = C.new
  7. obj.instance_eval { @x } # => "a private instance variable"


延迟执行

在 proc 或  lambda中存储一段代码及其上下文,用于以后执行。


  1. class C
  2.     def store(&block)
  3.         @my_code_capsule = block
  4.     end
  5.     def execute
  6.         @my_code_capsule.call
  7.     end
  8. end

  9. obj = C.new
  10. obj.store { $X = 1 }
  11. $X = 0
  12. obj.execute
  13. $X # => 1


动态派发

在运行时决定调用哪个方法

通过 send 发送消息,等价于方法调用。但通过 send 可以发送符号或字符串,灵活性大为增强。

  1. method_to_call = :reverse
  2. obj = "abc"
  3. obj.send(method_to_call) # => "cba"


动态方法

在运行时才决定如何定义一个方法

动态方法还有一个特性:不会开启一个新的作用域。

我们知道 def 、module、class 会开启新的作用域,扁平化作用域的办法就是用 define_method 、Module.new 、Class.new 等方法调用取代关键字。

  1. class C
  2. end
  3. C.class_eval do
  4.     define_method :my_method do
  5.         "a dynamic method"
  6.     end
  7. end
  8. obj = C.new
  9. obj.my_method # => "a dynamic method"


动态代理

把不能对应某个方法名的消息转发给另外一个对象。

method_missing 结合 send 技术。以下脚本要完善的话,还需要添加 respond_to? 的判断。


  1. class MyDynamicProxy
  2.     def initialize(target)
  3.         @target = target
  4.     end
  5.     def method_missing(name, *args, &block)
  6.         "result: #{@target.send(name, *args, &block)}"
  7.     end
  8. end
  9. obj = MyDynamicProxy.new("a string" )
  10. obj.reverse # => "result: gnirts a"


扁平作用域

使用闭包在两个作用域之间共享变量

注:其实这还算不得扁平化作用域,顶多算对象打开,或者作用域切换。或许算半个吧,毕竟 obj 的作用域都被拉到了当前作用域,可以看到 a_variable 变量了。


  1. class C
  2.     def an_attribute
  3.         @attr
  4.     end
  5. end
  6. obj = C.new
  7. a_variable = 100
  8. # flat scope:
  9. obj.instance_eval do
  10.     @attr = a_variable
  11. end
  12. obj.an_attribute # => 100


幽灵方法

响应一个没有关联方法的消息

比较简单,直接看代码了

  1. class C
  2.     def method_missing(name, *args)
  3.         name.to_s.reverse
  4.     end
  5. end
  6. obj = C.new
  7. obj.my_ghost_method # => "dohtem_tsohg_ym"


钩子方法

通过覆写某个特殊方法来截获对象模型事件。

有点事件驱动的感觉,呵呵。我们能拦截的事件,还是要依赖于 Ruby 提供。希望以后的 Ruby 版本能够想出一套任意添加事件的架构来。

其实现在我们也可以通过环绕别名来拦截一些自己的事件,但总觉得欠那么一点意思。


  1. $INHERITORS = []
  2. class C
  3.     def self.inherited(subclass)
  4.         $INHERITORS << subclass
  5.     end
  6. end
  7. class D < C
  8. end

  9. class E < C
  10. end

  11. class F < E
  12. end

  13. $INHERITORS # => [D, E, F]


内核方法

在 Kernel 模块中定义一个方法,使之对所有对象都可用。

puts 估计也是类似的方法,也可能存放在 class Object  中。

  1. module Kernel
  2.     def a_method
  3.         "a kernel method"
  4.     end
  5. end
  6. a_method # => "a kernel method"


惰性实例变量

当第一次访问一个实例变量时才对之进行初始化。

这个称为法术,有点勉为其难。

  1. class C
  2.     def attribute
  3.         @attribute = @attribute || "some value"
  4.     end
  5. end
  6. obj = C.new
  7. obj.attribute # => "some value"


拟态方法

把一个方法伪装成另外一种语言构件。

用来编写 DSL 比较方便。


  1. def BaseClass(name)
  2.     name == "string" ? String : Object
  3. end
  4. class C < BaseClass "string" # a method that looks like a class
  5.     attr_accessor :an_attribute # 伪装成关键字的方法
  6. end
  7. obj = C.new
  8. obj.an_attribute = 1 # 伪装成属性的方法


猴子打补丁

修改已有类的特性

一般还是要尽量避免,安全第一。

  1. "abc".reverse # => "cba"
  2. class String
  3.     def reverse
  4.         "override"
  5.     end
  6. end
  7. "abc".reverse # => "override"


有名参数

把方法参数收集到一个哈希表中,以便通过名字访问。

咦,跟第一条法术差不多。

  1. def my_method(args)
  2.     args[:arg2]
  3. end
  4. my_method(:arg1 => "A" , :arg2 => "B" , :arg3 => "C" ) # => "B"



命名空间

在一个模块中定义常量,以防止命名冲突。

其实就是作用域门的灵活运用。


  1. module MyNamespace
  2.     class Array
  3.         def to_s
  4.             "my class"
  5.         end
  6.     end
  7. end
  8. Array.new # => []
  9. MyNamespace::Array.new # => my class



空指针保护

用“或”操作符覆写一个空应用。就是 perl 的短路操作符


  1. x = nil
  2. y = x || "a value" # => "a value"


对象扩展

通过给一个对象 eigenclass 混入模块来定义单件方法。

咦,前面提到过啊。那是类扩展,对象扩展其实是一回事。你把一个类当成 Class 的实例对象就全明白了。

  1. obj = Object.new
  2. module M
  3.     def my_method
  4.         'a singleton method'
  5.     end
  6. end
  7. class << obj
  8.     include M
  9. end
  10. obj.my_method # => "a singleton method"


打开类

修改已有的类

这不就是猴子补丁吗?

  1. class String
  2.     def my_string_method
  3.         "my method"
  4.     end
  5. end
  6. "abc".my_string_method # => "my method"


模式派发

根据名字来选择需要调用的方法。

跟动态派发一回事,何必重复呢。

  1. $x = 0
  2. class C
  3.     def my_first_method
  4.         $x += 1
  5.     end
  6.     def my_second_method
  7.         $x += 2
  8.     end
  9. end
  10. obj = C.new
  11. obj.methods.each do |m|
  12.     obj.send(m) if m.to_s =~ /^my_/
  13. end

  14. $x # => 3


沙盒

在一个安全的环境中执行为授信的代码

  1. def sandbox(&code)
  2.     proc {
  3.         $SAFE = 2
  4.         yield
  5.         }.call
  6. end

  7. begin
  8.     sandbox { File.delete 'a_file' }
  9. rescue Exception => ex
  10.     ex # => #<SecurityError: Insecure operation `delete


作用域门

用 class、module 或 def 关键字来隔离作用域

这个是核心概念,要掌握好。再次提问,如何扁平化作用域(答案见前面)?

  1. a = 1
  2. defined? a # => "local-variable"

  3. module MyModule
  4.     b = 1
  5.     defined? a # => nil
  6.     defined? b # => "local-variable"
  7. end

  8. defined? a # => "local-variable"
  9. defined? b # => nil


Self Yield

把 self 传给当前块

非常有趣的特性,可以大幅简化编码,这应该是从函数式编程学来的吧。

  1. class Person
  2.     attr_accessor :name, :surname
  3.     def initialize
  4.         yield self
  5.     end
  6. end

  7. joe = Person.new do |p|
  8.     p.name = 'Joe'
  9.     p.surname = 'Smith'
  10. end


共享作用域

在同一个扁平作用域的多个上下文中共享变量

利用闭包,将共享变量保护起来。像不像一个类,还是有区别的,作用域不同。

  1. lambda {
  2.     shared = 10
  3.     self.class.class_eval do
  4.         define_method :counter do
  5.             shared
  6.         end
  7.         define_method :down do
  8.             shared -= 1
  9.         end
  10.     end
  11. }.call

  12. counter # => 10
  13. 3.times { down }
  14. counter # => 7


单件方法

在一个对象上定义一个方法,其实是在 该对象 eigenclass 中定义了一个实例方法。 如果 obj 换成类,就是类方法(其实也是实例方法,不过应该叫 Class 的这个特定对象的实例方法)

  1. obj = "abc"
  2. class << obj
  3.     def my_singleton_method
  4.         "x"
  5.     end
  6. end
  7. obj.my_singleton_method # => "x"


代码字符串

执行一段表示 Ruby 代码的字符串。

动态语言没这个功能,都不好意思出来见人。

  1. my_string_of_code = "1 + 1"
  2. eval(my_string_of_code) # => 2



符号到 Proc

把一个符号转换为调用单个方法的代码块。

充分体现 Ruby 变态文化的经典例子。(如果没有 Ruby 这么宽松,优秀的平台,能这么变态吗?)。

  1. [1, 2, 3, 4].map(&:even?) # => [false, true, false, true]


阅读(3274) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~