Ruby中类 模块 单例方法 总结

# 1 单例方法的一种写法和定义
# 在Ruby里,可以给具体的实例对象添加实例方法,这个方法只属于这个实例
# 对象,我们把这样的方法称之为单例方法。
# 单例方法也叫作单件方法。定义单例方法,首先要生成一个实例对象,其次,
# 要在方法名前加上对象名和一个点号“.”。
# 在下面示例中,对象p1不可以laugh , laugh方法只属于p2对象。
# 实例方法,属于类的每个实例对象。单例方法只出现在单个实例对象中。用单
# 例方法可以极大地丰富多态性在Ruby中的表现力。
class Person
end

p1 = Person.new
p2 = Person.new

def p1.lagugh
  puts '这是p1所独有的单例子方法'
end

# 2 接下来我们再看下导入模块
module MyModule
  def a_method
    puts 'Hello MyModule'
  end
end

# 3 直接extend导入 将模块的实例方法变为类方法
class F
  extend MyModule
end
F.a_method

# 4 直接include导入 将模块的实例方法变为实例方法
class H
  include MyModule
end
h = H.new
h.a_method

# 5 class << self 中 include Mymodule
# 此写法与类方法相同 使用include导入后变成类方法
class G
  class << self
    include MyModule
  end
end
G.a_method

# 6 用class << self 的写法无法使用extend
# class H
#   class << self
#     extend MyModule
#   end
# end
# H.a_method
# h.a_method
# H.a_method

# 7 使用实例 extend导入
obj = Object.new
obj.extend(MyModule)
obj.a_method

# 8 先说结论,本质上混入 module 都是将 Module 中的方法引入到对象(obj, 类E, 类F)的singleton_class(单件类)中.
# Ruby允许给单个对象增加方法,这种只针对单个对象生效的方法,称为单件方法。
# 9 extend与inclue 的区别
# include include to Object class
# extend include to Obj's singleton class
# 10总结 在类定义中使用include和extend时候
# include将类中的实例方法mixin变成类的实例方法
# extend将类中的实例方法变成类方法
# 但include可以使用定义类方法的class << self方法导入
# extend则可以通过实例mixn 究其原因
# include是将模块中的方法mixin对象的类中
# extend将模块中的方法mixin对象的单例类中
# 11 单例方法只属于这个对象 不属于这个类 这个类的其他实例都没有这个方法 因为单例方法就是这个类的 单例类的实例方法
# 每个对象的singleton class都不一样 每个对象都有自己的singleton class

str = 'Hello Ruby'
str2 = 'Hello Java'
def str.foo
  puts 'str的单例方法'
end

str.foo
# str2是没有foo这个属于str的单例方法的
# str2.foo

# 12 通过输出可知 str 和 str2 这两个类的内存地址不同 是相同类型的不相等的类
# str 和 str2 的类型也是 Class 类 把 Class看成对象
p str2.singleton_class
p str.singleton_class

# singleton_class 也是 Class类的一个对象
p str.singleton_class.class

# str2的实例方法中就没有str实例方法foo
# 13 所以 方法foo 可以看成是这个单例类的实例对象的方法
p str.singleton_class.instance_methods(false)
p str2.singleton_class.instance_methods(false)
p str.methods.grep(/foo/)
p str2.methods.grep(/foo/)

# 14 Ruby中类也是对象,而类名只是常量,所以在类上调用方法其实跟在对象上调用方法一样:
# 15 类方法的实质是:它是一个类的单件方法,实际上如果比较单件方法的定义和类方法的定义,会发现其实二者是一样的.

## 在定义单件方法时,存在着相似的地方
## 二者均使用了def关键词做定义。
## 上面的object可以是*对象的引用、常量类名或者self。
# 16 类方法定义与单例方法定义的比较
obj = Object.new

# 17 单例方法的定义
def obj.a_singleton_method
end

# 18 类方法的定义
class Myclass
  def self.clazz_method1
    puts '类方法1'
  end

  def Myclass.clazz_method2
    puts '类方法2'
  end

  class << self
    def clazz_method3
      puts '类方法3'
    end
  end
end

class << Myclass
  def clazz_method4
    puts '类方法4'
  end
end

def Myclass.clazz_method5
  puts '类方法5'
end

Myclass.clazz_method1
Myclass.clazz_method2
Myclass.clazz_method3
Myclass.clazz_method4
Myclass.clazz_method5

# 17 所以,单件方法可以认为是添加在对象的某个空间内只针对该对象有效的方法.
# 如果对象是个实例对象,添加的方法就是他的单件方法,
# 如果对象是个类,添加的方法就是这个类的类方法.

# 18 我们知道Ruby中对象的方法的查找顺序是:先向右,再向上,其含义就是先向右找到对象的类,
# 先在类的实例方法中尝试查找,如果没有找到,再继续顺着祖先链找.
# 前面介绍的单件方法是指那些只针对某个对象有效的方法,那么如果为一个对象定义了单件方法,
# 那么这个单件方法的查找顺序又应该是怎样的?

class Myclass
  def my_method
  end
end

obj = Myclass.new

def obj.my_singleton_method
end

# 19 首先,单件方法不会在obj中,因为obj不是一个类,其次它也不在MyClass中,
# 那样的话所有的MyClass实例都应该能共享调用这个方法,也就构不成单件类了.
# 同理,单件方法也不能在祖先链的某个位置(类似superclass: Object)中.
# 正确的位置是在单件类中,这个类其实就是我们在irb中向对象询问它的类时(obj.class)得到的那个类,
# 不同的是这类与普通的类还是有稍稍不同的.也可以称其为元类或本征类.
# 前提及到的实例对象的单件方法和类的类方法在创建上是类似的,
# 所以,通过的关键词class配合特殊的语法应该可以将其取到.class << obj.

# 20 如果存在单件方法那么查找顺序先去类去中的单例方法
class C
  def a_method
    puts 'C#a_method'
  end
end

class D < C
end

d = D.new
d.a_method # C#a_method

puts 'abc'.singleton_class #<Class:#<String:0x0000000002ec90a8>> String类的实例

class << d
  def d_method
    puts 'D#d_method'
  end
end

puts d.singleton_class #<Class:#<D:0x0000000002ec9238>> Class类的实例
puts d.singleton_class.class # singleton_class 的类型是Class
## 既然d.singleton_class 也是个类,那么久试着查一下他的父类
puts d.singleton_class.superclass # D
## singleton_class是一个类,那么它肯定有父类,它的父类就是对象所属的类
## 单例方法则存在于对象的特征类中
# 单例方法存在于 对象单例类的实例方法中
puts d.singleton_class.instance_methods.grep(/d_method/)  ## d_method
puts d.singleton_class.instance_methods(false)

# 打开单件类
# Ruby 中 class 作用是把代码的上下文变换到这个类中,也就是『打开类』,
# 同一个类可以在任何地方被打开,也因此别人的类可以被自己随意打开并改写.
# Ruby提供了两种方法获取单件类的引用,一种是通过传统的关键词class配合特殊的语法。
# 通过关键字 class << obj 打开实例对象的单件类空间,为对象添加一系列的单件方法.
# 单件类的本质还是一个类,是一个拥有唯一实例的类,所以叫单件类,或者单例类.

# 定义对象的一系列单件方法
# class << an_object
#   def a_singleton_method
#     puts '单例方法1'
#   end
# end

obj = Object.new
singleton_class = class << obj
                    self
end
p singleton_class.class

# 另一个方法是,通过Object#singleton_class方法来获得单件类的引用:

p singleton_class = 'abc'.singleton_class  #<Class:#<String:0x0000000002ed7d10>>
p singleton_class.class # => Class

# 单件类的特性
# 1. 每个单件类只有一个实例(被称为单件类的原因),而且不能被继承.
# 2. 单件类是一个对象的单件方法的存活所在.
# 3. 引入单件类后的方法查找
# 基于上面对单件类的基本认识,引入单件类后,Ruby的方法查找方式就不应该是先从其类(普通类)开始,
# 而是应该先从对象的单件类中开始查找,如果在单件类中没有找到想要的方法,它才会开始沿着类(普通类)开始,
# 再到祖先链上去找.这样从单件类之后开始,一切又回到了我们在没有引入单件类时候的次序.
# 类方法,实例方法,单例方法
# 类方法只有类本身可以调用,在ruby中,类方法是一种特殊的单例方法.
# 之前的例子中可以得到这样的结论,singleton_class也是一种类,
# 在ruby中所有的类又都是对象.对象都有对应的singleton_class.

# Ruby 中方法的查找 遵循的规则是 'one step to the right, then up',即 向右一步然后向上.
# Ruby中查找顺序
# 1singleton method defined on object
# 1* super class of singleton class
# 2 instance method defined in class
# 3 instance method defined in module
# reverse order in which they were included
# 4 instance method defined in ancestors
# 5 method_missing defined in Kernel module

class C
  def a_method
    puts 'C#a_method'
  end

  def self.a_class_method
    puts 'C#a_class_method'
  end
end

class D < C
end

obj = D.new

class << obj
  def a_singleton_method
    puts 'obj#a_singleton_method'
  end
end

# 欲调用 obj.a_singleton_method
# 此时调用对象是obj ,则会先去 #obj 处查找(向右一步)
# #obj
puts obj.a_singleton_method #obj#a_singleton_method

# 欲调用 D.a_class_method
# 此时调用对象是D ,则会先去 #D 出查找(向右一步)
# D.singleton_class 然后往上查(superclass)
# 单例类的父类就是对象所属的类
puts D.singleton_class.superclass #<Class:C>
puts D.singleton_class.class #Class
puts D.singleton_class.class.superclass #Module
# #D -> #C
# D的类方法方法 a_class_method 在 #C 里
puts D.a_class_method ## C#a_class_method

# 欲调用 obj.a_method
# 此时调用对象是obj ,则会先去 #obj 处查找(向右一步)
puts obj.singleton_class ## #<Class:#<D:0x007f9e88340b60>>
# obj.singleton_class 然后往上查(superclass)
puts obj.singleton_class.superclass ## D
# #obj 的父类 是 D 然后往上查(superclass)
puts D.superclass  ## C
# D 的父类 是 C
# 查找顺序即: #obj -> D -> C
# D的实例方法 a_method 在 类C 里
obj.a_method  ## C#a_method

###module
module C
  def foo
    puts 'foo in C'
  end
end

module D
  def foo
    puts 'foo in D'
  end
end

####classess
class A
  def foo
    puts 'foo in A'
  end
end

# class B < A
#   # include C
#   # include D
#   # def foo
#   #   puts 'foo in B'
#   # end
# end

class E
end

a = A.new
# b = B.new
e = E.new

# def b.foo
#   puts 'singleton foo'
# end

# 1 第一种情况有单例方法
# 输出 foo in A 和 singleton foo
a.foo
# b.foo
e.foo

# 2 第二种情况去除 单例方法
# 输出 foo in A  foo in B

# 3 第三种情况去除 类中的实例方法
# 输出foo in A 和 foo in D

# 4 第四种情况 从父类中找实例方法
# 输出foo in A 和 foo in A

# 5 父类中找不到的话就是方法丢失

# 总结找方法 先找单例中的方法 然后是类中定义的实例方法 模块中的实例方法 祖先类的实例方法 underfined method

 

转载于:https://my.oschina.net/u/3544267/blog/1574387

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值