Ruby 面向目标

Ruby 是纯面向目标的言语,Ruby 中的一切都是以目标的办法呈现。Ruby 中的每个值都是一个目标,即使是最原始的东西:字符串、数字,甚至连 true 和 false 都是目标。类自身也是一个目标,是 Class 类的一个实例。本章将向您解说一切与 Ruby 面向目标相关的主要功用。

类用于指定目标的办法,它结合了数据表明法和办法,把数据整理成一个规整的包。类中的数据和办法被称为类的成员。

Ruby 类界说

当您界说一个类时,您实践是界说了一个数据类型的蓝图。这实践上并没有界说任何的数据,而是界说了类的称号意味着什么,也便是说,界说了类的目标将由什么组成,以及在该目标上能履行什么操作。

类界说以关键字 class 开端,后跟类称号,最终以一个 end 进行分隔表明停止该类界说。例如,咱们运用关键字 class 来界说 Box 类,如下所示:

class Box
   code
end

依照常规,称号有必要以大写字母最初,假如包括多个单词,每个单词首字母大写,但此间没有分隔符(例如:CamelCase)。

界说 Ruby 目标

类供给了目标的蓝图,所以基本上,目标是依据类进行创立的。咱们运用 new 关键字声明类的目标。下面的句子声明晰类 Box 的两个目标:

box1 = Box.new
box2 = Box.new

initialize 办法

initialize 办法是一个规范的 Ruby 类办法,是类的结构函数,与其他面向目标编程言语中的 constructor 作业原理相似。当您想要在创立目标的一起初始化一些类变量,initialize 办法就派上用场了。该办法带有一系列参数,与其他 Ruby 办法相同,运用该办法时,有必要在前面放置 def 关键字,如下所示:

class Box
   def initialize(w,h)
      @width, @height = w, h
   end
end

实例变量

实例变量是类特点,它们在运用类创立目标时就变成目标的特点。每个目标的特点是独自赋值的,和其他目标之间不同享值。在类的内部,是运用 @ 运算符拜访这些特点,在类的外部,则是运用称为拜访器办法公共办法进行拜访。下面咱们以上面界说的类 Box 为实例,把 @width 和 @height 作为类 Box 的实例变量。

class Box
   def initialize(w,h)
      # 给实例变量赋值
      @width, @height = w, h
   end
end

拜访器(accessor) & 设置器(setter)办法

为了在类的外部运用变量,咱们有必要在拜访器办法内部界说这些变量,accessor 其实相当于 getter。下面的实例演示了拜访器办法的用法:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构函数
   def initialize(w,h)
      @width, @height = w, h
   end

   # 拜访器办法
   def printWidth
      @width
   end

   def printHeight
      @height
   end
end

# 创立目标
box = Box.new(10, 20)

# 运用拜访器办法
x = box.printWidth()
y = box.printHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

当上面的代码履行时,它会发生以下成果:

Width of the box is : 10
Height of the box is : 20

与用于拜访变量值的拜访器办法相似,Ruby 供给了一种在类的外部设置变量值的办法,也便是所谓的设置器办法,界说如下:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end

   # 拜访器办法
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # 设置器办法
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# 创立目标
box = Box.new(10, 20)

# 运用设置器办法
box.setWidth = 30
box.setHeight = 50

# 运用拜访器办法
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

当上面的代码履行时,它会发生以下成果:

Width of the box is : 30
Height of the box is : 50

实例办法

实例办法的界说与其他办法的界说相同,都是运用 def 关键字,但它们只能经过类实例来运用,如下面实例所示。它们的功用不限于拜访实例变量,也能依照您的需求做更多其他的使命。

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构办法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 实例办法
   def getArea
      @width * @height
   end
end

# 创立目标
box = Box.new(10, 20)

# 调用实例办法
a = box.getArea()
puts "Area of the box is : #{a}"

当上面的代码履行时,它会发生以下成果:

Area of the box is : 200

类办法 & 类变量

类变量是在类的一切实例中同享的变量。换句话说,类变量的实例能够被一切的目标实例拜访。类变量以两个 @ 字符(@@)作为前缀,类变量有必要在类界说中被初始化,如下面实例所示。

类办法运用 def self.methodname() 界说,类办法以 end 分隔符完毕。类办法可运用带有类称号的 classname.methodname 办法调用,如下面实例所示:

#!/usr/bin/ruby -w

class Box
   # 初始化类变量
   @@count = 0
   def initialize(w,h)
      # 给实例变量赋值
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# 创立两个目标
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# 调用类办法来输出盒子计数
Box.printCount()

当上面的代码履行时,它会发生以下成果:

Box count is : 2

to_s 办法

您界说的任何类都有一个 to_s 实例办法来回来目标的字符串表明办法。下面是一个简略的实例,依据 width 和 height 表明 Box 目标:

#!/usr/bin/ruby -w

class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 界说 to_s 办法
   def to_s
      "(w:#@width,h:#@height)"  # 目标的字符串格局
   end
end

# 创立目标
box = Box.new(10, 20)

# 主动调用 to_s 办法
puts "String representation of box is : #{box}"

当上面的代码履行时,它会发生以下成果:

String representation of box is : (w:10,h:20)

拜访操控

Ruby 为您供给了三个等级的实例办法保护,分别是 public、private 或 protected。Ruby 不在实例和类变量上运用任何拜访操控。

  • Public 办法: Public 办法可被恣意目标调用。默许状况下,办法都是 public 的,除了 initialize 办法总是 private 的。
  • Private 办法: Private 办法不能从类外部拜访或查看。只需类办法能够拜访私有成员。
  • Protected 办法: Protected 办法只能被类及其子类的目标调用。拜访也只能在类及其子类内部进行。

下面是一个简略的实例,演示了这三种修饰符的语法:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end

   # 实例办法默许是 public 的
   def getArea
      getWidth() * getHeight
   end

   # 界说 private 的拜访器办法
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # make them private
   private :getWidth, :getHeight

   # 用于输出面积的实例办法
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # 让实例办法是 protected 的
   protected :printArea
end

# 创立目标
box = Box.new(10, 20)

# 调用实例办法
a = box.getArea()
puts "Area of the box is : #{a}"

# 测验调用 protected 的实例办法
box.printArea()

当上面的代码履行时,它会发生以下成果。在这里,榜首种办法调用成功,可是第二办法会发生一个问题。

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

类的承继

承继,是面向目标编程中最重要的概念之一。承继答应咱们依据另一个类界说一个类,这样使得创立和保护运用程序变得愈加简略。

承继有助于重用代码和快速履行,不幸的是,Ruby 不支撑多承继,可是 Ruby 支撑 mixins。mixin 就像是多承继的一个特定完成,在多承继中,只需接口部分是可承继的。

当创立类时,程序员能够直接指定新类承继自某个已有类的成员,这样就不必从头编写新的数据成员和成员函数。这个已有类被称为基类或父类,新类被称为派生类或子类

Ruby 也供给了子类化的概念,子类化即承继,下面的实例解说了这个概念。扩展一个类的语法十分简略。只需增加一个 < 字符和父类的称号到类句子中即可。例如,下面界说了类 BigBoxBox 的子类:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 实例办法
   def getArea
      @width * @height
   end
end

# 界说子类
class BigBox < Box

   # 增加一个新的实例办法
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# 创立目标
box = BigBox.new(10, 20)

# 输出面积
box.printArea()

当上面的代码履行时,它会发生以下成果:

Big box area is : 200

办法重载

尽管您能够在派生类中增加新的功用,但有时您或许想要改动现已在父类中界说的办法的行为。这时您能够坚持办法称号不变,重载办法的功用即可,如下面实例所示:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 实例办法
   def getArea
      @width * @height
   end
end

# 界说子类
class BigBox < Box

   # 改动已有的 getArea 办法
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# 创立目标
box = BigBox.new(10, 20)

# 运用重载的办法输出面积
box.getArea()

以上实例运转输出成果为:

Big box area is : 200

运算符重载

咱们期望运用 + 运算符履行两个 Box 目标的向量加法,运用 * 运算符来把 Box 的 width 和 height 相乘,运用一元运算符 - 对 Box 的 width 和 height 求反。下面是一个带有数学运算符界说的 Box 类版别:

class Box
  def initialize(w,h) # 初始化 width 和 height
    @width,@height = w, h
  end

  def +(other)         # 界说 + 来履行向量加法
    Box.new(@width + other.width, @height + other.height)
  end

  def -@               # 界说一元运算符 - 来对 width 和 height 求反
    Box.new(-@width, -@height)
  end

  def *(scalar)        # 履行标量乘法
    Box.new(@width*scalar, @height*scalar)
  end
end

冻住目标

有时候,咱们想要避免目标被改动。在 Object 中,freeze 办法可完成这点,它能有效地把一个目标变成一个常量。任何目标都能够经过调用 Object.freeze 进行冻住。冻住目标不能被修正,也便是说,您不能改动它的实例变量。

您能够运用 Object.frozen? 办法查看一个给定的目标是否现已被冻住。假如目标已被冻住,该办法将回来 true,不然回来一个 false 值。下面的实例解说了这个概念:

#!/usr/bin/ruby -w

# 界说类
class Box
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end

   # 拜访器办法
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # 设置器办法
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# 创立目标
box = Box.new(10, 20)

# 让咱们冻住该目标
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end

# 现在测验运用设置器办法
box.setWidth = 30
box.setHeight = 50

# 运用拜访器办法
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

当上面的代码履行时,它会发生以下成果:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
        from test.rb:39

类常量

您能够在类的内部界说一个常量,经过把一个直接的数值或字符串值赋给一个变量来界说的,常量的界说不需要运用 @ 或 @@。依照常规,常量的称号运用大写。

一旦常量被界说,您就不能改动它的值,您能够在类的内部直接拜访常量,就像是拜访变量相同,可是假如您想要在类的外部拜访常量,那么您有必要运用 classname::constant,如下面实例所示。

#!/usr/bin/ruby -w

# 界说类
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 实例办法
   def getArea
      @width * @height
   end
end

# 创立目标
box = Box.new(10, 20)

# 调用实例办法
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

当上面的代码履行时,它会发生以下成果:

Area of the box is : 200
TATA Inc
Box weight is: 10

类常量可被承继,也可像实例办法相同被重载。

运用 allocate 创立目标

或许有一种状况,您想要在不调用目标结构器 initialize 的状况下创立目标,即,运用 new 办法创立目标,在这种状况下,您能够调用 allocate 来创立一个未初始化的目标,如下面实例所示:

#!/usr/bin/ruby -w

# 界说类
class Box
   attr_accessor :width, :height

   # 结构器办法
   def initialize(w,h)
      @width, @height = w, h
   end

   # 实例办法
   def getArea
      @width * @height
   end
end

# 运用 new 创立目标
box1 = Box.new(10, 20)

# 运用 allocate 创立两一个目标
box2 = Box.allocate

# 运用 box1 调用实例办法
a = box1.getArea()
puts "Area of the box is : #{a}"

# 运用 box2 调用实例办法
a = box2.getArea()
puts "Area of the box is : #{a}"

当上面的代码履行时,它会发生以下成果:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

类信息

Ruby的 self 和 Java 的 this 有相似之处,但又大不相同。Java的办法都是在实例办法中引证,所以this一般都是指向当时目标的。而Ruby的代码逐行履行,所以在不同的上下文(context)self就有了不同的意义。让咱们来看看下面的实例:.

#!/usr/bin/ruby -w

class Box
   # 输出类信息
   puts "Class of self = #{self.class}"
   puts "Name of self = #{self.name}"
end

当上面的代码履行时,它会发生以下成果:

Class of self = Class
Name of self = Box

这意味着类界说可经过把该类作为当时目标来履行,一起也意味着元类和父类中的该办法在办法界说履行期间是可用的。