浅析Ruby中继承和消息的相关知识

继承允许你创建一个类,作为另一个类的精炼(refinement)和特化(specialization)。例如,在我们的自动点唱机系统中,有“歌曲”这一概念,被封装在Song类中,然后,随着市场的成长,我们需要提供卡拉OK的支持。一首卡拉OK歌曲和其他歌曲没什么两样(它只是没有主唱的音轨,对此我们不必关心)。不过,它还包括对于的一套歌词以及时间信息。当我们的自动点唱机在播放一首卡拉OK歌曲时,歌词应该随音乐滚动显示在点唱机前的屏幕上。

解决这个问题的一种方法是定义一个新的类KaraokeSong,就是Song加上歌词。

class KaraokeSong <Song

  def initialize(name,artist,duration,lyrics)

    super(name,artist,duration)

    @lyrics = lyrics

  end

end

类定义一行中的“< Song”告诉Ruby, KaraokeSong是Song 的子类(subclass).因此,这也意味着Song是KaraokeSong的超类(superclass)

song = KaraokeSong.new("My Way","Sinatra",255,"And now,the ...")

song.to_s        ->          *Song:My Way--Sinatra(225)*

调用to_s方法没有显示歌词

这和我们在向一个对象发送消息时,Ruby判定调用哪个方法的机制有关。在程序代码的初始解析(parse)期间,当Ruby遇到方法调用song.to_s时,它并不知道从何处找到to_s方法,而是将判定推迟直至程序开始运行时再运行。在那时,Ruby查看song所属的类。如果该类实现了和消息名称相同的方法,就运行这个方法。否则,Ruby就查看其父类中的方法,然后是祖父类,凡此以往追溯整个祖先链。如果最终它在祖先类中没有找到合适的方法,Ruby会产生一种特殊的行为,通常是导致引发一个错误。

让我们通过实现KaraokeSong#to_s来解决这个问题,你有许多方法可以完成它。让我们从最槽糕的方法开始,我们将to_s方法从Song类中拷贝出来并添加lyrics信息。

class KaraokeSong

 #...

 def to_s

   "KS: #@name--#@artist(#@duration){#@lyrics}"

  end

end

song = KaraokeSong.new("My Way", "Sinatra", 225,"And now,the...")

song.to_s     ->"KS: My Way--Sinatra(225){And now,the...}"

我们正确地显示了实例变量@lyrics的值。但使用这种方法,子类需要直接访问其祖先的实例变量。那么为什么这是实现to_s的一种糟糕方式呢?

答案与良好的编程风格有关(有时被称为解耦)。直接戳进父类的内部结构,并且显示地检验它的实例变量,会使得我们和父类的实现紧密地绑在一起。

我们通过让每个类处理其自身实现细节的方法来解决这个问题。当调用KaraokeSong#to_s时,我们调用其父类的to_s方法来得到歌曲的细节。然后,将歌词信息添加上去,并返回结果。这里使用的技巧是Ruby的关键字super。当你调用super而不使用参数时,Ruby向当前对象的父类发送一个消息,要求它调用子类中的同名方法。Ruby将我们原先调用方法时的参数传递给父类的方法。现在,我们可以实现改进后新的to_s方法。

class KaraokeSong <Song

  #Format ourselves as a string by appending

  #our lyrics to our parent's to_s value.

  def to_s

    super+"{#@lyrics}"

  end

end

song = KaraokeSong.new("My Way", "Sinatra" ,225, "And now,the...")

song.to_s      ->"Song:My Way--Sinatra(225){And now,the...}"

我们明确地告诉Ruby,KaraokeSong是Song的子类,但是我们并没有指定Song类本身的父类是什么。如果你在定义一个类时没有指定其父类,Ruby默认以Object类作为其父类。这意味着所有类的始祖都是Object,并且Object的实例方法对Ruby的所有对象都可用。

(0)

相关推荐

  • Ruby类继承、抽象类、类拓展混入、代理类实例

    总结一下工作中遇到的类扩展: 1.类继承: 当多个类公用很多方法的时候可以将公用方法部分抽取出来,需要的类做相关继承. 例子: 复制代码 代码如下: class A < ActiveRecord::Base     def a         p "it was a "     end end class B<A end class C<A end B.new.a #=>"it was a " C.new.a #=>"it w

  • Ruby最简单的消息服务器代码

    ser.rb 复制代码 代码如下: require 'socket's = TCPServer.new 3333conn = s.acceptloop do    puts conn.getsend clt.rb 复制代码 代码如下: require 'socket's = TCPSocket.new 'localhost',3333loop do    sms=gets.chomp    s.puts smsend 作者:Hevienz

  • 浅析Ruby中继承和消息的相关知识

    继承允许你创建一个类,作为另一个类的精炼(refinement)和特化(specialization).例如,在我们的自动点唱机系统中,有"歌曲"这一概念,被封装在Song类中,然后,随着市场的成长,我们需要提供卡拉OK的支持.一首卡拉OK歌曲和其他歌曲没什么两样(它只是没有主唱的音轨,对此我们不必关心).不过,它还包括对于的一套歌词以及时间信息.当我们的自动点唱机在播放一首卡拉OK歌曲时,歌词应该随音乐滚动显示在点唱机前的屏幕上. 解决这个问题的一种方法是定义一个新的类Karaoke

  • Python函数中的不定长参数相关知识总结

    一. 不定长位置参数 # 在定义函数参数时,可以在形参的前面加*,该形参将获取所有的位置实参 # 它会将所有的实参保存在一个元组中 def fn(*args): print("args=", args) print("args type:", type(args)) # 带*形参和其他参数配合使用 def fn1(a, b, *args): print(a) print(b) print(args) # 下面这两种写法可以,但是在传实参的时候要注意 def fn2(

  • 浅析Ruby中的类对象的概念

    面向对象的程序涉及类和对象. 一个类是蓝本,从个别对象被创建.在面向对象的术语,我们说小明的自行车是被称为自行车类的对象实例. 任何车辆的例子.它包括轮子,马力,燃油或燃气罐容量.这些特点形成的类车辆的数据成员.可以从其他车辆区分这些特征. 车辆也有一定的功能,如停止,驾驶,超速驾驶.即使这些功能形成的类车辆的数据成员.因此,可以定义一个类作为一个组合的特点和功能. 车辆类可以被定义为: Class Vehicle { Number no_of_wheels Number horsepower

  • Ruby中关于模块的一些基础知识

    模块与类很相似,它也可以说成是"不能被实例化的类".由于Class类是Module类的子类,所以说成是"类=模块+实例化能力"也许更好. 所谓模块,究竟是用来干什么的呢?其作用主要有两大类:Mix-in和命名空间. Mix-in实际上是受限制的多重继承.利用实际做成的继承关系,可以实现对某些类进行一些"点缀"的目的.事实上,Mix-in这种说法就来自于在冰激凌上面的饼干或者坚果. 在面向对象设计的历史中,由多重继承机制造成的问题早已众所周知了.所

  • 浅析Ruby中的Profiling工具的用法

    内置的profiler实现的很简单,在ruby2.2中只有150行代码,大家可以看看它的实现profile.rb .内置的profiler使用起来非常的方便,只需要加上-rprofile参数即可.例如: 执行: ruby -rprofile test.rb 输出结果为: 通过打印出的结果能够很明显的看出耗时的方法.内置的profiler很简单,只能打印出这样的结果,没有 其他输出格式的选项,下面介绍的其他几种都有丰富的格式输出. ruby-prof repo: https://github.co

  • 浅析Ruby中的正则表达式的使用

    如果只是需要中查找字符串的 text, 不要使用正则表达式:string['text'] 针对简单的结构, 你可以直接使用string[/RE/]的方式来查询. match = string[/regexp/] # get content of matched regexp first_group = string[/text(grp)/, 1] # get content of captured group string[/text (grp)/, 1] = 'replace' # strin

  • 浅析Ruby中的DATA对象

    这段代码能运行吗? 这个DATA是什么东西? require 'erb' data = DATA.read max = 15_000 title = "hello world!" content = "hello world!\n" * 10 max.times{ ERB.new(data).result(binding) } __END__ <html> <head> <%= title %> </head> <

  • SQLite3中自增主键相关知识总结

    一.SQLite清空表并将自增列归零 SQL标准中有TRUNCATE TABLE语句,用来清空表的所有内容.但SQLite不支持这个语句.在SQLite中直接使用 DELETE FROM TableName 就可以了.对于大多数DBMS来说,用DELETE不如用TRUNCATE 速度快,因为TRUNCATE 不用访问整个表,不用记录数据的变动. SQLite虽然不支持TRUNCATE,但它对DELETE做了优化:通常在清空表的时候,还需要把自增列归零.在SQLite中定义自增列的方法如下: 复制

  • 在Python中marshal对象序列化的相关知识

    有时候,要把内存中的一个对象持久化保存到磁盘上,或者序列化成二进制流通过网络发送到远程主机上.Python中有很多模块提供了序列化与反序列化的功能,如:marshal, pickle, cPickle等等.今天就讲讲marshal模块. 注意: marshal并不是一个通用的模块,在某些时候它是一个不被推荐使用的模块,因为使用marshal序列化的二进制数据格式还没有文档化,在不同版本的Python中,marshal的实现可能不一样.也就是说,用python2.5序列为一个对象,用python2

  • 举例讲解JavaScript中关于对象操作的相关知识

    从数组到对象 var myarr = ['red','blue','yellow','purple']; myarr;// ["red","blue","yellow","purple"] myarr[0];//"red" myarr[3];//"purple' 数组大家都很熟悉吧,我们可以理解为一个Key对应一个Value,而这个Key在数组中,已经默认了(如上述代码,它的key分别是0,1,2

随机推荐