Skip to content

Latest commit

 

History

History
298 lines (189 loc) · 9.63 KB

blessed_references.pod

File metadata and controls

298 lines (189 loc) · 9.63 KB

经 bless 后的引用

Perl 5 的默认对象系统故意最小化。以下三条简单的规则相组合构成了简单────但有效的 ────基本对象系统:

  • 一个类就一个包;

  • 一个方法就是一个函数;

  • 一个(bless 后的)引用就是一个对象。

你已经在 Moose 里见过了前两条规则。第三条规则是新出现的。bless 关键字将一个类 的名称和一个引用关联起来,使得任何在该引用上进行的方法调用由与之相关联的类来解析。 它听上去比实际的要复杂一些。

默认的 Perl 5 对象构造器是一个创建引用并对其进行 bless 的方法。出于惯例,构造器通 常命名为 new(),但并非一定如此。构造器几乎总是 类方法

bless 接受两个参数,与类相关联的引用以及类的名称。虽然程序抽象建议使用一个单 独的方法来处理,但你仍可以在构造器或类之外使用 bless。类名称不需要事先存在。

设计上,构造器以被调用者的形式接收类名。直接硬编码类名称也是可能的,但不推荐。 参数式的构造器使得此方法可以通过继承、委托或导出来重用。

引用的类型对对象上的方法调用没有影响。它只掌控对象如何存储 实例数据────对象 自身的信息。哈希引用最为常见,但你可以 bless 任何类型的引用:

Moose 中创建的类由它们自己声明式地定义各自的对象属性,Perl 5 默认的 OO 则非常 宽松。一个存储球衣号和位置、代表篮球运动员的类可能会使用如下构造器:

……运动员可以这样创建:

在类的内部,方法可以直接访问哈希元素:

类之外的方法也可以这样做。这样便违反了封装────特别是,它意味着你绝不能在不 破坏外部代码的情况下改变对象的内部表示,除非投机取巧────因此,保险起见还应 提供访问器方法:

即便只有两个属性,Moose 在那些非必须代码方面显得更具吸引力。

方法查找和继承

除实例数据外,对象的另一部分就是方法分派。给定一个对象(一个 bless 后的引用), 如下形式的方法调用:

……将查找与经 bless 后的引用 $joel 相关联类的名称。在此例中,该类就是 Player。 接下来,Perl 在 Player 包内查找一个名为 number 的函数。如果 Player 类从其 它类继承而来,Perl 也会在父类查找(如此继续)直到它找到 number 方法为止。如果存 在的话,Perl 以 $joel 作为调用物调用它。

Moose 类在元模型中存放各自的继承信息。每一个经 bless 后的引用的类将父类信息存放在 一个名为 @ISA 的包全局变量中。方法分派器会在一个类的 @ISA 中查找它的父类,以 在其中搜索合适的方法。因此,一个 InjuredPlayer 类会在其 @ISA 中包含 Player。 你可以这样编写这重关系:

许多现存的 Perl 5 项目都这样做,但用 parent 编译命令来替代会更容易些:

可以从多个父类继承:

在解析方法分派时,Perl 5 在传统上偏向于对父类使用深度优先搜索。这就是说,如果 InjuredPlayerPlayerHospital::Patient 两者继承,一个在 InjuredPlayer 实例上调用的方法将先分派到 InjuredPlayer,然后是 Player,接着经过所有 Player 的父类来到 Hospital::Patient

Perl 5.10 增加了一个名为 mro 的编译命令,它允许你另行使用称作 C3 的方法解析策略。 虽然特定的细节可能在处理复杂的多重继承布局时变得复杂,关键的区别是,方法解析过程将 在访问父类之前访问所有的子类。

虽然其他技巧(诸如 角色 roles 和 Moose 方法修饰符)允许你避开多重继承,但 mro 编译命令可以帮助你避免方法分派时令人惊讶的行为。可以这样在类中启用:

除非你在编写具有互操作插件的复杂框架,你几乎不会用到它。

AUTOLOAD

如果在调用者及其超类的类定义内没有可用的方法,Perl 5 接下来将按照所选方法解析顺序 在每个类中查找 AUTOLOAD 函数。Perl 会调用它找到的任何 AUTOLOAD,由此提供或 谢绝所需方法。参加 autoload 以获取更多细节。

如你所想的那样,在面对多重继承和多个候选 AUTOLOAD 目标时会变得很复杂。

方法覆盖和 SUPER

与 Moose 中一样,你可以在默认的 Perl 面向对象系统中覆盖方法。不幸的是,Perl 5 核心 并没有提供指出你覆盖父类方法 意图 的机制。更糟糕的是,任何预声明、声明或导入子 类的函数都可能因重名而覆盖父类方法。你可以忘记使用 Moose 的 override 系统,但在 Perl 5 默认的面向对象系统中你根本没有这样(甚至是可选的)一重保护。

要在子类中覆盖一个方法,只需声明一个和父类方法同名的方法。在覆盖的方法内,你可以通 过 SUPER:: 分派指示来调用父类方法:

方法名的 SUPER:: 前缀告诉方法分派器将此方法分派到 父类 的具名实现。你可以向 其传递任何参数,但最好还是重用 @_

应付经 bless 后引用的策略

可能的话避免使用 AUTOLOAD。如果 必须 用到,你应该用它来转发函数(functions) 定义以帮助 Perl 知道哪个 AUTOLOAD 会提供方法的实现。

使用访问器方法而非直接通过引用访问实例数据。甚至在类内部的方法体内也应该这样做。 由你自己来生成这些方法相当乏味;如果你无法使用 Moose,考虑使用诸如 Class::Accessor 这类模块来避免重复编写样板。

准备好某人某时最终将继承你的类(或委托或重新实现接口)。通过不对代码内部细节做出假定、 使用两参数形式的 bless 和把类分割为最小职责单元,可以使得他人的工作更加轻松。

不要在同一个类里混用函数和方法。

为每一个类使用单独的 .pm 文件,除非该类是一个仅用于某处的小型自包含辅助类。

考虑使用 Moose 和 Any::Moose 来替代赤裸的 Perl 5 OO;它们可以轻松和 Perl 5 对象系统 的类和对象互动,还减轻了几乎所有因类声明而带来的无趣,同时提供了更多及更好的特性。

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 5:

A non-empty Z<>