Ruby中的反射(Reflection)-通过类名称构造类对象

在Java语言中,提供了发射机制,通过发射机制可以通过字符串构造出这个对象,可以获取对象的所有方法(包括私有方法),可以调用私有方法,可以更改成员变量的值(包括私有的成员变量)。
Ruby也是面向对象的高级语言,当然也提供了反射机制,今天我们讨论通过类名称构造类对象的功能。

我们先看普通的构造:

module ModuleA
#the class name, later we will use it to create the corresponding object
CLASS_NAME_OF_WOOD = "ModuleA::Wood"
CLASS_NAME_OF_WOODDESK = "ModuleA::WoodDesk"
CLASS_NAME_OF_WOODCHAIR = "ModuleA::WoodChair"

class Wood
def initialize
@desc = "I am a primal wood"
end

def say
puts @desc
end
end

class WoodDesk < Wood def initialize @desc = "I am a desk made of wood" end def say_private puts "actually, i have some bug but no public" end public :say private :say_private end class WoodChair < Wood def initialize @desc = "I am a chair made of wood" end def say_private puts "I Want get married with a WoodDesk..." end def smile puts "ha hah hah haha ...." end public :say private :say_private, :smile end end
定义了一个基础类Wood,有两个子类:WoodDesk, WoodChair,子类有分别有一个私有方法 say_private。

我们new出对象来执行:

#the normal initailze
wood = ModuleA::Wood.new
wood.say
desk = ModuleA::WoodDesk.new
desk.say
chair = ModuleA::WoodChair.new
chair.say

#try call the private method
puts "desk respond to say_private? #{desk.respond_to? :say_private}"
desk.say_private if desk.respond_to? :say_private

上面代码,执行public方法say,然后尝试执行private方法 say_private,执行先check是否能够执行,返回结果是不能执行,desk.respond_to? :say_private返回false:

I am a primal wood
I am a desk made of wood
I am a chair made of wood
desk respond to say_private? false

好,现在我们通过反射机制来构造对象,并尝试执行其私有方法。

我们注意到模块的定义中有三个常量,定义的是类名称,

#the class name, later we will use it to create the corresponding object
CLASS_NAME_OF_WOOD = "ModuleA::Wood"
CLASS_NAME_OF_WOODDESK = "ModuleA::WoodDesk"
CLASS_NAME_OF_WOODCHAIR = "ModuleA::WoodChair"

下面会通过这三个变量来理解Module.constants方法。

下面代码片段,基于上面的类定义:

#get all module constants
obj_list = Array.new
tmp_const_sym_list = ModuleA.constants
tmp_const_sym_list.each do | sym |
obj_list << ModuleA.const_get(sym) puts "calss = #{sym.class}, value = #{sym}" end

我们注意到 ModuleA.constants,这个方法是Module模块中的,其作用是返回模块中所有常量的Symbol对象。我们看结果输出:

calss = Symbol, value = CLASS_NAME_OF_WOOD
calss = Symbol, value = CLASS_NAME_OF_WOODDESK
calss = Symbol, value = CLASS_NAME_OF_WOODCHAIR
calss = Symbol, value = Wood
calss = Symbol, value = WoodDesk
calss = Symbol, value = WoodChair

从结果中看到,定义的三个常量和类名称都被返回了。所以注意:Ruby中的常量是包含定义的常量(变量)和类名称,注意他们都是Symbol对象。

不过我们是需要根据类名称构造类对象,那么那三个常量就是没用的,需要删除。我们通过正则表达式匹配名字,来过滤。上面的代码修改一下:

#get all module constants
sym_list = Array.new
tmp_const_sym_list = ModuleA.constants
tmp_const_sym_list.each do | sym |
puts "calss = #{sym.class}, value = #{sym}"
sym_list << ModuleA.const_get(sym) if /^Wood\w*/ =~ sym.to_s end

sym_list << ModuleA.const_get(sym) if /^Wood\w*/ =~ sym.to_s,仅保存以Wood开头的symbol,这样我们就过滤掉了那三个常量。 找都类名称之后,开始构造对象:
#create object from symbol
obj_list = Array.new
sym_list.each do | sym |
obj = sym.new
obj_list << obj puts "create the object: #{obj}" end begin obj_list.each do | wood | wood.say end

调用Symbol的new方法构造出次对象(sym.new),然后我们调用对象的say方法:

create the object: #
create the object: #
create the object: #
I am a primal wood
I am a desk made of wood
I am a chair made of wood

达到了我们预期的结果。

网友评论comments

发表评论

电子邮件地址不会被公开。 必填项已用*标注

  1. XiaoMing说道:

    Ruby 和 Python比能否简单说点有什么特色的东西,还有Lua(看到有人写WireShark的插件)。
    你们用到了?

    • Mao说道:

      这个有必要交流一下,哈哈。你教教我们python。
      个人看法:
      1、python用tab锁进来替代{},太难维护了;而ruby是 end,容易读。
      2、ruby的block功能和强大。
      3、ruby的API设计的很好用,如Array.delete_if(…)删除不符合条件的元素。
      4、ruby支持re-open一个类,如你可以给string加方法。

      • ibusybox说道:

        python用 tab来格式化 这个毁誉参半 好处是编程规范 不过coding的兄弟基本上都是很不羁的
        ruby的block确实很强大,非常灵活;完全是模版的一个超集 而且好象还是运行时检查的 这太变态了
        ruby抽象得太夸张了,很多逻辑处理它都有现成的方法

        老毛 ruby现在也是玩得很精了

      • XiaoMing说道:

        除了第一条各有看法,剩下都有类似替代品.唉,人生苦短,用到在学吧

        虽然加了框框,没有高亮,还是不好看:-)

        • Mao说道:

          管理员再改进改进?
          你可以整一些python的上来,探讨探讨。

          • XiaoMing说道:

            比如人家这个代码高亮做的好coolshell

          • XiaoMing说道:

            1.给内建类添加方法,在网上找了个代码,以字符串为例
            (有点搞笑,大概是hack了源码之后才搞出来的,实际代码中决不能这样做。)
            import ctypes as c

            _get_dict = c.pythonapi._PyObject_GetDictPtr
            _get_dict.restype = c.POINTER(c.py_object)
            _get_dict.argtypes = [c.py_object]
            def get_dict(object):
            return _get_dict(object).contents.value

            #add method to a built in type
            def sayHello(self):
            print 'hello world'
            get_dict(str)['sayHello'] = sayHello
            "abc".sayHello()

            python语法本身确实不能给内建的类做扩展,这样是为了保证最基本系统库的纯净,通常做法是继承他再搞个类。

            2.
            关于ruby的block,网上说比python lambda更强大,但没理解到精髓(这个可能涉及到高阶编程思想了,也许值得好好了解一下),

            python只会这样的:
            arr = ["d","a","b","c"]
            arr.sort(lambda x,y: cmp(x,y))

            3.类似delete_if,应当可以用python列表解析( List comprehensions)代替,
            arr = [2,3,4,5,6]
            newarr=[x%2 for x in arr]

            列表解析提供的是更通用的一种方式,单独为delete_if提供api可能会让我再需要某个功能的时候就猜测,是不是还有multiple_if, tostr_if,记忆上会有负担:)

            真是各有千秋吧,对比学习也不错,只要不是单纯的holy war,哈哈

    • Mao说道:

      可以参考:http://c2.com/cgi/wiki?PythonVsRuby

      • Mao说道:

        1、第一点,有一个专业术语:open class,意思是可以把一个class重新open。在某些场景下还是很有用的。比如你的代码中用到了String对象,你很想要一个方法但是原生的api不提供,你这种情况下就可以自己加一个方法。具体可以参考:http://blog.tektea.com/archives/299.html。

        2、第二点,Python的lambda实际上和ruby中的Proc更象一些:

        def gen_times(factor)
        return Proc.new {|n| n*factor }
        end

        times3 = gen_times(3)
        times5 = gen_times(5)

        times3.call(12) #=> 36
        times5.call(5) #=> 25
        times3.call(times5.call(4)) #=> 60

        我想Proc设计出来是弥补ruby在面向过程领域的缺陷。
        不过在ruby1.9中也支持lambda。
        我想大家都很迷惑ruby中的block,Proc,lambda,我整理一下到时候讨论。

        3、第三点,我觉得是API封装的设计思想吧。
        之前在martin fowler的博客上看到Java和ruby的API设计比较,Java也设计的很好,但是用起来,开发者要多写一些代码,ruby就方便一些。他举例了:

        ary = Array.new
        first = ary.first
        last = ary.last

        而Java的API,如ArrayList,就不提供获取第一个和最后一个元素的功能,需要开发者多写几行代码。

        你说到对比学习,我想到在文学和历史和哲学领域,都有比较文学,比较历史学,比较哲学,就是拿同一个时代不同地域或不同文化的文学,历史,哲学思想来做比较,也很有意思的,能够得出不少规律。

        我们可以搞一个比较编程,哈哈。

  2. ibusybox说道:

    xiaoming的python炉火纯青了 啥时候发发python相关的

    • XiaoMing说道:

      不敢不敢,一旦不在正式项目中用,很快就忘差不多了,再说从来对没有哪个工具敢说炉火纯青。等真有心得再来写。
      对了你们都用mac,safari, 能不能说说有多爽~

Copyright © 2012-2017 YUNWEIPAI.COM - 运维派 - 粤ICP备14090526号-3
扫二维码
扫二维码
返回顶部