• 类宏
  • 环绕别名
  • singleton class
Class就是增强的模块,类定义也就是模块定义。

5.1 Class Definitions Demystified

5.11 Inside Class Definitions

美  [,dɛfɪ'nɪʃən]

Self关键字:Ruby的每一行代码都会在一个对象中被执行--这个对象就是所谓的当前对象,用self表示。

class and module 也是 object, so class  also can  be "self"

5.12 The Current Class 当前类

Ruby程序的任何位置,总会存在一个当前对象。因此就有一个当前类/模块存在。定义一个方法时,这个方法就会成为当前类的一个instance method.

1.在程序的顶层,当前类是Object,这是main对象的类。(所以在顶层定义方法会成为Object的private method。)

p self.class  #=> Object
def my
  "123"
end
p self.private_methods(false)
#=> [:public, :private, :define_method, :include, :using, :DelegateClass, :my]

2.在一个方法中,当前类就是当前对象的类。在一个方法中嵌套另一个方法,新方法会定义在self所属的类中。

class C
  def m1
    def m2;end
  end
end
p C.instance_methods(false)  #=>[:m1] 这是因为def定义的代码不会立即执行
obj = C.new
obj.m1  #obj是当前对象self。调用m1方法,执行里面的代码(声明了m2方法),m2方法的类是self的类
p C.instance_methods(false)  #=>[:m2, :m1]

3.当用class/module关键字时,当前对象self转换为class/module.这个类也称为当前类。

class_eval

如何在不知道类名字的情况下,打开一个类? 使用Module#class_eval方法(module_eval)

例子,下面例子给String增加了一个新的方法m:

p self

p self.class

 
def add_method_to(a_method)
    p self

p self.class

  a_method.class_eval do
    def m
      "hello"
    end
    p self
    p self.class
  end
end
 
add_method_to(String)
p "ss".m
结果:
 
main
Object
main
Object
String
Class
"hello"
 

class_eval方法同时修改了self和当前类 。

class_eval方法和class比较:

  • 灵活:可以对任何代表类的变量使用class_eval, 而class关键字只接收常量命名。
  • scope: class_eval flatten the scope可以接收block外部scope的变量. 而class关键字是Scope Gate.

module_eval/class_eval 一样,module_exec/class_evec可以接收额外的代码块做参数。class_exec(arg...) {|var...| block } → obj

instance_eval 和 module_eval/class_eval 的 选择:

  • instance_eval打开非类的对象,可以修改对象的方法和实例变量
  • class_eval打开类,然后用def定义方法。
  • 如果要打开的一个对象也是类/模块,选择可以准确表达你的目的的方法。
 
 

小结:

  • Ruby总是追踪当前类/模块的引用,所有使用def的方法都成为当前类的实例方法。
  • 类定义,当前类就是self--正在定义的类
  • 如果有一个类的引用,可以用class_eval/module_eval打开这个类。(打开类:对已经存在的类进行动态修改)
 

5.13Class Instance Variables  类实例变量

Ruby解释权规定所有的实例变量都属于当前对象self。

class Myclass
  p self #=> Myclass
  @my_var = 1
end
 
puts Myclass.instance_variables
# => @my_var

类的实例变量,类的对象的实例变量。是两回事。

class Myclass

  @my_var = 1  #只能被类自身访问,子类都不行。
  def self.read    #类方法,也可以这么写:Myclass.read
    @my_var
  end
  def write
    @my_var = 2
  end
  def read
    @my_var
  end
end
 
obj = Myclass.new
p obj.read  #=>nil
p obj.write #=>2
p obj.read  #=>2
p Myclass.read  #=> 1
 

类变量:@@var,可以被子类和类的实例调用,为了避免以外,一般不使用类变量

5.3 Singleton Methods 单件方法

单件方法可以增强某个对象(类也是对象),是ruby最常见的特性。

Object#define_singleton_method:

define_singleton_method(symbol) { block } → symbol

str = "just a regular string"
# def str.title?也可以这么写,即用def关键字定义单件方法。
#   self.upcase == self
# end

#或者这么写,用Object#define_singleton_method定义单件方法

str.define_singleton_method(:title?) do
  self.upcase == self
end
 
p str.title?
p str.methods.grep /^title?/
p str.singleton_methods
结果:
false
[:title?]
[:title?]

5.32 The truth about Class Methods

详细(5.4Singleton Classes)

AClass.a_class_method 这是在一个由常量引用的对象(类)上调用方法。

an_object.a_method 这是在一个由变量引用的对象上调用方法。

语法完全一样。

 

类方法的实质是: 它是一个类的单件方法。

object可以是对象引用,类,self。底层机制一样的。

def object.method

...

end

Duck Typing:鸭子类型。

Ruby这样的动态语言,对象的类型并不严格与它的类相关,“类型”只是对象能相应的一组方法。这也叫鸭子类型。 116页

5.33Class Macro类宏

什么是拟态方法?看起来像关键字或者别的,就是不像方法,但其实是方法。

puts, private, protect,  attr_accessor, attr_read, attr_write(类宏)

什么是类宏?

普通的方法,看起来像关键字,其实是在类里定义好了。如attr_accessor, attr_read, attr_write。 类宏依靠类方法实现。

本例子:类宏用了类方法实现。

也使用了:

Dynamic method(Module#define_method):用于普获对旧方法的调用,并把调用转发给重命名的新方法。

Dynamic#dispatch(Module#send)

Kernel#warn:  warn(msg, ...) → nil, 一般用于测试代码。

class Book

  def title;end
  def subtitle;end
  def lend_to(user)
    puts "Lending to #{user}"
  end
 
  def self.deprecate(old_method, new_method)
    define_method(old_method) do |*args, &block|
      warn "Warning: #{old_method}() is deprecated. Use #{new_method}()"
      send(new_method, *args, &block)
    end
  end
  # 类宏
  deprecate :GetTitle, :title
  deprecate :title2, :subtitle
  deprecate :lend_to_user, :lend_to
end
 
b = Book.new
b.lend_to_user("bill")
结果:

Warning: lend_to_user() is deprecated. Use lend_to()

Lending to bill

5.4Singleton Class

5.41 the Mystery of Singleton Methods

对象的单件方法不存在对象里,也不存在它的类和超类中。

单件方法存在哪里?类方法是单件方法的特例,它又存在哪里?

答案:见5.42(绿色框)。

5.42 Singleton Classes Revealed

被隐藏起来,需要使用Object#singleton_class或class<<关键字 得到它

str = "just a regular string"
# 定义了2个str的单件方法 house, title?
 
def str.house
  puts "house:我是一个单件方法"
end
def str.title?
  self.upcase == self
end
 

#两种显示对象的单件类的代码

p str.singleton_class #=>#<Class:#<String:0x00007f8f3e03dd68>>
 
puts class<<str      #=>#<Class:#<String:0x00007f8f3e03dd68>>
  self
end
p str.singleton_class.instance_methods(false)  #=> [:house, :title?]

由⬆️代码可知:

一个对象的单件方法(可以有多个单件方法) --存储在--> 一个单件类中;

因此,一个单件类也只有这么一个实例对象。

5.43 单件类在ancesstors中的位置。

class C
  def a_method
    "C#a_method()"
  end
end
 
class D < C
end
 
obj = D.new
 
def obj.a_singleton_method
  "obj#a_singleton_method()"
end
p obj.singleton_class.instance_methods(false)    #=>[:a_singleton_method]

p obj.singleton_class.ancestors

#=>[#<Class:#<D:0x00007fa9af03cc98>>, D, C, Object, Kernel, BasicObject]

由此可知,如果对象有单件方法,那么当查找这个对象的任意方法时,Ruby从它的单件类开始查找,用#obj表示单件类

obj->#obj->D->C->Object

Ruby模型对象的7条规则:

  1. 只有一种对象,要么是普通对象,要么是模块
  2. 只有一种模块,可以是一个普通模块,一个类或者一个单件类
  3. 只有一种方法,它存在于一个模块--通常是一个类中
  4. 每个对象(包括类)都有自己的“真正的类”--要么是一个普通类,要么是一个单件类
  5. 除了BasicObject类没有超类外,每个类有且只有一个祖先--要么是一个类,要么是一个模块。这意味着任何类只有一条向上的,直到BasicObject的祖先链
  6. 一个对象的单件类的超类是这个对象的类:一个类的单件类的超类是这个类的超类的单件类
  7. 调用一个方法,Ruby先向右迈一步进入接受者真正的类,然后向上进入祖先链。这是Ruby查找方法的方式。

类方法的语法:

class Myclass

def self.another_class_method; end

end

或者 (这是最好的选择✅,可以在class<<关键字中定义多个类方法,方便阅读代码。)

class Myclass

class << self

def yet_another_class_method; end

end

end

不建议:

def Myclass.a_class_method; end

单件类和instance_eval方法

class_eval方法会修改self,同时也就变更了当前类。instance_eval内修改self为调用它的receiver.

如果在instance_eval的{}中,定义一个def方法,当前类变为receiver的单件类, 这个方法成为receiver的singleton m

ethod.

所以,instance_eval也会变更当前类.不过Ruby程序员一般不用这个特点定义方法。

instance_eval的标准含义: 我想修改当前self.

s1, s2 = "abc", "def"
p self                #=> main
s1.instance_eval do
  p self              #=> "abc"
  def swoosh
    reverse!
  end
end
p s1.swoosh
p s1.singleton_class.instance_methods(false)   #=> [:swoosh]

“类属性”

Ruby对象没有属性的概念。所谓属性只不过是用拟态方法加上的一对儿读写的方法罢了。

这种简写的拟态方法,如attr_*也称为类宏Class Macro,其实就是普通的方法而已。

因此,类属性就是是这个类自己用的方法,类方法,放到这个类的单件类里.

class Myclass
  class << self
    attr_accessor :x
  end
end
p Myclass.x = "aa"

5.5 Module Trouble

如何用包含模块的方式来定义一个类方法?

module MyModule
  def my_method
    "sdf"
  end
end
 
class Myclass
  class << self
    include MyModule
  end
end

在模块中定义普通的实例方法,然后把模块放到类的单件类中,成为类方法。这称为Class Extension 类扩展

这种方法同样适用于对象,因为类方法本身就是单件方法的特例。所以可以把Module包含进对象的单件类中。这称为Object Extension.

module MyModule
  def my_method
    p "hello"
  end
end
 
obj = Object.new
 
class << obj
  include MyModule
end
 
obj.my_method  #=> "hello"
p obj.singleton_methods   #=> [:my_method]
 

Object#extend

类扩展和对象扩展使用普遍,所以打包代码做了一个extend方法。相当于快捷方式。

module Mymodule

  def my_method; "hello"; end
end
 
obj = Object.new
obj.extend(Mymodule)
p obj.my_method
 
class Myclass
  extend Mymodule
end
 
p Myclass.my_method

5.6Method Wrappers

方法包装器

  • Around Aliases
  • Refinement Wrapper
  • Prepended Wrapper

用途:有一个不能直接修改(在一个库中)或者修改的地方太多(怕漏掉)的Method, 我们希望为这个方法包装额外的功能。可以使用Method Wrappers.


Module#alias_method :new_name, :old_name 

Ruby提供alias关键字,为顶级作用域Object下提供帮助。

alias :new :old   #⚠️ 没有逗号。

环绕别名

名字类似指针

  1. 给方法a定义一个别名b
  2. 重新定义a,此时我的理解b方法是过去的那个方法,a重新定义了所以是新的方法
  3. 在新的方法a中调用b方法,就叫环绕别名。

绝大部分Ruby操作符合实际上是方法,例如整数的+操作符是Fixnum#+方法的语法糖。

问题:重新定义+方法。让(x+y)的结果返回(x+y+1).?

解答:只能用环绕别名的方法,因为新的+方法依赖于旧的+方法。super方法不能重复用了

class Fixnum
  alias_method :old_plus, :+
  def +(value)
    self.old_plus(value).old_plus(1)
  end
end
p 2 + 1  #=>warning: constant ::Fixnum is deprecated  4

细化封装器 Refinement Wrapper

使用refine(mod){},配合using方法。作用域是:

  1. refine内部
  2. 从using开始到文件结束(如果是在顶层上下文中调用using)
  3. 或者从using开始到模块结束(如果是在模块中调用using)
注意:细化有很多限制。

module StringRefinement

  refine String do
    def length
      super > 5 ? 'long' : "short"
    end
  end
end
 
using StringRefinement
 
p "asdfa".length   #=> short
module Mymodule
  using StringRefinement
  p "12345678".length#=>long
end

Prepend Wrapper 下包含包装器(祖先链:当前类的下面)

module ExplicitString
  def length
    super > 5 ? 'long' : 'short'
  end
end
 
String.class_eval do
  prepend ExplicitString   
end
p "tttffff".length

#把包含的模块插入祖先链下方,而非上方。这意味着prepend方法包含的模块可以覆写该类的同名方法,同时可以通过super调用该类中的原始方法。


5.8小结

  • 类定义对self(调用方法时默认的receiver)和当前类(定义方法时默认的所在地)的影响
  • singleton_method, 单件类,类方法。 从新认识了对象模型(7条规则),和方法查找。
  • 新的法术,类实例变量(类自己用不能继承),类宏(拟态简写),下包含包装器
  • 模块就是类

3-13《元编程》第5章Class Definitions 3-14(5-4Singleton Classes,2小时)3-15(3小时✅)的更多相关文章

  1. 3-8《Ruby元编程》第二章对象模型

    <Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...

  2. 一道模板元编程题源码解答(replace_type)

    今天有一同学在群上聊到一个比较好玩的题目(本人看书不多,后面才知是<C++模板元编程>第二章里面的一道习题), 我也抱着试一试的态度去完成它, 这道题也体现了c++模板元编程的基础和精髓: ...

  3. 初识C++模板元编程(Template Mega Programming)

    前言:毕设时在开源库上做的程序,但是源码看得很晕(当时导师告诉我这是模板元编程,可以不用太在乎),最近自己造轮子时想学习STL的源码,但也是一样的感觉,大致了解他这么做要干什么,但是不知道里面的机制. ...

  4. 3-11 《Ruby元编程》第4章block块 3-12

    第4章代码块blocks 基础知识 作用域:用代码块携带variables through scopes 通过传递block给instance_eval方法来控制作用域. 把block转换为Proc, ...

  5. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

  6. 异步编程系列06章 以Task为基础的异步模式(TAP)

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  7. atitit.元编程总结 o99

    atitit.元编程总结 o99.doc 1. 元编程(Metaprogramming) 1 2. 元编程的历史and发展 1 3. 元类型and元数据 1 4. 元编程实现方式 2 4.1. 代码生 ...

  8. C++ 元编程 —— 让编译器帮你写程序

    目录 1 C++ 中的元编程 1.1 什么是元编程 1.2 元编程在 C++ 中的位置 1.3 C++ 元编程的历史 2 元编程的语言支持 2.1 C++ 中的模板类型 2.2 C++ 中的模板参数 ...

  9. ES6中的元编程-Proxy & Reflect

    前言 ES6已经出来好久了,但是工作中比较常用的只有let const声明,通过箭头函数改this指向,使用promise + async 解决异步编程,还有些数据类型方法...所以单独写一篇文章学习 ...

  10. Python类元编程

    类元编程是指在运行时创建或定制类.在Python中,类是一等对象,因此任何时候都可以使用函数创建新类,而无需用class关键字.类装饰器也是函数,不过能够审查.修改,甚至把被装饰的类替换成其他类.元类 ...

随机推荐

  1. git提交时候出错

    Please make sure you have the correct access rights and the repository exists. 解决方案: 主要原因是没有加载keygen ...

  2. python实现常量const

    新建const.py: #-*-coding:UTF-8-*- #Filename: const.py # 定义一个常量类实现常量的功能 # # 该类定义了一个方法__setattr()__,和一个异 ...

  3. 应用程序无法正常启动0xc000007b怎么解决

    解决方法两种: 1. 网上搜索中最常见的,缺少DirectX 9 ,去下载一个安上就OK了. 2.第二种情况比较操蛋,其实报的错误应该是:mfc100u.dll丢失 .我在两台电脑上装了相同系统后,台 ...

  4. Linux基础命令---find

    file 判断指定文件的文件类型,它依据文件内容判断,并不依据扩展名.此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法     ...

  5. phonegap 开发案例

    PhoneGap-Android-HTML5-WebSocket 不使用任何框架,教你制作网页滑动切换效果 http://www.csdn.net/article/2012-04-17/2804644 ...

  6. CocoaPods的安装及使用

    CocoaPods安装使用及配置私有库 http://www.exiatian.com/cocoapods%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E5%8F%8A%E ...

  7. Python3基础 input 输入浮点数,整数,字符串

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  8. HDU 4135 Co-prime(容斥:二进制解法)题解

    题意:给出[a,b]区间内与n互质的个数 思路:如果n比较小,我们可以用欧拉函数解决,但是n有1e9.要求区间内互质,我们可以先求前缀内互质个数,即[1,b]内与n互质,求互质,可以转化为求不互质,也 ...

  9. 手机常用meta标签-有注释

    <!-- 设置字体编码 --> <meta charset="UTF-8"> <!-- 视图窗口,移动端特属的标签. --> <meta ...

  10. maven 插件在线安装

    NO.1 在Eclipse中安装Maven插件安装详解 前言 本来是没打算写博客的,作为一个13年毕业的菜鸟,自认为水平太渣写不出什么好文章,但是前些日子看到一篇鼓励性质的文章说,技术人员的成长靠的就 ...