从自己实现Ruby单例模式揭秘Ruby模块内幕

缘起

整个故事要从某一天开始说起,那天,我看到一篇文章介绍了Ruby中实现单例模式的模块。Singleton模块,只要include  Singleton模块,就可以为一个Ruby类添加单例功能。

我颇为惊奇,于是查看Singleton.rb源码,洋洋洒洒几百行,看不懂。做技术的都知道,遇到不理解的东西我们总是想探究清楚,否则必茶不思饭不想,夜不能寐。

于是,我就想到CSDN论坛问问Ruby高人,不料Ruby论坛总是系统维护中这个状态,想到其他Ruby社区问问,但一想还是算了。我平常都不逛其他社区的。

于是,继续钻研,费了一晚上的时间,终有所获。虽然没有完全看懂Ruby核心库中的Singleton模块的源码,但自己也鼓捣出了一个Singleton模块出来。

并且对Ruby模块和Ruby语言的本质有了更深的认识。

使用模块类载入方法实现的单例模块!

源代码:

下面请大家直接看看我的简单版的Singleton模块的源代码。

单例模块,存在的意义就是在模块被载入类时,给类添加类方法和创建对象实例等

module   Singleton

这个方法在模块被载入类时调用,传入的参数是  类对象

def self.included(base)

这个操作把另一个模块直接添加到Class对象上。类对象上的方法就是类方法。

尽管模块中的方法是模块的实例方法。

base.extend(SingletonClass);

puts("Singleton  Include!");

创建类的实例,然后把类的new方法设为私有类方法,防止被调用

最后把类的实例存放到类的方法中。

myInstance=base.new();

base.private_class_method :new

# private_class_method :new

base.setClass(myInstance);

#createSingleton(base);

end

end

module  SingletonClass

这个模块附加上去的类方法,把类实例保存在模块的实例变量中。也就是目标类的类变量中。

def setClass(base)

puts("setClass");

@myClass=base;

return  @myClass;

end

单例方法,仅仅返回类的保护实例变量。

def instance()

puts("instance");

return  @myClass;

end

end

class Test

include Singleton;

#private_class_method :new

def say(val)

puts(val);

end

end

test=Test.instance();

test.say("sssssss");

#test=Test.new();  # fail

技术详解

上面展示的Singleton模块的源代码,虽然代码量不多,却使用了很多Ruby的高级技术。很有教学意义。当然,如果直接拿去使用也没有问题,虽然没有官方的Singleton模块功能强,却一样实现了单例模式。

通过模块怎样给类添加类方法

Singleton模块的功能是,目标类通过

include Singleton;

这一行代码,可以给自己的类添加一个类方法instance().

我们一般使用Module,是给Module定义一些实例方法,然后这些方法就会成为目标类的实例方法。

我们也可以给Module定义类方法。这些方法必须要通过

Module.方法名()调用。

试验下来,我们发现,目标类上不能调用模块的方法。

那怎么才能实现通过Module给目标类添加类方法呢?

我们知道,Ruby中类是类,但其实类也是一个对象,一个特殊的对象。

Ruby的类对象就是Class类的实例。因此,对象具有的所有特征,类对象也都有。

Java,JS,C#都是如此的。

Ruby的类对象上的实例方法,不就是类的方法吗?

鉴于此,我们想到,只要在类对象上添加实例方法,那么我们就可以给类添加类方法。

而Ruby和JavaScript一样,都可以给对象而不是类型动态添加方法。这样就成了。

Ruby的类继承体系

ObjectClass

……

BaseClass

^

|

(SingletonClass)

^

|     各个include的Module

SomeClass

^

|

(SingletonClass)

^

|

someObj

上面就是Ruby的继承体系,独特而又精妙。

Ruby可以实现动态增删对象的成员,而不仅仅是动态增删类的成员。

另一种具备此种神功的就是JavaScript。但是,Ruby内部实现这些强大功能的内部机制和JavaScript的机制是截然不同的。

可谓殊途同归!

1,Ruby是单根结构的单继承体系的语言,它的根基类是Object类。所有的类最后都继承Object类,这和其他语言一样。Java,C#,JavaScript,SmallTalk等都是如此。

2,Ruby的类可以包括多个模块。通过

include ModuleName

即可达到此种目的,这种技术叫做混入Mixin。

通过这种技术,使单继承的Ruby达到了多继承的效果。

JavaScript也可以实现混入Mixin技术。这个我以后有时间再说。

在方法查找时,是先查找类本身定义的方法,然后再查找引入的模块中的方法。最后查找基类的方法。

3,Ruby不仅可以使用反射技术实现动态增删类型的成员,还可以动态增删对象的成员。

也就是说,Ruby类的各个实例,可以在运行时具备完全不同的数据结构和方法、行为特征!

这种强大的特性,JavaScript也具备。

现在让我们看看Ruby是怎样做到这一点的。

Ruby的每一个类实例,都有一个单独的特殊的类型,SingletonClass。

(SingletonClass)

^

|

someObj

注意,SingletonClass不是Singleton模块,不是用于实现单例模式的那个冬冬。

Ruby的一个特殊特征就是,一切都是类。JavaScript的哲学是一切都是对象。

既然任何对象都从属于一个唯一的类型,那么我们只要使用动态修改Ruby类的成员的技术修改这个SingletonClass的成员,就可以实现动态修改单个对象的成员了。

4,回到上面的Singleton模块的例子,为什么我们要讲Ruby动态修改单个对象的方法呢?因为我们要使用这种技术实现给Ruby的类增加类方法instance()。

再看上面的Ruby的继承图,我们看到Ruby的类,本身也是Class类型的一个实例。因此,任何类也都是有SingletonClass的。

因此,只要我们给类的SingletonClass增加实例方法instance,那么这个类就有了实例方法instance。这样,我们的类就有了类方法instance。

上面,我们正是利用了Ruby的这一特性,通过模块给目标类动态添加了类成员函数instance()。

5,模块的included方法。

module   Singleton

这个方法在模块被载入类时调用,传入的参数是  类对象

def self.included(base)

模块的included方法,会在模块被类include时执行,其参数就是目标类对象。这个方法就是我们通过模块类操作目标类的入口点。

1)给类的SingletonClass添加模块,从而给目标类添加instance()方法。

尽管模块中的方法是模块的实例方法。

base.extend(SingletonClass);

puts("Singleton  Include!");

2)创建类的实例,然后把类的new方法设为私有类方法,防止被调用

最后把类的实例存放到类的方法中。

myInstance=base.new();

base.private_class_method :new

现在,没有人能够再创建目标类的实例了!

3)保存我们的单例。

base.setClass(myInstance);

base.private_class_method : setClass

4)看看我们给目标类添加静态方法的模块。

module  SingletonClass

这个模块附加上去的类方法,把类实例保存在模块的实例变量中。也就是目标类的类变量中。

def setClass(base)

puts("setClass");

@myClass=base;

return  @myClass;

end

单例方法,仅仅返回类的保护实例变量。

def instance()

puts("instance");

return  @myClass;

end

OK!大功告成了!

下回也许我会再写一篇文章,比较一下同样NB的JavaScript和Ruby的优劣。

从自己实现Ruby单例模式揭秘Ruby模块内幕相关推荐

  1. centos下安装ruby,删除ruby

    简言 由于centos7下面,直接使用yum install ruby,安装的是2.0.0版本,太低了,没法部署redis集群,redis集群需要2.3版本,所以我们需要安装更高的版本 注意 在新版r ...

  2. ruby array_在Ruby中使用Array.pop和Array.shift方法从Array中删除元素

    ruby array Ruby Array.pop和Array.shift方法 (Ruby Array.pop and Array.shift methods) If you are reading ...

  3. ruby array_在Ruby中使用Array.delete()和Array.delete_at()从Array中移除元素

    ruby array Ruby Array.delete()和Array.delete_at()方法 (Ruby Array.delete() and Array.delete_at() method ...

  4. Ruby编程语言(The Ruby Programming Language) 中文版

    Ruby编程语言(The Ruby Programming Language) 中文版 转载于:https://www.cnblogs.com/gavinhughhu/archive/2009/11/ ...

  5. java支付宝支付_Java 高并发环境下的性能优化,揭秘支付宝技术内幕

    前言 高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等. 为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适 ...

  6. 设计一款编程语言有多难?Ruby 创始人揭秘

    作者 | DAVID CASSEL 译者 | 弯月,责编 | 郑丽媛 头图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 以下为译文: 最近,Ruby编程语言的创始人兼主 ...

  7. linux ruby作用域,细说Ruby细化

    0. 什么是细化? 细化是Ruby2.0增添的体验版特性,但如今它已经算是标准库的一部分了.它主要用于修改指定类的对应方法,并且对目标方法的修改只在指定的区域内生效,而不会对全局产生副作用. Refi ...

  8. linux redis ruby,redis requires ruby version 2.2.2的解决方案

    做Redis的Cluster集群的时候,在执行gem install redis时,提示如下错误: gem install redis ERROR: Error installing redis: r ...

  9. [Ruby]转载: 关于ruby中 %Q, %q, %W, %w, %x, %r, %s 的用法

    单引号内的内容,ruby会原样输出 双引号内的内容,ruby会解析 我们看个简单的例子,针对字符串      #{foo}test     我们分别用单引号核双引号操作 '#{foo}test' =& ...

最新文章

  1. 你在打王者农药,有人却用iPhone来训练神经网络
  2. 项目松弛时期 团队如何休养生息?
  3. [转帖]IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?...
  4. 打印机服务器不支持1020,win10系统电脑安装hp1020打印机后无法使用的详细技巧
  5. iOS核心动画之CALayer-自定义层
  6. BestCoder Round #86 1002 HDU 5805 ——NanoApe Loves Sequence
  7. Auto Layout 和 Constraints
  8. struts html:select 默认选中,Struts2自动选择下拉框的值
  9. vue2项目中全局引入scss变量
  10. 计算机三级信息安全技术
  11. 终于搞清楚了:java的long的小l和大L区别
  12. oracle数据错误循环冗余检查,Oracle数据恢复:文件 数据错误(循环冗余检查) 解决...
  13. 实验6 熟悉Hive的基本操作
  14. oracle11g磁盘阵列 pdf,融会贯通 从Oracle11g到SQL Server2008 中文PDF版 16.7MB
  15. C语言入门——初识C语言
  16. iterator的用法 | reverse_iterator的封装实现
  17. electron打包的一些问题
  18. Proxy与Reflect详解
  19. 什么是VLAN?为什么要划分VLAN?
  20. 百度浏览器Android6.0,有些安卓机型百度浏览器h5唤起app失效

热门文章

  1. ATS (apache traffic server) http_ui 设置与使用
  2. vc++ 关于 指针操作
  3. pku 2418 Hardwood Species 字典树
  4. linux shell date 1,linux shell date命令的坑(date 1 month ago 的诡异现象)
  5. mysql order by 运算_在MySQL中具有ORDER BY的多个LIKE运算符?
  6. 程序员基本功10栈和队列
  7. nat,acl及单臂路由实验(ensp)
  8. android调用网页方法,Android调用手机浏览器的正确方式
  9. android自定义task,Android Gradle 自定义Task 详解
  10. java exec mvn_maven---常用插件之EXEC