本章我们将继续讨论 Ruby 中的面向对象编程。
我们从属性访问器开始。 我们将介绍类常量,类方法和运算符重载。 我们将定义多态性,并展示如何在 Ruby 中使用它。 我们还将提到模块和异常。
Ruby 属性访问器
所有 Ruby 变量都是私有的。 只能通过方法访问它们。 这些方法通常称为 setter 和 getter。 创建一个 setter 和一个 getter 方法是非常常见的任务。 因此,Ruby 具有方便的方法来创建两种类型的方法。 它们是attr_reader
,attr_writer
和attr_accessor
。
attr_reader
创建吸气剂方法。 attr_writer
方法为该设置器创建设置器方法和实例变量。 attr_accessor
方法同时创建 getter 方法,setter 方法及其实例变量。
我们有汽车课。 在类的定义中,我们使用attr_reader
和attr_writer
为 Car 类创建两个 getter 和 setter 方法。
在这里,我们创建两个名为 name 和 price 的实例方法。 注意,attr_reader
将方法的符号作为参数。
attr_writer
创建两个名为 name 和 price 的设置方法,以及两个实例变量@name
和@price
。
在这种情况下,调用了两个 setter 方法以用一些数据填充实例变量。
在这里,调用了两个 getter 方法以从c1
对象的实例变量获取数据。
这是示例的输出。
如前所述,attr_accessor
方法创建 getter,setter 方法及其实例变量。
我们有一个Book
类,其中attr_accessor
创建两对方法和两个实例变量。
设置标题和页面方法以及@title
和@pages
实例变量的attr_accessor
方法。
创建一个Book
类的对象。 两种设置方法填充对象的实例变量。
在此代码行中,我们使用两种 getter 方法来读取实例变量的值。
这是示例输出。
Ruby 类常量
Ruby 使您可以创建类常量。 这些常量不属于具体对象。 他们属于阶级。 按照约定,常量用大写字母表示。
我们有一个带有PI
常量的MMath
类。
我们创建一个PI
常量。 请记住,Ruby 中的常量不是强制性的。
我们使用::
运算符访问PI
常量。
运行示例,我们看到此输出。
Ruby to_s
方法
每个对象都有一个to_s
方法。 它返回对象的字符串表示形式。 请注意,当puts
方法将对象作为参数时,将调用该对象的to_s
。
我们有一个 Beinging 类,其中我们重写了to_s
方法的默认实现。
创建的每个类都从基Object
继承。 to_s
方法属于此类。 我们覆盖to_s
方法并创建一个新的实现。 我们提供了一个易于理解的对象描述。
我们创建一个 Beinging 类,并调用to_s
方法两次。 第一次是显式的,第二次是隐式的。
这是我们运行示例时得到的。
运算符重载
运算符重载是指不同的运算符根据其参数具有不同的实现的情况。
在 Ruby 中,运算符和方法之间只有微小的区别。
在示例中,我们有一个Circle
类。 我们在类中重载了+
运算符。 我们使用它来添加两个圆形对象。
我们定义一个带有+
名称的方法。 该方法将两个圆形对象的半径相加。
我们创建两个圆形对象。 在第三行中,我们添加这两个对象以创建一个新对象。
将这两个圆形对象相加会创建第三个半径为 11 的对象。
Ruby 类方法
Ruby 方法可以分为类方法和实例方法。 类方法在类上调用。 不能在类的实例上调用它们。
类方法不能访问实例变量。
上面的代码示例展示了Circle
类。 除了构造函数方法外,它还具有一个类和一个实例方法。
以self
关键字开头的方法是类方法。
实例方法不能以 self 关键字开头。
我们称为类方法。 注意,我们在类名上调用该方法。
要调用实例方法,我们必须首先创建一个对象。 实例方法总是在对象上调用。 在我们的例子中,c
变量保存对象,然后在圆对象上调用 area 方法。 我们利用点运算符。
描述 Ruby 中类方法的代码示例的输出。
有三种方法可以在 Ruby 中创建类方法。
该示例包含三个类。 它们每个都有一个类方法。
类方法可以以self
关键字开头。
另一种方法是将方法定义放在class << self
构造之后。
这是在 Ruby 中定义类方法的第三种方法。
我们看到在Wood
,Brick
和Rock
类上调用所有三个类方法的输出。
在 Ruby 中创建实例方法的三种方法
Ruby 有三种创建实例方法的基本方法。 实例方法属于对象的实例。 使用点运算符在对象上调用它们。
在示例中,我们从 Wood,Brick
和Rock
类创建三个实例对象。 每个对象都有一个定义的实例方法。
这可能是定义和调用实例方法的最常用方法。 info 方法在Wood
类中定义。 之后,创建对象,然后在对象实例上调用 info 方法。
另一种方法是使用属性访问器创建方法。 这是一种方便的方法,可以节省程序员的键入时间。 attr_accessor
创建两个方法,即 getter 和 setter 方法。它还创建一个实例变量来存储数据。 使用信息设置器方法,将创建砖对象,并将数据存储在@info
变量中。 最后,该消息由 info getter 方法读取。
在第三种方法中,我们创建一个空的Rock
类。 该对象被实例化。 以后,将动态创建一个方法并将其放置到对象中。
示例输出。
Ruby 多态性
多态性是对不同的数据输入以不同方式使用运算符或函数的过程。 实际上,多态性意味着如果类 B 从类 A 继承,则不必继承关于类 A 的所有内容; 它可以完成 A 类所做的某些事情。 (维基百科)
通常,多态性是以不同形式出现的能力。 从技术上讲,它是重新定义派生类的方法的能力。 多态性与将特定实现应用于接口或更通用的基类有关。
请注意,在静态类型的语言(例如 C++ ,Java 或 C# )和动态类型的语言(例如 Python 或 Ruby)中,多态的定义有所不同。 在静态类型的语言中,当编译器在编译时或运行时确定方法定义时,这一点很重要。 在动态类型的语言中,我们专注于具有相同名称的方法执行不同操作的事实。
我们有一个简单的继承层次结构。 有一个Animal
基类和两个后代,即Cat
和Dog
。 这三个类中的每一个都有其自己的make_noise
方法实现。 后代方法的实现替换了Animal
类中方法的定义。
Dog
类中的make_noise method
的实现替换了Animal
类中的make_noise
的实现。
我们为每个类创建一个实例。 我们在对象上调用make_noise
和sleep
方法。
这是polymorhism.rb
脚本的输出。
Ruby 模块
Ruby Module
是方法,类和常量的集合。 模块与类相似,但有一些区别。 模块不能有实例,也不能是子类。
模块用于对相关的类进行分组,方法和常量可以放入单独的模块中。 这也防止了名称冲突,因为模块封装了它们所包含的对象。 在这方面,Ruby 模块类似于 C# 名称空间和 Java 包。
模块还支持在 Ruby 中使用 mixins。 mixin 是 Ruby 工具,用于创建多重继承。 如果一个类从一个以上的类继承功能,那么我们说的是多重继承。
Ruby 具有内置的Math
模块。 它具有多种方法和一个常数。 我们使用::运算符访问 PI 常量。 和类中一样,方法由点运算符访问。
如果我们在脚本中包含模块,则可以直接引用 Math 对象,而无需使用 Math 名称。 使用include
关键字将模块添加到脚本中。
程序的输出。
在下面的示例中,我们展示了如何使用模块来组织代码。
Ruby 代码可以在语义上进行分组。 岩石和树木属于森林。 游泳池,电影院,广场属于一个城镇。 通过使用模块,我们的代码具有一定的顺序。 动物也可以在森林和城镇中。 在一个脚本中,我们不能定义两个动物类。 他们会发生冲突。 将它们放在不同的模块中,我们可以解决问题。
我们正在创建属于森林和城镇的对象。 要访问模块中的对象,我们使用::运算符。
将创建两个不同的动物对象。 Ruby 解释器可以在它们之间进行区分。 它通过模块名称来标识它们。
这是 modules3.rb 程序的输出。
本节的最终代码示例将演示使用 Ruby 模块的多重继承。 在这种情况下,这些模块称为混合模块。
我们有三个模块和一个类。 模块代表一些功能。 可以打开和关闭设备。 许多对象可以共享此功能,包括电视,移动电话,计算机或冰箱。 我们没有为每个对象类创建这种被切换为开/关的功能,而是将其分离到一个模块中,如有必要,该模块可以包含在每个对象中。 这样,代码可以更好地组织和紧凑。
“音量”模块组织负责控制音量级别的方法。 如果设备需要这些方法,则仅将模块包含在其类中。
手机使用include
方法添加所有三个模块。 模块的方法在CellPhone
类中混合。 并且可用于该类的实例。 CellPhone
类还具有特定于它的自己的 ring 方法。
创建一个CellPhone
对象,然后在该对象上调用三个方法。
运行示例将给出此输出。
Ruby 异常
异常是表示偏离正常程序执行流程的对象。 引发,引发或引发异常。
在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 Internet 连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为了防止这种情况的发生,我们应该预期并应对预期程序操作中的错误。 为此,我们可以使用异常处理。
异常是对象。 它们是内置Exception
类的后代。 异常对象携带有关异常的信息。 它的类型(异常的类名),可选的描述性字符串和可选的回溯信息。 程序可以是Exception
的子类,或更常见的是StandardError
的子类,以获取提供有关操作异常的其他信息的自定义 Exception 对象。
在上面的程序中,我们有意将数字除以零。 这会导致错误。
可能失败的语句放置在begin
关键字之后。
在rescue
关键字后面的代码中,我们处理了一个异常。 在这种情况下,我们将错误消息打印到控制台。 e 是发生错误时创建的异常对象。
在示例的输出中,我们看到了异常消息。 最后一行显示名为ZeroDivisionError
的异常对象。
程序员可以使用raise
关键字自己引发异常。
18 岁以下的年轻人不允许进入俱乐部。 我们在 Ruby 脚本中模拟这种情况。
如果此人是未成年人,则会引发异常情况。 如果raise
关键字没有特定的异常作为参数,则会引发RuntimeError
异常,将其消息设置为给定的字符串。 该代码未到达puts "Entry allowed"
行。 代码的执行被中断,并在救援块处继续执行。
在救援块中,我们输出错误消息和RuntimeError
对象的字符串表示形式。 我们还调用exit
方法来通知环境脚本执行错误结束。
未成年人未获准进入俱乐部。 bash $?
变量设置为脚本的退出错误。
Ruby 的ensure
子句创建一个始终执行的代码块,无论是否存在异常。
在代码示例中,我们尝试打开并读取 Stones 文件。 I / O 操作容易出错。 我们很容易会有异常。
在确保块中,我们关闭文件处理程序。 我们检查处理程序是否存在,因为它可能尚未创建。 分配的资源通常放置在确保块中。
如果需要,我们可以创建自己的自定义异常。 Ruby 中的自定义异常应继承自StandardError
类。
假设我们处于无法处理大量数字的情况。
我们有一个BigValueError
类。 该类派生自内置的StandardError
类。
超出此常数的数字在我们的程序中被视为“大”。
如果该值大于限制,则抛出自定义异常。 我们给异常消息“超出最大值”。
运行程序。