Ruby on Rails 继承(Inheritance)与开放类别

到目前为止的范例都是只有单一类别,但在真实的世界里其实是更复杂的,像是如果想要再加入一个小狗类别:

class Cat
  def eat(food)
    puts "#{food} 好好吃!!"
  end
end

class Dog
  def eat(food)
    puts "#{food} 好好吃!!"
  end
end

在这个例子,不管是 Cat 或 Dog 类别都有定义了 eat 方法,在物件导向的概念里,如果这些类别的用途是同一型的(例如 Cat 跟 Dog 都是动物),通常会把相同功能的方法移到「上一层」的类别里,然後再去继承它:

class Animal
  def eat(food)
    puts "#{food} 好好吃!!"
  end
end

class Cat < Animal
end

class Dog < Animal
end

在这里,我定义了一个 Animal 类别,然後让 Cat 跟 Dog 都去继承它,那个小於符号 < 就是「继承」的意思。这样一来,就算 Cat 跟 Dog 类别空空的什麽都没写,也一样有 eat 方法可以用。虽然 Cat 跟 Dog 本身是两个不同的类别,但我们可以说「Cat 是一种 Animal,Dog 也是一种 Animal」,利用这样的设计,可以把程序码整理得更漂亮,不会写出一堆重复的程序码。

开放类别(Open Class)

大家请先看一下这段程序码:

class Cat
  def abc
    # ...
  end
end

class Cat
  def xyz
    # ...
  end
end

kitty = Cat.new
kitty.abc       # => 会发生什麽事?
kitty.xyz       # => 会发生什麽事?

一个不小心,在这里定义了两个名字都叫做 Cat 的类别,你可能会猜,後面写的类别应该会盖掉前面先写的类别,所以 kitty.xyz 可正常运作,但 kitty.abc 会出错。

但在 Ruby 里,如果遇到两个一样名字的类别,其实并不会「覆盖」,而是会进行「融合」,上面这两个类别最後会变成这样:

class Cat
  def abc
    # ...
  end

  def xyz
    # ...
  end
end

然後 abc 跟 xyz 两个方法都可以正常执行。利用这个特性,可以做出有趣的效果:

class String
  def say_hello
    "hi, I am #{self}"
  end
end

puts "eddie".say_hello  # => hi, I am eddie
puts "kitty".say_hello  # => hi, I am kitty

在这里,我先定义一个 String 类别,并且在里面定义了 say_hello 方法,根据前面的规则,遇到「同名」的类别的话,方法会互相「融合」,所以在那之後所有的字串就都有 say_hello 方法可以用了。等等,那个 String 类别不是内建的类别吗?是的,你没看错,在 Ruby 即使是内建的类别,也是可以帮它「加料」的,这个技巧称之开放类别(Open Class)。

这是我个人很喜欢的功能,有些人可能会认为这样的设计感觉很随便,竟然连内建的类别都可以任意修改,但我想大家都是大人了,应该不会没事乱 open 然後去恶搞自己或自己的同事吧。事实上,Rails 本身也正是利用这个特性,让程序码的可读性变得更好,例如:

puts 3.days.ago    # => Wed, 21 Dec 2016 12:06:13 UTC +00:00
puts 10.megabyte   # => 10485760
这样不是很酷吗 :)

一加一不等於二?
还记得在前面有提到,1 + 2 其实是「1 这个数字物件,呼叫了 + 方法,并且把 2 这个数字物件当做参数传给它」,事实上,open class 不仅能帮原来的类别加功能,连原来即有的行为都可以改掉:

class Integer
  def +(n)
    1000
  end
end

puts 1 + 2   #=> 得到 1000
puts 3 + 4   #=> 得到 1000

我在 Integer 类别里重新定义了 + 方法,让它总是回传数字 1000,这样一来,不管是 1 + 2 或是 3 + 4,答案都会是 1000。

这样其实没什麽用,不过可以再做一点事情,让原本的 + 还是能算出它原本的答案,但又还可以做些额外的事:

class Integer
  alias :original_plus :+

  def +(n)
    puts "hey hey hey"
    original_plus(n)
  end
end

puts 1 + 2
puts 3 + 4
在这里我使用了 Ruby 内建的 alias 方法把原本的 + 方法加个别名 original_plus,然後再新定义的 + 方法里,再呼叫它原本的算法。执行之後就会发现计算结果跟原本的 + 是一样的,但会偷偷多印了 hey hey hey 字样在画面上。至於这个 hey hey hey 可以做什麽,就看大家的想像力了。

Ruby 是个设计上很特别也很弹性的程序语言,不仅很多东西都不是它看起来的样子(例如 + 号竟然只是个方法),连内建的方法如果不喜欢都可以直接改掉(例如把 + 方法的结果改掉)。
[为你自己学Ruby on Rails]https://railsbook.tw/chapters/08-ruby-basic-4.html


<<:  [烧烤吃到饱-3] 猪对有韩式烤肉吃到饱-台中精武店 #中秋节烤肉精选店家

>>:  Day 3 - HTAP

[DAY 16] _Si7020温湿度读写

今天来说说温湿度读取的部分吧,首先来看看这颗的Datasheet: https://www.sila...

有关多台网页伺器, 但对外IP才几个

各位大大 因企业运维来了一位大哥建议公司把各种网站服务放在VM上,VM可以切很多台服务器出来,这样子...

Day 27 - 客制化 ListRowPresenter 来实作 Loop Banner 效果 Part3

今天我们要来完成 Banner 的效果啦!! 修改 CustomListRowPresenter 我...

Day_16 : 让 Vite 来开启你的Vue 之 资料定义 ref 与 reactive

Hi Dai Gei Ho~ 我是Winnie~ 在今天文章中,我们要来说的是Compostion ...

Flutter体验 Day 19-InheritedWidget

功能组件-InheritedWidget InheritedWidget是一个具有特殊功能的组件,它...