元编程-类定义

2019-07-09  本文已影响0人  葫芦葫芦快显灵

类定义

注意: 类也是对象

当前类

                        # 顶层的类为Object,若在此定义方法则为Object的实例方法
    class MyClass       # class 打开的MyClass 即为当前类
       def my_method    # MyClass为当前类
            
       end
    end

class_eval 方法

class 限制: 需要知道类名
class_eval 不需要知道类名就能打开当前类

Module#class_eval会在已存在类的上下文中执行一个块

    def add_method_to a_class               # 此时当前类为Object
      a_class.class_eval do                 # 当前类变更 a_class
        def test_a
            'class_eval会修改当前类和self'
        end
      end
    end
    
    add_method_to String
    "a".test_a  # => 'class_eval会修改当前类和self'

对比:
class:

  1. 打开类需要指定类的名字,而且必须时常量
  2. class 是作用域门,使用class会失去局部绑定
    Module#class_eval:
  3. 任何代表类的变量都可以使用class_eval
  4. class_eval使用的是代码块,扁平作用域,可以获取局部绑定

单例方法

ruby 允许给单个对象增加一个方法
只对单个对象生效的方法-单例方法

str = 'this is a singleton_method'

def str.title?
  str.update == str
end

str.title? # => false
str.methods.grep(/title?/) #=> [:title?]
str.singleton_methods      #=> [:title?]

这一段代码单独为str添加一个title? 方法,其他的对象是没有这个方法的

单例方法也可以用Object#define_singleton_method 来定义

类方法

类也是一个对象
类名只是常量

an_obj.a_method
AClass.a_class_method

类方法的调用和对象实例方法的调用语法是一样的,所以类方法的实质就是: 类方法是一个类的单例方法

类宏

attr_accessor 是一个类(Module类)的类方法
当一个模块引入这个模块的时候自动帮我们拓展出这个方法
示例: ruby 内核中的方法 attr_accessor

 class MyClass
    def my_attribute=(value)
      @my_attribute = value
    end
    
    def my_attribute
      @my_attribute
    end
 end
 
 obj = MyClass.new
 obj.my_attribute= 'whj'
 obj.my_attribute # => 'whj'

可以用 Module#attr_*来定义访问器

所有的attr_* 方法都定义在Module中,不管self是类还是模块,都可以使用方法,像这样的方法称之为类宏

单件类(元类/本征类)

一个对象的单例方法没有存在obj中,因为obj不是一个类,也没有存在obj的类中,因为这样obj类的所有实例都能共享这个单例方法

单件类是一个对象特有的隐藏类
Object#class 会将元类隐藏起来

如何进入单件类:

  1. class的特需语法
class << an_obj
    #do_something
end

如果想要获得单件类的引用,需在离开作用域时返回self

obj = Object.new
singleton_class = class < obj
  self
end
singleton_class.class # => Class
  1. Object#singleton_class 能够获取单件类的引用

单件类是对象的单件方法存放的地方
每个单件类只有一个实例,而且不能被继承

单件类和模块结合

类扩展

一个class 引入一个module时,只引入了module的实例方法,不会引入类方法
解决:
将类方法当初一个普通的实例方法,然后在class的单例类中引入这个module-类扩展

module MyModule
 def a_method
    "hello, whj"
 end
end

class MyClass
  class << self
    include MyModule
  end
end

MyClass.a_method # => 'hello, whj'

对象扩展

module MyModule
 def a_method
    "hello, whj"
 end
end

class MyClass
 
end

obj = MyClass.new
class << obj
    include MyModule
end

obj.a_method # => 'hello, whj'

方法包装器

包(GEM)中的方法不能直接修改,希望能够为这个方法附加一个额外的属性, 所有的客户端都会自动获得这个属性

Module#alias_method 别名

class MyClass
    def my_method
        'hello, whj'
    end
end

alias_method :m, :my_method

obj = MyClass.new
obj.my_nethod # => 'hello, whj'
obj.m         # => 'hello, whj'

环绕别名

  1. 给方法重定义一个别名
  2. 重定义这个方法
  3. 在新的方法中调用老的方法

细化包装器

下包含包装器 Module#prepend (较常用)

prepend包含的模块会插入该类祖先链的下方, 意味着它可以重写该类的同名方法

class MyClass
  def my_method
    "a"
  end
end

module MyModule
  def my_method
    "b"                     #也可以使用super 来继承
  end
end

MyClass.class_eval do 
    prepend MyModule
end

obj = MyClass.new
obj.my_method # => "b"

上一篇下一篇

猜你喜欢

热点阅读