Ruby 面向对象

  • 面向对象

    Ruby是一种纯粹的面向对象的语言,所有事物在Ruby中都是对象。Ruby中的每个值都是一个对象,甚至是最原始的东西:字符串,数字甚至是非。甚至类本身也是作为Class类实例的对象。本章将带您了解与面向对象的Ruby相关的所有主要功能。一个类用于指定对象的形式,它将数据表示形式和用于将该数据处理到一个整齐的包中的方法结合在一起。类中的数据和方法称为该类的成员。
  • Ruby 类定义

    定义类时,将为数据类型定义一个蓝图。这实际上并没有定义任何数据,但确实定义了类名的含义,即,类的对象将由什么组成,以及可以对该对象执行什么操作。类定义以关键字class开头,后跟类名,并以end分隔。例如,我们使用关键字class定义Box类,如下所示:
    
    class Box
       code
    end
    
    名称必须以大写字母开头,并且按照惯例,包含多个单词的名称必须与每个单词大写且没有分隔符一起使用(CamelCase)。
  • Ruby 对象定义

    类提供了对象的设计图,因此基本上是从类创建对象的。我们使用new关键字声明类的对象。以下语句声明Box类的两个对象-
    
    box1 = Box.new
    box2 = Box.new
    
  • 初始化方法

    initialize方法是一个标准的Ruby类方法和工作方式几乎相同的方式,构造函数在其他面向对象编程语言的作品。当您要在创建对象时初始化一些类变量时,initialize方法很有用。此方法可能会包含参数列表,并且像其他任何ruby方法一样,它前面会带有def关键字,如下所示-
    
    class Box
       def initialize(w,h)
          @width, @height = w, h
       end
    end
    
  • 实例变量

    实例变量是一种类属性的,他们就成为对象的属性一旦被使用类创建的对象。每个对象的属性都是单独分配的,并且与其他对象没有任何价值。在类中使用@运算符可以访问它们,但是要在类之外访问它们,我们使用公共方法,这些方法称为accessor方法。如果我们采用上述定义的Box类,则@width和@height是Box类的实例变量。
    
    class Box
       def initialize(w,h)
          # assign instance variables
          @width, @height = w, h
       end
    end
    
  • 访问器和设置器方法

    为了使变量可以从类外部使用,必须在访问器方法中定义它们,这些访问器方法也称为getter方法。以下示例显示访问器方法的用法-
    语法
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
    
       # accessor methods
       def printWidth
          @width
       end
    
       def printHeight
          @height
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # use accessor methods
    x = box.printWidth()
    y = box.printHeight()
    
    puts "Width of the box is : #{x}"
    puts "Height of the box is : #{y}"
    
    尝试一下
    类似于用于访问变量值的访问器方法,Ruby提供了一种使用setter方法从类外部设置这些变量的值的方法,其定义如下-
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
    
       # accessor methods
       def getWidth
          @width
       end
       def getHeight
          @height
       end
    
       # setter methods
       def setWidth=(value)
          @width = value
       end
       def setHeight=(value)
          @height = value
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # use setter methods
    box.setWidth = 30
    box.setHeight = 50
    
    # use accessor methods
    x = box.getWidth()
    y = box.getHeight()
    
    puts "Width of the box is : #{x}"
    puts "Height of the box is : #{y}"
    
    尝试一下
  • 实例方法

    在实例方法中以相同的方式也被定义为我们使用定义的任何其它方法DEF关键字并且它们可以仅使用如下所示一个类的实例来使用。它们的功能不仅限于访问实例变量,还可以根据您的要求做更多的事情。
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
       # instance method
       def getArea
          @width * @height
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # call instance methods
    a = box.getArea()
    puts "Area of the box is : #{a}"
    
    尝试一下
  • 类方法和变量

    类变量是在类的所有实例之间共享的变量。换句话说,变量有一个实例,对象实例可以访问它。类变量以两个@字符(@@)为前缀。必须在类定义中初始化类变量,如下所示。使用def self.methodname()定义一个类方法,该方法以end定界符结尾,并且将使用类名作为classname.methodname进行调用,如以下示例所示:
    
    class Box
       # Initialize our class variables
       @@count = 0
       def initialize(w,h)
          # assign instance avriables
          @width, @height = w, h
    
          @@count += 1
       end
    
       def self.printCount()
          puts "Box count is : #@@count"
       end
    end
     
    # call class method to print box count
    Box.printCount()
    
    尝试一下
  • to_s方法

    您定义的任何类都应具有to_s实例方法,以返回对象的字符串表示形式。以下是一个简单的示例,用于表示Box对象的宽度和高度-
    
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
       # define to_s method
       def to_s
          "(w:#@width,h:#@height)"  # string formatting of the object.
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # to_s method will be called in reference of string automatically.
    puts "String representation of box is : #{box}"
    
    尝试一下
  • 访问控制

    Ruby在实例方法级别为您提供了三种保护级别,它们可以是public,private或protected。Ruby没有对实例和类变量应用任何访问控制。
    • public方法 -任何人都可以调用公共方法。默认情况下,方法是公共的(初始化除外),该方法始终是私有的。
    • private方法 -无法访问私有方法,甚至无法从类外部查看私有方法。只有类方法可以访问私有成员。
    • protected方法 -受保护的方法只能由定义类及其子类的对象调用。访问权限保留在家庭内部。
    以下是一个简单的示例,显示所有三个访问修饰符的语法-
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
    
       # instance method by default it is public
       def getArea
          getWidth() * getHeight
       end
    
       # define private accessor methods
       def getWidth
          @width
       end
       def getHeight
          @height
       end
       # make them private
       private :getWidth, :getHeight
    
       # instance method to print area
       def printArea
          @area = getWidth() * getHeight
          puts "Big box area is : #@area"
       end
       # make it protected
       protected :printArea
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # call instance methods
    a = box.getArea()
    puts "Area of the box is : #{a}"
    
    # try to call protected or methods
    box.printArea()   
    
    尝试一下
    执行以上代码后,将产生以下结果。在这里,第一种方法被成功调用,但是第二种方法产生了问题。
    
    ': protected method `printArea' called for # (NoMethodError)
    Did you mean? printArea
    Area of the box is : 200
    
  • 类继承

    面向对象编程中最重要的概念之一是继承。继承使我们可以用另一个类来定义一个类,这使创建和维护应用程序变得更加容易。继承还提供了重用代码功能和快速实现时间的机会,但是不幸的是Ruby不支持多层继承,但Ruby支持mixins。mixin就像多重继承的特殊实现,其中仅接口部分被继承。创建类时,程序员可以指定新类应继承现有类的成员,而不必编写全新的数据成员和成员函数。此现有类称为基类或超类,而新类称为派生类或子类。Ruby还支持子类化的概念,即继承,下面的示例对此概念进行了说明。扩展类的语法很简单。只需在类声明中添加<字符和超类的名称。例如,下面将BigBox类定义为Box的子类-
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
       # instance method
       def getArea
          @width * @height
       end
    end
    
    # define a subclass
    class BigBox < Box
    
       # add a new instance method
       def printArea
          @area = @width * @height
          puts "Big box area is : #@area"
       end
    end
    
    # create an object
    box = BigBox.new(10, 20)
    
    # print the area
    box.printArea()
    
    尝试一下
  • 方法覆盖

    尽管您可以在派生类中添加新功能,但是有时您想更改父类中已定义方法的行为。您可以简单地通过保持方法名称相同并覆盖该方法的功能来做到这一点,如下例所示:
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
       # instance method
       def getArea
          @width * @height
       end
    end
    
    # define a subclass
    class BigBox < Box
    
       # change existing getArea method as follows
       def getArea
          @area = @width * @height
          puts "Big box area is : #@area"
       end
    end
    
    # create an object
    box = BigBox.new(10, 20)
    
    # print the area using overriden method.
    box.getArea()
    
    尝试一下
  • 运算符重载

    我们希望+运算符可以使用+对两个Box对象执行矢量加法运算,*运算符可以将Box的宽度和高度乘以标量,一元-运算符可以使Box的宽度和高度取反。这是Box类的一个版本,其中定义了数学运算符-
    
    class Box
       def initialize(w,h)     # Initialize the width and height
          @width,@height = w, h
       end
    
       def +(other)       # Define + to do vector addition
          Box.new(@width + other.width, @height + other.height)
       end
    
       def -@           # Define unary minus to negate width and height
          Box.new(-@width, -@height)
       end
    
       def *(scalar)           # To perform scalar multiplication
          Box.new(@width*scalar, @height*scalar)
       end
    end
    
  • 冻结对象

    有时,我们希望防止更改对象。Object中的冻结方法使我们能够执行此操作,从而有效地将对象转换为常量。可以通过调用Object.freeze冻结任何对象。冻结的对象可能无法修改:您无法更改其实例变量。您可以使用Object.frozen检查给定的对象是否已经冻结。方法,如果冻结对象,则返回true,否则返回false值。以下示例清除了概念-
    语法
    
    # define a class
    class Box
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
    
       # accessor methods
       def getWidth
          @width
       end
       def getHeight
          @height
       end
    
       # setter methods
       def setWidth=(value)
          @width = value
       end
       def setHeight=(value)
          @height = value
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # let us freez this object
    box.freeze
    if( box.frozen? )
       puts "Box object is frozen object"
    else
       puts "Box object is normal object"
    end
    
    # now try using setter methods
    box.setWidth = 30
    box.setHeight = 50
    
    # use accessor methods
    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,如图所示。下面的例子。
    
    # define a class
    class Box
       BOX_COMPANY = "TATA Inc"
       BOXWEIGHT = 10
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
       # instance method
       def getArea
          @width * @height
       end
    end
    
    # create an object
    box = Box.new(10, 20)
    
    # call instance methods
    a = box.getArea()
    puts "Area of the box is : #{a}"
    puts Box::BOX_COMPANY
    puts "Box weight is: #{Box::BOXWEIGHT}"
    
    尝试一下
    类常量是继承的,可以像实例方法一样覆盖。
  • 使用allocate创建对象

    在某些情况下,您想创建一个对象而不调用其构造函数initialize,即使用new方法,在这种情况下,您可以调用allocate,这将为您创建一个未初始化的对象,如以下示例所示:
    
    # define a class
    class Box
       attr_accessor :width, :height
    
       # constructor method
       def initialize(w,h)
          @width, @height = w, h
       end
    
       # instance method
       def getArea
          @width * @height
       end
    end
    
    # create an object using new
    box1 = Box.new(10, 20)
    
    # create another object using allocate
    box2 = Box.allocate
    
    # call instance method using box1
    a = box1.getArea()
    puts "Area of the box is : #{a}"
    
    # call instance method using box2
    a = box2.getArea()
    puts "Area of the box is : #{a}"
    
    尝试一下