在 Perl 中,对象是通过对标准数据结构(数组、哈希、标量)的引用进行 "blessing" 实现的。从历史上看,Perl 中的面向对象编程是通过对哈希的访问来构建的,其中键是属性名称,值是数据本身。这种方法提供了灵活性,但需要纪律:语言不实现严格的封装和访问修饰符——一切都基于约定。
问题:没有明确的限制,任何代码都可以访问属性;容易破坏对象的不变量,混淆命名空间,在继承时出错。
解决方案:严格遵循约定:通过约定(例如,使用下划线)隐藏内部数据,尽可能使用访问器方法,对于复杂的任务使用标准模块 Moose、Moo、Class::Accessor 等。
代码示例:
package Animal; sub new { my $class = shift; my $self = { _name => shift }; bless $self, $class; return $self; } sub get_name { $_[0]->{_name} } package Dog; use parent 'Animal'; sub bark { print "Woof! "; } my $dog = Dog->new("Buddy"); print $dog->get_name; $dog->bark;
关键特性:
可以不使用 bless 创建 Perl 对象吗?
回答:不可以,只有 bless 可以将普通引用转换为可通过 -> 方法理解的对象。
base/parent 的作用是什么,与 @ISA 有什么区别?
回答:@ISA 是一个数组,指向基类。base/parent 自动化处理 @ISA,使模块继承更安全:防止重复继承并提供额外检查。
如果子类定义了与父类同名的方法,是否会覆盖父类的方法?
回答:会的,如果子类定义了同名方法,'->' 会优先选择它——经典的 "方法覆盖" 工作原理。
在 Animal 模块中,数据存储在公开属性中,直接访问。有人不小心从外部修改字段值——对象变得不一致。
优点:
缺点:
模块使用访问器,所有内部字段以 _ 开头,具有明确的规范——所有数据操作只能通过 get/set 方法进行,同时在 set 中进行了检查。
优点:
缺点: