本文和大家重点讨论一下Perl面向对象编程的两种实现和比较,这里比较了在Perl中两种主流的面向对象编程的实现方式,并且深刻地剖析了两种实现的技术内幕,并且提供了可供读者直接使用的代码和模块示例。
Perl面向对象编程的两种实现和比较
本文比较了在Perl中两种主流的面向对象编程的实现方式,基于匿名哈希表的实现和基于数组的实现。深刻地剖析了两种实现的技术内幕,并且提供了可供读者直接使用的代码和模块示例。在文章的***作者比较了两种实现方式的优劣,并对读者给出了在实际工作中选择何种方式实现面向对象编程的建议。
背景
我们常常可以从软件工程的书和文章中,或者项目经理的口中,听到面向对象编程这样的字眼。与大多数时髦的技术用词不同,面向对象编程的确可以为我们的软件设计和开放工作带来本质性的变化。Perl作为一种成熟的“面向过程”的语言,同样也提供了对于面向对象编程的支持。
一个好的“面向对象“的设计不仅是以数据为中心,它还尽力地封装并且隐藏了实际的数据结构,而且只对外界开放有限的,具备良好文档的接口。在下文中,我们将看到如何使用Perl语言的特性来实现这些面向对象设计的优点的。
Perl中有两种不同地面向对象编程的实现,一是基于匿名哈希表的方式,每个对象实例的实质就是一个指向匿名哈希表的引用。在这个匿名哈希表中,存储来所有的实例属性。二是基于数组的方式,在定义一个类的时候,我们将为每一个实例属性创建一个数组,而每一个对象实例的实质就是一个指向这些数组中某一行索引的引用。在这些数组中,存储着所有的实例属性。
Perl面向对象编程的概念
首先,我们定义几个预备性的术语。
实例(instance):一个对象的实例化实现。
标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。
实例属性(instanceattribute):一个对象就是一组属性的集合。
实例方法(instancemethod):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化。
类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。
基于匿名散列表的方法
首先我们来谈谈基于匿名散列表的面向对象实现。首先,我们需要定一个匿名散列表,并用一个引用指向这个匿名散列表。如清单1所示,我们定义了一个初始化函数来封装这个匿名散列表的初始化过程。这个函数接受参数作为初始值,并且用这些值初始化其内部包含的匿名散列表,并且返回一个指向这个匿名散列表的引用。在这个例子当中,我们创建了一个Person模块,并且定义了一个可以实例化模块Person的new函数。
清单1.基于匿名哈希表的面向对象编程
- packagePerson;
- subnew{
- my($name,$age)=@_;
- my$r_object={
- “name”=>$name,
- “age”=>$age
- }
- return$r_object;
- }
- my$personA=Person->new(“Tommy”,22);
- my$personB=Person->new(“Jerry”,30);
- print“PersonA’sname:”.$personA->{name}.“age:”.$personA->{age}.”.\n”;
- print“PersonB’sname:”.$personB->{name}.“age:”.$personB->{age}.”.\n”;
但是,现在的这个方案有一个致命的缺点,Perl的编译器并不知道如何new函数所返回的指向匿名哈希表的引用属于哪个类(模块)。这样的话,如果要使用类中的实例方法,只能直接标出方法所属于的类(模块)的名字,并将引用作为方法的***个参数传递给它,
基于匿名散列表的方法中的继承:
Perl允许一个模块在一个特殊的名为@ISA的数组中制定一组其他模块的名称。当在模块中找不到某个实例方法时,它就为检查那个模块的@ISA是否被初始化。如果已经初始化了,它就为检查其中的某个模块是否支持这个“缺少”的函数。如果它按照深度优先的层次结构搜索@ISA数组并且发现同名的方法,它会调用***个被发现的同名方法并将控制权交给它。我们利用Perl语言的这个特性实现了继承。
考虑这样一个类的层次,我们定义一个Employee类,继承于基类Person,如清单5所示。
我们将类名Person放入包Employee的ISA数组中,这样当调用一个在包Employee中没有定义的函数时,Perl编译器会自动在Person类寻找这个函数。当用户调用new函数初始化一个Employee对象实例的时候,Employee的new函数会在内部调用它的基类的new函数,并且返回一个包含部分以初始化的基类实例属性的匿名哈希表。接着Employee的new函数将继续执行new函数的剩余代码,完成属于Employee自身的初始化工作,为Employee中剩余的实例属性赋值。
基于数组的方法
基于匿名哈希表的面向对象编程方法中有两个明显的不足:一是无法为属性提供一种访问限制,限制外部对内部属性的访问和改变。二是在处理大规模的实例的情况下,系统的内存开销颇大。100个实例意味着将创建100个散列表,这100个散列表都要为插入新纪录的操作而分配额外的存储空间。除了基于匿名散列表的实现,我们也可以利用数组来存储属性,实现面向对象的编程。
整个实现的数据结构非常简单,我们将为每一个类的实例属性分配一个数组(见图一,图中的每一列对应于类的一个实例属性),而每一个新的实例将是跨越所有数组列的一个切片(图中的每一个被使用的行对应于类的一个实例)。每次需要实例化一个新的对象,new函数将被调用。一个新的逻辑行将被分配,新的实例的实例属性将以新的行偏移量插入到相应的属性列当中去。
基于数组的方法中的继承
基于数组的方法中的继承与基于匿名哈希表的方法中的继承完全一样。我们设计的InsideOut类中利用@ISA数组提供了对继承的支持。
总结
相比于基于匿名哈希表的方法,基于数组的方法对存取属性的访问提供了更好的控制和保护并且实现了对于对象的封装,同时也提高了存储空间的利用效率。但是基于匿名哈希表的方法也有着简单易学,逻辑上较为直观而且无需要第三方模块支持的优点。具体使用哪种方式实现面向对象的设计,还要在工作中根据实际情况进行考虑才对。
【编辑推荐】