文档章节

Ruby 中的单例

穿山
 穿山
发布于 2017/11/15 20:54
字数 1769
阅读 26
收藏 0

singleton_class 和Singleton Method

class 混入 module 时的相关思考

在Ruby里, 我们可以通过在 class 里混入 module 来达到一些用途,那么 module 里面的代码是怎么混入class 里,又是被放置在了什么地方呢?

module MyModule  
  def a_method  
    puts "hello, MyModule#a_method"  
  end  
end 

## 直接extend module 
class F  
  extend MyModule  
end  
F.a_method ## hello, MyModule#a_method  
  
## class << self 中 include MyModule
class E  
  class << self  
    include MyModule  
  end  
end  
E.a_method ## hello, MyModule#a_method  

## 对象也是可以混入一个module
obj = Object.new  
obj.extend(MyModule)  
obj.a_method  ## hello, MyModule#a_method

上面的例子包含了三种引入 module 的方法:

  • 实例对象(obj) extend MyModule,

  • 类E 的class << self 中 include MyModule

  • 类F extend MyModule.

从这个我们可以明白,Ruby可以为对象混入一个 module 来作为拓展,那么混入的代码被放置到哪里了呢?

先说结论,本质上混入 module 都是将 Module 中的方法引入到对象(obj, 类E, 类F)的singleton_class(单件类)中.

在讲单件类之前,先来看看单件方法.

Singleton Method (单件方法)

在 Ruby 里

Ruby允许给单个对象增加方法,这种只针对单个对象生效的方法,称为单件方法。例如:

str = "I am a string"
def str.title?
  self.upcase == self
end
str.title? # => false
str.methods.grep(/title?/) # => [:title?]
str.singleton_methods  #=> [:title?]
str.class # => String
String.new.title? #=> NoMethodError

## 除此之外,还可以用 define_singleton_method 来定义单例方法
guy = "Bob"
guy.define_singleton_method(:hello) { "#{self}: Hello there!" }
guy.hello    #=>  "Bob: Hello there!"

Ruby中类也是对象,而类名只是常量,所以在类上调用方法其实跟在对象上调用方法一样:

类方法的实质是:它是一个类的单件方法,实际上如果比较单件方法的定义和类方法的定义,会发现其实二者是一样的.

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

def MyClass.another_class_method
end

所以,单件方法可以认为是添加在对象的某个空间内只针对该对象有效的方法.

如果对象是个实例对象,添加的方法就是他的单件方法,

如果对象是个类,添加的方法就是这个类的类方法.

 

singleton_class (单件类)

我们知道Ruby中对象的方法的查找顺序是:先向右,再向上,其含义就是先向右找到对象的类,先在类的实例方法中尝试查找,如果没有找到,再继续顺着祖先链找.

前面介绍的单件方法是指那些只针对某个对象有效的方法,那么如果为一个对象定义了单件方法,那么这个单件方法的查找顺序又应该是怎样的?

class MyClass
  def my_method
  end
end
  
obj = MyClass.new
  
def obj.my_singleton_method
end

首先,单件方法不会在obj中,因为obj不是一个类,其次它也不在MyClass中,那样的话所有的MyClass实例都应该能共享调用这个方法,也就构不成单件类了.同理,单件方法也不能在祖先链的某个位置(类似superclass: Object)中.

正确的位置是在单件类中,这个类其实就是我们在irb中向对象询问它的类时(obj.class)得到的那个类,不同的是这类与普通的类还是有稍稍不同的.也可以称其为元类或本征类.

前提及到的实例对象的单件方法和类的类方法在创建上是类似的,所以,通过的关键词class配合特殊的语法应该可以将其取到.class << obj.

 


这是实例方法的查找规则

这是单件方法的查找规则

class C  
  def a_method  
    puts "C#a_method"  
  end  
end  
  
class D < C  
    
end  
  
d = D.new  
d.a_method  

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

打开单件类

Ruby 中 class 作用是把代码的上下文变换到这个类中,也就是『打开类』,同一个类可以在任何地方被打开,也因此别人的类可以被自己随意打开并改写.

Ruby提供了两种方法获取单件类的引用,一种是通过传统的关键词class配合特殊的语法。

通过关键字 class << obj 打开实例对象的单件类空间,为对象添加一系列的单件方法.单件类的本质还是一个类,是一个拥有唯一实例的类,所以叫单件类,或者单例类.

class << an_object
  # 自己的代码,定义对象的一系列单件方法
end
  
obj = Object.new
singleton_class = class << obj
  self
end
singleton_class.class # => Class

 

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

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

单件类的特性

1. 每个单件类只有一个实例(被称为单件类的原因),而且不能被继承.

2. 单件类是一个对象的单件方法的存活所在.

3. 引入单件类后的方法查找

基于上面对单件类的基本认识,引入单件类后,Ruby的方法查找方式就不应该是先从其类(普通类)开始,而是应该先从对象的单件类中开始查找,如果在单件类中没有找到想要的方法,它才会开始沿着类(普通类)开始,再到祖先链上去找.这样从单件类之后开始,一切又回到了我们在没有引入单件类时候的次序.

类方法,实例方法,单例方法

类方法只有类本身可以调用,在ruby中,类方法是一种特殊的单例方法.之前的例子中可以得到这样的结论,singleton_class也是一种类,在ruby中所有的类又都是对象.对象都有对应的singleton_class.

Ruby 中方法的查找规则
注释: 以#号标示的是singleton_class, c标示 (eigen)class, s表示 superclass

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  ## #C
# #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

Ruby 中方法的查找 遵循的规则是 'one step to the right, then up',即 向右一步然后向上.

本文转载自:https://segmentfault.com/a/1190000009740456

穿山
粉丝 9
博文 33
码字总数 18655
作品 0
南京
私信 提问
Ruby中的继承、原型、面向对象、访问域

先有类还是先有对象 从鸡蛋悖论解决可以悟到一个道理,不要从常识上假设非此即彼和绝对静止。 Ruby中的类和对象正是这么个东西 我们创建一个类,那它就是Class这个对象的实例,而Class,于是...

可数局部基
02/24
8
0
redis 一二事 - 搭建集群缓存服务器

在如今并发的环境下,对大数据量的查询采用缓存是最好不过的了,本文使用redis搭建集群 (个人喜欢redis,对memcache不感冒) redis是3.0后增加的集群功能,非常强大 集群中应该至少有三个节...

风间影月
2016/04/28
0
0
「Ruby」命名之争 :singleton_class? meta_class? eigen_class?

class << self; self; end 这段代码,每个Rubyist应该都很熟悉。 但是这里面的self是什么? singletonclass? metaclass? eigen_class? 在早期,确切来说应该是Ruby1.9.2发布之前,Ruby社区关...

blackanger
2018/06/28
0
0
也许有了这5个特性, Java能重归巅峰?

  【IT168 资讯】Yegor Bugayenko称,他偶然受到了Brian Goetz关于Java中数据类提议的启发,立即意识到自己也有一些关于如何使Java成为更好语言的想法。其中,以下这五个最重要的。   全...

it168网站
2017/11/09
0
0
Go 单例模式[个人翻译]

  原文地址:http://marcio.io/2015/07/singleton-pattern-in-go/     最近几年go语言的增长速度非常惊人,吸引着各界人士切换到Go语言。最近有很多关于使用Ruby语言的公司切换到Go、体...

lpxxn
2017/10/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Activity启动模式二

上篇文章Activity启动模式一主要介绍了Activity的四种启动模式,这些启动模式都是在AndroidManifest中进行配置的。除此之外,Android系统还通过Intent类提供了一些标志位,同样可以指定Activ...

ltlovezh
17分钟前
4
0
三原色还原

1、Color Filter Array — CFA 随着数码相机、手机的普及,CCD/CMOS 图像传感器近年来得到广泛的关注和应用。 图像传感器一般都采用一定的模式来采集图像数据,常用的有 BGR 模式和 CFA 模式...

天王盖地虎626
28分钟前
3
0
kubernetes pod exec接口调用

正文 一般生产环境上由于网络安全策略,大多数端口是不能为集群外部访问的。多个集群之间一般都是通过k8s的ApiServer组件提供的接口通信,如https://192.168.1.101:6443。所以在做云平台时,...

码农实战
今天
8
0
3_数组

3_数组

行者终成事
今天
8
0
经典系统设计面试题解析:如何设计TinyURL(二)

原文链接:https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部