NAME
perlboot - 初 学 者 的 面 向 对 象 教 程
DESCRIPTION 描 述
如 果 你 对 其 他 语 言 中 的 对 象 并 不 熟 悉 的 话 , 那 么 其 他 有 关 perl对 象 的 文 件 可 能 使 你 感 到 恐 惧 , 比 如 perlobj , 这 是 基 础 性 的 参 考 文 件 , 和 perltoot, 这 是 介 绍 perl对 象 的 特 性 的 教 程 . 所 以 , 让 我 们 走 另 一 条 路 ,假 定 你 没 有 任 何 关 于 对 象 的 概 念 . 你 需 要 了 解 子 程 序 (perlsub), 引 用 (perlref et. seq.), 和 包 (或 模 块 ) (perlmod), 如 果 还 不 清 楚 的 话 ,先 把 他 们 搞 清 楚 .
If we could talk to the animals...如 果 我 们 能 和 动 物 交 谈 让 我 们 让 动 物 讲 会 儿 话 :
sub Cow::speak { print "a Cow goes moooo!\n";} sub Horse::speak { print "a Horse goes neigh!\n";} sub Sheep::speak { print "a Sheep goes baaaah!\n"} Cow::speak; Horse::speak; Sheep::speak;结 果 是 : a Cow goes moooo! a Horse goes neigh! a Sheep goes baaaah!没 什 么 特 别 的
. 只 是 简 单 的 子 程 序 , 虽 然 来 自 不 同 的 包 , 并 用 完 整 的 包 名 来 调 用 . 那 么 让 我 们 建 立 一 个 完 整 的 牧 场 吧 :
# Cow::speak, Horse::speak, Sheep::speak 与 上 同 @pasture = qw(Cow Cow Horse Sheep Sheep); foreach $animal (@pasture) { &{$animal."::speak"};}结 果 是 : a Cow goes moooo! a Cow goes moooo! a Horse goes neigh! a Sheep goes baaaah! a Sheep goes baaaah!嗯
. 这 里 的 符 号 代 码 引 用 有 些 不 太 好 . 我 们 正 依 赖 于 "no strict subs" 模 式 , 在 稍 大 些 的 程 序 中 应 尽 量 避 免 . 那 为 什 么 要 这 样 呢 ? 因 为 我 们 要 调 用 的 子 程 序 和 它 所 在 的 包 似 乎 是 不 可 分 的 . 真 的 是 这 样 吗 ?
Introducing the method invocation arrow 调 用 方 法 时 的 箭 头 符 号 现 在 ,我 们 说 "Class->method" 是 调 用 了 包 (或 模 块 )"Class"中 的 "method" 方 法 。 (Here, "Class" is used in its "category" meaning, not its "scholastic" meaning.) 不 是 很 准 确 ,不 过 我 们 会 一 步 一 步 的 来 做 . 现 在 ,可 以 这 样 做 :
# Cow::speak, Horse::speak, Sheep::speak as before Cow->speak; Horse->speak; Sheep->speak;输 出 为 : a Cow goes moooo! a Horse goes neigh! a Sheep goes baaaah!还 不 是 很 有 趣
. 一 样 的 字 符 ,常 量 ,没 有 变 量 . 但 是 , 不 同 部 分 可 以 分 开 了 . 请 看 :
$a = "Cow"; $a->speak; # invokes Cow->speak哇
! 现 在 包 名 与 子 程 序 名 可 以 分 开 了 , 我 们 可 以 用 变 量 来 表 示 包 名 . 这 样 ,在 使 用 "use strict refs" 预 编 译 指 令 时 也 可 以 正 常 工 作 了 .
Invoking a barnyard 创 建 一 个 牲 口 棚 现 在 让 我 们 把 箭 头 用 到 牲 口 棚 的 例 子 中 , 范 例 :
sub Cow::speak { print "a Cow goes moooo!\n";} sub Horse::speak { print "a Horse goes neigh!\n";} sub Sheep::speak { print "a Sheep goes baaaah!\n"} @pasture = qw(Cow Cow Horse Sheep Sheep); foreach $animal (@pasture) { $animal->speak;}现 在 我 们 所 有 的 动 物 都 能 说 话 了 , 而 且 不 用 使 用 代 码 引 用
. 不 过 注 意 到 那 些 相 同 的 代 码 . 每 个 "speak" 子 程 序 的 结 构 是 相 同 的 : 一 个 "print" 操 作 符 和 一 个 基 本 相 同 的 字 符 串 , 只 有 两 个 词 不 同 . 如 果 我 们 可 以 析 出 相 同 的 部 分 就 更 好 了 , 如 果 将 来 要 把 "goes" 替 换 为 "says" 时 就 简 单 得 多 了 实 际 上 这 并 不 困 难 , 不 过 在 这 之 前 我 们 应 该 对 箭 头 符 号 了 解 的 更 多 一 些 .
The extra parameter of method invocation 方 法 调 用 时 的 额 外 参 数 语 句 :
Class->method(@args) 这 样 调 用 函 数 "Class::method":
Class::method("Class", @args);
(如 果 子 程 序 找 不 到 ,"继 承 , inheritance" 开 始 起 作 用 ,这 在 后 面 会 讲 到 ). 这 意 味 着 我 们 得 到 的 第 一 个 参 数 是 类 名 (如 果 没 有 给 出 其 他 参 数 , 它 就 是 调 用 时 的 唯 一 参 数 ).所 以 我 们 可 以 象 这 样 重 写 "Sheep" speaking 子 程 序 :
sub Sheep::speak { my $class = shift; print "a $class goes baaaah!\n";}另 外 的 动 物 与 此 类 似 : sub Cow::speak { my $class = shift; print "a $class goes moooo!\n";} sub Horse::speak { my $class = shift; print "a $class goes neigh!\n";}每 次
$class 都 会 得 到 与 子 程 序 相 关 的 正 确 的 值 . 但 是 ,还 是 有 很 多 相 似 的 结 构 . 可 以 再 简 单 些 吗 ? 是 的 . 可 以 通 过 在 一 个 类 中 调 用 其 它 的 方 法 来 实 现 .
Calling a second method to simplify things 调 用 另 一 个 方 法 以 简 化 操 作 我 们 在 "speak" 中 调 用 "sound". 这 个 方 法 提 供 声 音 的 内 容 .
{ package Cow; sub sound { "moooo" } sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"}}现 在 , 当 我 们 调 用
"Cow->speak" 时 , 我 们 在 "speak" 中 得 到 "Cow" 的 类 $class. 他 会 选 择 "Cow->sound" 方 法 , 然 后 返 回 "moooo". 那 如 果 是 "Horse" 呢 ?
{ package Horse; sub sound { "neigh" } sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"}}仅 仅 包 名 和 声 音 有 变 化
. 因 此 我 们 可 以 在 Cow和 Horse中 共 用 "speak" 吗 ? 是 的 ,通 过 继 承 实 现 !
Inheriting the windpipes 继 承 气 管 我 们 创 建 一 个 公 共 函 数 包 ,命 名 为 "Animal",在 其 中 定 义 "speak":
{ package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"}}然 后 ,在 每 个 动 物 那 里
"继 承 , inherits" "Animal" 类 , 同 时 赋 予 每 个 动 物 各 自 的 声 音 :
{ package Cow; @ISA = qw(Animal); sub sound { "moooo" }}注 意 增 加 的 数 组
@ISA . 我 们 马 上 讲 到 它 . 现 在 当 我 们 调 用 "Cow->speak" 时 会 发 生 什 么 ? 首 先 , Perl构 造 参 数 列 表 . 在 这 种 情 况 下 , 只 有 "Cow". 然 后 Perl 查 找 "Cow::speak". 但 是 找 不 到 , 所 以 Perl检 查 继 承 数 组 @Cow::ISA. 找 到 了 , 那 里 只 有 一 个 "Animal"
Perl 然 后 在 "Animal" 中 查 找 "speak", "Animal::speak". 找 到 了 , 然 后 调 用 该 子 程 序 , 参 数 在 一 开 始 就 被 固 定 了 . 在 子 程 序 "Animal::speak" 中 , $class 是 "Cow" (第 一 个 参 数 ). 在 我 们 调 用 "$class->sound" 时 , 首 先 寻 找 "Cow->sound" , 找 到 了 , 因 此 不 用 查 看 @ISA. 成 功 ! 关 于 @ISA应 该 注 意 的 几 点 问 题 神 奇 的 @ISA 变 量 (读 作 "is a" 而 不 是 "ice-uh"), 声 明 了 "Cow" 是 一 个 ("is a") "Animal"。 注 意 它 是 一 个 数 组 ,而 不 是 一 个 单 值 , 因 为 在 个 别 情 况 下 , 需 要 在 几 个 父 类 中 寻 找 方 法 . 如 果 "Animal" 也 有 一 个 @ISA, 我 们 也 要 查 看 它 . 寻 找 是 递 归 的 ,深 度 优 先 ,在 每 个 @ISA 中 从 左 到 右 寻 找 . 一 般 地 ,每 个 @ISA 只 有 一 个 元 素 (多 元 素 意 味 着 多 继 承 和 多 重 的 头 痛 ), 这 样 我 们 可 以 得 到 一 个 漂 亮 的 继 承 树 . 如 果 使 用 "use strict", @ISA会 引 起 抱 怨 , 因 为 它 不 是 含 有 显 式 包 名 的 变 量 , 也 不 是 字 典 变 量 ("my"). 我 们 不 能 把 它 用 做 "my"变 量 (它 必 须 属 于 所 继 承 的 包 ),但 是 也 还 是 有 几 种 解 决 的 办 法 . 最 简 单 的 办 法 是 加 上 包 名 :
@Cow::ISA = qw(Animal); 或 者 使 用 包 声 明 :
package Cow; use vars qw(@ISA); @ISA = qw(Animal);如 果 你 希 望 把 包 放 到 程 序 内 , 可 以 把 : package Cow; use Animal; use vars qw(@ISA); @ISA = qw(Animal);简 写 为 : package Cow; use base qw(Animal);这 就 精 简 多 了
.
Overriding the methods 方 法 重 载 让 我 们 添 上 一 只 老 鼠 , 它 的 声 音 差 不 多 听 不 到 :
# Animal package from before { package Mouse; @ISA = qw(Animal); sub sound { "squeak" } sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; print "[but you can barely hear it!]\n";}} Mouse->speak;输 出 为 : a Mouse goes squeak! [but you can barely hear it!]在 这 里 ,
"Mouse" 有 它 自 己 的 speak 函 数 , 所 以 "Mouse->speak" 不 会 调 用 "Animal->speak". 这 叫 做 重 载 "overriding". 实 际 上 , 我 们 甚 至 不 用 说 "Mouse" 是 "Animal", 因 为 "speak" 所 用 到 的 所 有 方 法 在 "Mouse" 中 都 有 定 义 . 但 是 有 些 代 码 与 "Animal->speak" 的 相 同 , 这 在 程 序 维 护 时 是 个 问 题 . 我 们 能 不 能 让 "Mouse" 与 其 它 "Animal" 作 相 同 的 事 ,但 是 给 它 加 上 特 殊 的 部 分 呢 ? 可 以 ! 首 先 ,我 们 可 以 直 接 调 用 "Animal::speak" 方 法 :
# Animal package from before { package Mouse; @ISA = qw(Animal); sub sound { "squeak" } sub speak { my $class = shift; Animal::speak($class); print "[but you can barely hear it!]\n";}}注 意 我 们 必 须 使 用
$class (几 乎 肯 定 是 "Mouse") 作 为 "Animal::speak" 的 第 一 个 参 数 , 因 为 我 们 没 有 用 箭 头 符 号 . 那 为 什 么 不 用 呢 ? 嗯 , 如 果 我 们 在 那 儿 调 用 "Animal->speak", 则 第 一 个 参 数 是 "Animal" 而 不 是 "Mouse" , 这 样 当 调 用 "sound" 时 , 就 找 不 到 正 确 的 函 数 了 . 虽 然 如 此 ,直 接 调 用 "Animal::speak" 确 实 不 怎 么 好 . 万 一 "Animal::speak" 不 存 在 , 而 是 继 承 自 @Animal::ISA 中 的 某 个 类 呢 ? 因 为 没 有 使 用 箭 头 符 号 , 我 们 只 有 一 次 机 会 去 调 用 正 确 的 函 数 . 还 要 注 意 到 ,现 在 类 名 "Animal" 直 接 在 子 程 序 中 使 用 . 如 果 维 护 代 码 的 人 没 有 注 意 到 这 一 点 , 改 变 了 <Mouse> 的 @ISA, 没 有 注 意 到 "speak" 用 到 了 "Animal" 那 就 会 出 问 题 . 因 此 , 这 可 能 不 是 一 个 好 方 法 .
Starting the search from a different place 从 其 它 地 方 开 始 寻 找 较 好 的 解 决 办 法 是 让 Perl从 继 承 链 的 上 一 级 开 始 寻 找 :
# same Animal as before { package Mouse; # same @ISA, &sound as before sub speak { my $class = shift; $class->Animal::speak; print "[but you can barely hear it!]\n";}}这 就 对 了
. 使 用 这 一 语 法 , 我 们 从 "Animal" 寻 找 "speak", 在 找 不 到 时 寻 找 "Animal" 的 继 承 链 .且 第 一 个 参 数 是 $class, 所 以 "speak" 和 "Mouse::sound" 都 会 被 正 确 地 调 用 . 但 这 还 不 是 最 好 的 方 法 .我 们 还 必 须 调 整 @ISA 的 元 素 顺 序 . 更 糟 糕 的 是 , 如 果 "Mouse" 有 多 个 父 类 在 @ISA, 我 们 还 要 知 道 哪 个 类 定 义 了 "speak". 那 么 ,有 没 有 更 好 的 办 法 呢 ?
The SUPER way of doing things 使 用 SUPER方 法 通 过 把 "Animal" 改 成 "SUPER" 类 , 程 序 可 以 自 动 在 所 有 父 类 中 (@ISA):
# same Animal as before { package Mouse; # same @ISA, &sound as before sub speak { my $class = shift; $class->SUPER::speak; print "[but you can barely hear it!]\n";}}
"SUPER::speak" 意 味 着 在 当 前 包 的 @ISA 中 寻 找 "speak", 调 用 第 一 个 找 到 的 函 数 。 注 意 它 不 会 查 找 $class 的 @ISA
Where we’re at so far...到 现 在 为 止 我 们 学 了 些 什 么 我 们 已 经 看 到 了 箭 头 符 号 语 法 :
Class->method(@args); 和 它 的 等 价 形 式 :
$a = "Class"; $a->method(@args);它 们 构 造 这 样 一 个 参 数 列 表 : ("Class", @args)并 调 用 Class::method("Class", @Args);但 是 , 如 果 找 不 到
"Class::method", 程 序 会 查 看 @Class::ISA (递 归 的 ) 找 到 一 个 包 含 "method" 的 包 ,然 后 执 行 它 . 使 用 这 种 简 单 的 语 法 , 我 们 可 以 有 类 方 法 ,(多 )继 承 ,重 载 ,以 及 其 它 扩 展 . 使 用 我 们 已 经 学 到 的 东 西 , 我 们 可 以 析 出 公 共 的 代 码 ,以 各 种 不 同 的 形 式 重 用 同 一 工 具 . 这 是 对 象 能 够 提 供 的 核 心 内 容 , 但 是 对 象 还 能 够 提 供 实 例 数 据 , 这 一 点 我 们 还 没 有 涉 及 .
A horse is a horse, of course of course -- or is it? 马 就 是 马 ——真 的 是 这 样 吗 ? 我 们 从 "Animal" 和 "Horse" 类 的 代 码 开 始 :
{ package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"}} { package Horse; @ISA = qw(Animal); sub sound { "neigh" }}这 样 使 得 我 们 调 用
"Horse->speak", 从 而 向 上 调 用 "Animal::speak", 然 后 调 用 "Horse::sound" 来 获 得 指 定 的 声 音 , 输 出 为 :
a Horse goes neigh! 但 是 我 们 所 有 的 马 都 是 相 同 的 . 如 果 我 增 加 一 个 子 程 序 , 所 有 的 马 都 会 共 享 它 . 这 在 创 建 相 同 的 马 时 确 实 不 错 , 但 是 我 们 如 何 能 够 区 分 不 同 的 马 呢 ? 比 如 , 假 设 我 想 给 我 的 第 一 匹 马 起 个 名 字 . 应 该 有 办 法 使 得 它 的 名 字 和 别 的 马 的 名 字 不 同 . 这 可 以 通 过 创 建 一 个 "实 例 ,instance" 来 实 现 . 实 例 是 由 类 创 建 的 . 在 Perl中 , 任 何 引 用 都 可 以 是 实 例 , 就 让 我 们 从 最 简 单 的 引 用 开 始 吧 ,一 个 标 量 引 用 :
my $name = "Mr. Ed"; my $talking = \$name;现 在
$talking 是 指 向 实 例 特 有 数 据 ( $name )的 引 用 。 把 这 个 引 用 变 成 真 正 的 实 例 的 是 一 个 特 殊 的 操 作 符 ,叫 做 "bless":
bless $talking, Horse; 这 个 操 作 符 把 包 名 "Horse" 中 的 所 有 信 息 存 放 到 引 用 所 指 向 的 东 西 中 . 这 时 ,我 们 说 $talking 是 "Horse" 的 一 个 实 例 . 也 就 是 说 , 它 是 一 匹 独 特 的 马 . 引 用 并 没 有 改 变 , 还 可 以 用 于 间 接 引 用 操 作 符 .
Invoking an instance method 调 用 实 例 方 法 箭 头 符 号 可 以 用 于 实 例 . 那 么 , 听 听 $talking 的 声 音 吧 :
my $noise = $talking->sound; 要 调 用 "sound", Perl 首 先 注 意 到 $talking 是 一 个 blessed 引 用 (因 此 是 一 个 实 例 ). 它 会 构 造 一 个 参 数 列 表 , 现 在 只 有 $talking. (在 后 面 我 们 会 看 到 参 数 们 在 实 例 变 量 之 后 , 与 使 用 类 时 相 似 .) 然 后 ,是 真 正 有 意 思 的 部 分 : Perl 查 找 实 例 所 属 的 类 , 这 里 是 "Horse", 在 其 中 寻 找 对 应 的 方 法 . 这 里 , "Horse::sound" 直 接 可 以 找 到 (不 用 使 用 继 承 ), 最 后 这 样 调 用 :
Horse::sound($talking) 注 意 这 里 的 第 一 个 参 数 还 是 实 例 本 身 , 而 不 像 前 面 我 们 学 到 的 是 类 名 . 最 后 返 回 值 是 "neigh", 它 被 赋 值 给 $noise 变 量 . 如 果 找 不 到 Horse::sound, 会 在 @Horse::ISA 列 表 中 查 找 . 类 方 法 与 实 例 方 法 的 唯 一 区 别 是 调 用 时 的 第 一 个 参 数 是 实 例 (一 个 blessed引 用 )还 是 一 个 类 名 (一 个 字 符 串 ).
Accessing the instance data 访 问 实 例 数 据 因 为 我 们 得 到 的 第 一 个 参 数 是 实 例 ,我 们 可 以 访 问 实 例 特 有 的 数 据 . 我 们 可 以 取 得 马 的 名 字 :
{ package Horse; @ISA = qw(Animal); sub sound { "neigh" } sub name { my $self = shift; $$self;}}现 在 ,我 们 调 用 名 字 : print $talking->name, " says ", $talking->sound, "\n";在
"Horse::name" 中 , @_ 数 组 仅 含 有 $talking, shift 将 $talking 赋 给 了 $self. (传 统 上 我 们 在 处 理 实 例 方 法 时 总 是 把 第 一 个 元 素 赋 给 $self, 所 以 你 也 应 该 这 么 做 , 除 非 你 有 不 这 样 做 的 充 分 理 由 .) 然 后 , $self 被 标 量 化 ,成 为 "Mr. Ed", 这 就 行 了 . 输 出 是 :
Mr. Ed says neigh.
How to build a horse 如 何 创 建 一 匹 马 当 然 啦 ,如 果 我 们 手 工 创 建 所 有 的 马 , 我 们 会 出 很 多 错 误 . 不 仅 如 此 ,我 们 还 亵 渎 了 面 向 对 象 编 程 的 特 性 ,因 为 在 那 种 情 况 下 马 的 "内 脏 "也 可 见 了 . 如 果 你 是 兽 医 的 话 ,这 倒 正 好 , 可 是 如 果 你 仅 仅 是 个 爱 马 者 呢 ? 所 以 ,我 们 让 Horse 类 来 创 建 一 匹 新 马 :
{ package Horse; @ISA = qw(Animal); sub sound { "neigh" } sub name { my $self = shift; $$self;} sub named { my $class = shift; my $name = shift; bless \$name, $class;}}现 在 ,我 们 可 以 用
"named" 方 法 创 建 一 匹 马 :
my $talking = Horse->named("Mr. Ed"); 注 意 到 我 们 有 回 到 了 类 方 法 , 所 以 传 递 给 "Horse::named" 的 两 个 参 数 是 "Horse" 和 "Mr. Ed". "bless" 操 作 符 不 仅 将 $name 实 例 化 , 且 将 指 向 $name 的 引 用 作 为 返 回 值 返 回 . 这 样 , 我 们 就 创 建 了 一 匹 马 . 这 里 ,我 们 调 用 了 构 造 器 "named", 它 的 参 数 就 是 特 定 的 "Horse" 的 名 字 . 你 可 以 使 用 不 同 的 构 造 器 用 不 同 的 名 字 建 立 不 同 的 对 象 (比 如 记 录 它 的 谱 系 或 生 日 ). 但 是 , 你 会 发 现 多 数 使 用 Perl的 人 更 喜 欢 把 构 造 器 命 名 为 "new", 并 使 用 不 同 的 方 法 解 释 "new" 的 参 数 . 两 种 都 挺 好 ,只 要 你 能 创 建 对 象 就 行 . (你 会 自 己 创 建 一 个 ,对 吗 ?)
Inheriting the constructor 继 承 构 造 器 但 是 那 个 方 法 中 有 没 有 什 么 对 于 "Horse" 来 说 比 较 特 殊 的 东 西 呢 ? 没 有 . 因 此 , 从 "Animal" 创 建 其 它 任 何 东 西 也 可 以 使 用 相 同 的 方 法 ,我 们 来 试 试 ::
{ package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"} sub name { my $self = shift; $$self;} sub named { my $class = shift; my $name = shift; bless \$name, $class;}} { package Horse; @ISA = qw(Animal); sub sound { "neigh" }}好 了 , 但 是 以 实 例 调 用
"speak" 会 产 生 什 么 结 果 呢 ?
my $talking = Horse->named("Mr. Ed"); $talking->speak;我 们 得 到 的 是 : a Horse=SCALAR(0xaca42ac) goes neigh!为 什 么
?因 为 "Animal::speak" 希 望 它 的 第 一 个 参 数 是 类 名 , 而 不 是 实 例 . 当 实 例 被 传 入 时 ,我 们 希 望 使 用 的 是 字 符 串 而 不 是 实 例 本 身 ,显 示 的 结 果 不 是 我 们 所 希 望 的 .
Making a method work with either classes or instances 使 方 法 同 时 支 持 类 和 实 例 我 们 需 要 做 的 是 让 方 法 检 测 它 是 被 实 例 调 用 的 还 是 被 类 调 用 的 . 最 直 接 的 方 法 是 使 用 "ref" 操 作 符 . 它 在 参 数 是 实 例 时 返 回 字 符 串 ,在 参 数 是 类 名 时 返 回 "undef". 我 们 首 先 改 写 "name" 方 法 :
sub name { my $either = shift; ref $either ? $$either # it’s an instance, return name: "an unnamed $either"; # it’s a class, return generic}在 这 儿 ,
"?:" 操 作 符 决 定 是 选 择 间 接 引 用 (dereference)还 是 派 生 字 符 串 . 现 在 我 们 可 以 同 时 使 用 类 或 实 例 了 . 注 意 我 修 改 了 第 一 个 参 数 为 $either 来 表 示 期 望 的 变 化 :
my $talking = Horse->named("Mr. Ed"); print Horse->name, "\n"; # prints "an unnamed Horse\n" print $talking->name, "\n"; # prints "Mr Ed.\n"我 们 可 以 改 写
"speak" :
sub speak { my $either = shift; print $either->name, " goes ", $either->sound, "\n";}而
"sound" 本 来 就 可 以 工 作 . 那 么 现 在 就 一 切 完 成 了 !
Adding parameters to a method 给 方 法 加 参 数 让 我 们 训 练 动 物 们 吃 饭 :
{ package Animal; sub named { my $class = shift; my $name = shift; bless \$name, $class;} sub name { my $either = shift; ref $either ? $$either # it’s an instance, return name: "an unnamed $either"; # it’s a class, return generic} sub speak { my $either = shift; print $either->name, " goes ", $either->sound, "\n";} sub eat { my $either = shift; my $food = shift; print $either->name, " eats $food.\n";}} { package Horse; @ISA = qw(Animal); sub sound { "neigh" }} { package Sheep; @ISA = qw(Animal); sub sound { "baaaah" }}试 试 吧 : my $talking = Horse->named("Mr. Ed"); $talking->eat("hay"); Sheep->eat("grass");输 出 为 : Mr. Ed eats hay. an unnamed Sheep eats grass.有 参 数 的 实 例 方 法 调 用 时 首 先 得 到 实 例 的 引 用 , 然 后 得 到 参 数 的 列 表 。 因 此 第 一个 调 用 实 际 上 是 这 样 的 : Animal::eat($talking, "hay");
More interesting instances 更 多 有 趣 的 实 例 如 果 实 例 需 要 更 多 的 数 据 该 怎 么 办 呢 ? 更 多 的 项 目 产 生 更 有 趣 的 实 例 , 每 个 项 目 可 以 是 一 个 引 用 或 者 甚 至 是 一 个 对 象 . 最 简 单 的 方 法 是 把 它 们 存 放 到 哈 希 中 . 哈 希 中 的 关 键 词 叫 做 ’实 例 变 量 "(instance variables)或 者 "成 员 变 量 "(member variables), 相 应 的 值 也 就 是 变 量 的 值 。 但 是 我 们 怎 么 把 马 放 到 哈 希 中 呢 ? 回 忆 到 对 象 是 被 实 例 化 (blessed)的 引 用 . 我 们 可 以 简 单 地 创 建 一 个 祝 福 了 的 哈 希 引 用 ,同 时 相 关 的 的 内 容 也 作 些 修 改 就 可 以 了 . 让 我 们 创 建 一 只 有 名 字 有 颜 色 的 绵 羊 :
my $bad = bless { Name => "Evil", Color => "black" }, Sheep; 那 么 "$bad->{Name}" 是 "Evil", "$bad->{Color}" 是 "black". 但 是 我 们 想 通 过 "$bad->name" 存 取 绵 羊 的 名 字 name, 这 有 点 的 问 题 ,因 为 现 在 它 期 望 一 个 标 量 引 用 . 别 担 心 ,因 为 修 正 它 很 简 单 :
## in Animal sub name { my $either = shift; ref $either ? $either->{Name} : "an unnamed $either";}
"named" 当 然 还 是 创 建 标 量 的 绵 羊 , 如 下 修 正 就 好 了 :
## in Animal sub named { my $class = shift; my $name = shift; my $self = { Name => $name, Color => $class->default_color }; bless $self, $class;}默 认 颜 色
"default_color" 是 什 么 ? 嗯 , 如 果 "named" 只 有 一 个 参 数 name, 我 们 还 是 希 望 有 个 颜 色 , 所 以 我 们 设 定 一 个 类 初 始 化 颜 色 . 对 绵 羊 来 说 , 白 色 比 较 好 :
## in Sheep sub default_color { "white" }为 了 避 免 为 每 个 类 定 义 颜 色 , 我 们 可 以 在
"Animal" 中 定 义 一 个 "缺 省 的 缺 省 , backstop" 的 颜 色 :
## in Animal sub default_color { "brown" }现 在 , 因 为 只 有
"name" 和 "named" 与 对 象 的 "结 构 , structure" 相 关 , 其 余 的 部 分 可 以 保 持 不 变 , 所 以 "speak" 工 作 正 常 .
A horse of a different color 一 匹 不 同 颜 色 的 马 但 是 如 果 所 有 的 马 都 是 棕 色 的 ,也 挺 烦 人 的 . 所 以 我 们 可 以 写 个 方 法 来 改 变 马 的 颜 色 .
## in Animal sub color { $_[0]->{Color}} sub set_color { $_[0]->{Color} = $_[1];}注 意 到 存 取 参 数 的 不 同 方 法 了 吗 :
$_[0] 直 接 使 用 , 而 没 有 用 "shift". (这 在 我 们 频 繁 存 取 时 可 以 节 省 一 些 时 间 .) 现 在 我 们 可 以 把 Mr. Ed的 颜 色 变 过 来 :
my $talking = Horse->named("Mr. Ed"); $talking->set_color("black-and-white"); print $talking->name, " is colored ", $talking->color, "\n";结 果 是 : Mr. Ed is colored black-and-white
Summary 总 结 现 在 我 们 讲 了 类 方 法 ,构 造 器 ,实 例 方 法 ,实 例 数 据 ,甚 至 还 有 存 取 器 (accessor). 但 是 这 些 还 仅 仅 是 开 始 . 我 们 还 没 有 讲 到 以 两 个 函 数 getters,setters 形 式 出 现 的 存 取 器 , 析 构 器 (destructor),间 接 对 象 (indirect object notation),子 类 (subclasses that add instance data),per-class data,重 载 (overloading),"isa" 和 "can" 测 试 ,公 共 类 ("UNIVERSAL" class),等 等 . 这 有 待 其 它 文 档 去 讲 解 了 . 无 论 如 何 ,希 望 本 文 使 你 对 对 象 有 所 了 解 .
SEE ALSO 参 见
更 多 信 息 可 参 见 perlobj (这 里 有 更 多 的 Perl对 象 的 细 节 ,而 本 文 的 是 基 础 ), perltoot (面 向 对 象 的 中 级 教 程 ), perlbot (更 多 的 技 巧 ), 以 及 书 籍 ,比 如 Damian Conway的 不 错 的 书 叫 做 《 面 向 对 象 的 Perl (Object Oriented Perl)》 。 某 些 模 块 可 能 对 你 有 用 , 它 们 是 Class::Accessor, Class::Class, Class::Contract, Class::Data::Inheritable, Class::MethodMaker 还 有 Tie::SecureHash
COPYRIGHT
Copyright (c) 1999, 2000 by Randal L. Schwartz and Stonehenge Consulting Services, Inc. Permission is hereby granted to distribute this document intact with the Perl distribution, and in accordance with the licenses of the Perl distribution; derived documents must include this copyright notice intact.
Portions of this text have been derived from Perl Training materials originally appearing in the Packages, References, Objects, and Modules course taught by instructors for Stonehenge Consulting Services, Inc. and used with permission.
Portions of this text have been derived from materials originally appearing in Linux Magazine and used with permission.
中 文 版 维 护 人
redcandle <redcandle51 [AT] chinaren.com>
中 文 版 最 新 更 新
2001年 12月 9日 星 期 日
中 文 手 册 页 翻 译 计 划
http://cmpp.linuxforum.net 跋 本 页 面 中 文 版 由 中 文 man 手 册 页 计 划 提 供 。 中 文 man 手 册 页 计 划 : https://github.com/man-pages-zh/manpages-zh