Perl中的面向对象编程

一、模块简介

  模块(module)就是Perl包(package)。Perl的对象基于对包中数据项的引用。

  在用其它语言进行面向对象编程时,先声明一个类然后创建该类的对象(实例),特定类所有对象的行为方式是相同的,由类方法确定,可以通过定义新类或从现存类继承来创建类。

  • 类是一个Perl包,其中包含提供对象方法的类;
  • 方法是一个Perl子程序,类名是其第一个参数;
  • 对象是对类中数据项的引用。

二、Perl中的类

  一个Perl类是一个包。Perl5用双冒号(::)来标识基本类和继承类(之类)。

  Perl中的继承只继承方法,必须使用自己的机制来实现数据的继承。

  因为每个类是一个包,所以它有自己的名字空间及自己的符号名关联数组(详见第x章关联数组),每个类因而可以使用自己的独立符号名集。与包的引用结合,可以用单引号(‘)操作符来定位类中的变量,类中成员的定位形式如:$class‘$member。在Perl5中,可用双冒号替代单引号来获得引用,如:$class‘$member与$class::$member相同。

三、创建类

  package Cocoa;

  #
  # Put "require" statements in for all required,imported packages
  #

  #
  # Just add code here
  #

  接下来,我们往包里添加方法使之成为一个类。第一个需添加的方法是new(),它是创建对象时必须被调用的,new()方法是对象的构造函数。

四、构造函数

  sub new {
      my $this = {}; # Create an anonymous hash, and #self points to it.
      bless $this; # Connect the hash to the package Cocoa.
      return $this; # Return the reference to the hash.
  }

  1 #!/usr/bin/perl
  2 push (@INC,‘pwd‘);
  3 use Cocoa;
  4 $cup = new Cocoa;

  

  ps,

  1. 一定要在构造函数中初始化变量;

  2. 一定要用my函数在方法中创建变量;

  3. 一定不要在方法中使用local,除非真的想吧变量传递给其他子程序;

  4. 一定不要在类模块中使用全局变量。

五、方法

  Perl类的方法只不过是一个Perl子程序而已,也即通常所说的成员函数。Perl的方法定义不提供任何特殊语法,但规定方法的第一个参数为对象或者其被引用的包。Perl有两种方法,静态方法以及虚方法。

  静态方法的第一个参数为类名,虚方法的第一个参数为对象的引用。

六、方法的输出

  如果你现在想引用Cocoa.pm包,将会得到编译错误说未找到方法,这是因为Cocoa.pm的方法还没有输出。输出方法需要Exporter模块,在包的开始部分加上下列两行:
    require Exporter;
    @ISA = qw (Exporter);
    这两行包含上Exporter.pm模块,并把Exporter类名加入@ISA数组以供查找。接下来把你自己的类方法列在@EXPORT数组中就可以了。例如想输出方法closeMain和declareMain,语句如下:
    @EXPORT = qw (declareMain , closeMain);
    Perl类的继承是通过@ISA数组实现的。@ISA数组不需要在任何包中定义,然而,一旦它被定义,Perl就把它看作目录名的特殊数组。它与@INC数组类似,@INC是包含文件的寻找路径。@ISA数组含有类(包)名,当一个方法在当前包中未找到时就到@ISA中的包去寻找。@ISA中还含有当前类继承的基类名。
    类中调用的所有方法必须属于同一个类或@ISA数组定义的基类。如果一个方法在@ISA数组中未找到,Perl就到AUTOLOAD()子程序中寻找,这个可选的子程序在当前包中用sub定义。若使用AUTOLOAD子程序,必须用use Autoload;语句调用autoload.pm包。AUTOLOAD子程序尝试从已安装的Perl库中装载调用的方法。如果AUTOLOAD也失败了,Perl再到UNIVERSAL类做最后一次尝试,如果仍失败,Perl就生成关于该无法解析函数的错误。

七、方法的调用

  调用一个对象的方法有两种,一是通过该对象的引用(虚方法),二是直接使用类名(静态方法)。当然该方法必须已经被输出。

package Cocoa;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(setImports, declareMain, closeMain);
#
# This routine creates the references for imports in Java functions
#
sub setImports{
  my $class = shift @_;
  my @names = @_;
  foreach (@names) {
    print "import " . $_ . ";\n";
  } 
}
#
# This routine declares the main function in a Java script
#
sub declareMain{
  my $class = shift @_;
  my ( $name, $extends, $implements) = @_;
  print "\n public class $name";
  if ($extends) {
    print " extends " . $extends;
  }
  if ($implements) {
    print " implements " . $implements;
  }
  print " { \n";
}
#
# This routine declares the main function in a Java script
#
sub closeMain{
  print "} \n";
}
#
# This subroutine creates the header for the file.
#
sub new {
  my $this = {};
  print "\n /* \n ** Created by Cocoa.pm \n ** Use at own risk \n */ \n";
  bless $this;
  return $this;
}

1;

现在,我们写一个简单的Perl脚本来使用该类的方法,下面是创建一个Java applet源代码骨架的脚本代码:

#!/usr/bin/perl
use Cocoa;
$cup = new Cocoa;
$cup->setImports( ‘java.io.InputStream‘, ‘java.net.*‘);
$cup->declareMain( "Msg" , "java.applet.Applet", "Runnable");
$cup->closeMain();

八、重载

  有时需要制定使用哪个类的方法,如两个不同的类有同名方法的时候。假设类Espresso和Qava都定义了方法grind,可以用::操作符指定使用Qava的方法:
    $mess = Qava::grind("whole","lotta","bags");
    Qava::grind($mess, "whole","lotta","bags");
    可以根据程序的运行情况来选择使用哪个类的方法,这可以通过使用符号引用去调用来实现:
    $method = $local ? "Qava::" : "Espresso::";
    $cup->{$method}grind(@args);

九、析构函数

  Perl跟踪对象的链接数目,当某对象的最后一个应用释放到内存池的时候,该对象就自动销毁。对象的西沟发生在代码停止后,脚本将要结束时。对于全局变量而言,析构发生在最后一行代码运行之后。

  如果你想在对象被释放之前获取控制权,可以定义DESTROY()方法。DESTROY()在对象将释放前被调用,使你可以做一些清理工作。DESTROY()函数不自动调用其它DESTROY()函数,Perl不做内置的析构工作。如果构造函数从基类多次bless,DESTROY()可能需要调用其它类的DESTROY()函数。当一个对象被释放时,其内含的所有对象引用自动释放、销毁。
    一般来说,不需要定义DESTROY()函数,如果需要,其形式如下:

sub DESTROY {
#
# Add code here.
#
}

  因为多种目的,Perl使用了简单的、基于引用的垃圾回收系统。任何对象的引用数目必须大于0,否则该对象的内存就会被释放。当程序退出时,Perl的一个彻底的查找并销毁函数进行垃圾回收,进程中的一切被简单地删除。在UNIX类的系统中,这是多余的,但在内嵌式系统或者多线程环境中这确实是必要的。

十、继承

  

十一、子类方法的继承

十二、Perl类和对象的一些注释

  OOP的最大好处就是代码重用。OOP用数据封装来隐藏一些复杂的代码,Perl的包和模块通过my函数提供数据封装功能,但Perl并不保证之类一定不会直接访问基类的变量,这确实减少了数据封装的好处,虽然这种动作是可以做到的,但却是一个很坏的的编程风格。

  1. 一定要通过方法来访问类变量;  

  2. 一定不要从模块外直接访问类变量。

  当编写包时,应该保证方法所需的条件已具备或者通过参数传递给它。在包内部,应保证对全局变量的访问只用通过方法传递的引用来访问。对于方法要使用的静态或者全局数据,应该在基类中使用local()定义,自雷通过调用基类来获取。有时,子类可能需要改变这种数据,这时,基类可能就不知道怎样去寻找新的数据,因此,这时最好定义对该数据的引用,子类和基类都通过引用来改变该数据。

时间: 2024-10-19 01:24:15

Perl中的面向对象编程的相关文章

Lua语言基础汇总(12)-- Lua中的面向对象编程

简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 1 2 3 4 5 6 7 8 9 10 11 12 local tb1 = {a = 1, b = 2} local tb2 = {a = 1, b = 2} local tb3 = tb1   if tb1 == tb2 then      print("tb1 == tb2") else      print("tb1 ~= tb2") end   tb3.a = 3 pri

lua中的面向对象编程

简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 上述代码会输出tb1 ~= tb2.说明两个具有相同值得对象是两个不同的对象,同时在Lua中table是引用类型的.我在<Lua中的模块与包>中也总结了,我们是基于table来实现的模块,在table中可以定义函数,也就是说,每个table对象都可以拥有其自己的操作.看一段代码: 上面的代码创建了一个新函数,并将该函数存入Account对象的withDraw字段中,然后我们就可以调用该函数了.不过,在函数中使

python中的面向对象编程

在python中几乎可以完成C++里所有面向对象编程的元素. 继承:python支持多继承: class Derived(base1, base2, base3): pass 多态:python中的所有实例方法都是virtual类型的 封装: 这个比较特殊,C++编程中一直强调得比较多的是:为了隐藏实现,所有的成员变量都要是private类型的,还有那些仅与实现相关的,不作为外部接口的方法都要定义成private类型的(或者protected).但是在python里面,并不存在真正的私有类型,根

深入理解javascript中实现面向对象编程方法

介绍Javascript中面向对象编程思想之前,需要对以下几个概念有了解: 1. 浅拷贝和深拷贝:程序在运行过程中使用的变量有在栈上的变量和在堆上的变量,在对象或者变量的赋值操作过程中,大多数情况先是复制栈上的信息,这样就会出现以下情况,如果变量是对象,那么这一操作,复制的只是真正对象所在 的堆内存空间的起始地址,这就是所谓的浅拷贝,如果是深拷贝,则是在内存堆空间中重新分配一个内存,并把分配的内存的起始地址复制过去. 2. 引用类型数据和值类型数据:谈到引用类型数据和值类型数据,自然而然的联想到

JavaScript中的面向对象编程,详解原型对象及prototype,constructor,proto,内含面向对象编程详细案例(烟花案例)

面向对象编程: 面向:以什么为主,基于什么模式 对象:由键值对组成,可以用来描述事物,存储数据的一种数据格式 编程:使用代码解决需求 面向过程编程: 按照我们分析好的步骤,按步骤解决问题 优点:性能比面向对象高,适合跟硬件联系很紧密的东西 缺点:没有面向对象那么容易维护,复用,扩展 面向对象编程: 把事务分解成一个个对象,然后由对象之间分工与合作,分工明确,每一个对象都是功能中心 面向对象特性:封装性.继承性 .多态性 封装性:将一个功能封装起来,小封装 将多个函数封装起来,整体封装起来形成一个

Objective-C中的面向对象编程

1.过程式编程实例,画出Shape数组中的图形: // // main.m // hello-obj // // Created by zhouyang on 16/4/4. // Copyright © 2016年 zhouyang. All rights reserved. // #import <Foundation/Foundation.h> //枚举变量,代表形状 typedef enum{ kCircle, kRectangle, kEgg } ShapeType; //枚举变量,

JavaScript 中的面向对象编程

使用JSON 来定义一个对象: <script type="text/javascript">var xiaoming = { name : 'xiaoming', age : 18, say : function(){ console.log('my name is '+this.name); }}xiaoming.say();xiaoming.name = 'li xiao ming';xiaoming.say();</script>  

echarts中js面向对象编程

function fetchData(cb) { // 通过 setTimeout 模拟异步加载 setTimeout(function () { cb({ categories: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"], data: [5, 20, 36, 10, 10, 20] }); }, 1000);} // 初始 optionoption = {

如何理解并学习javascript中的面向对象(OOP) [转]

如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的作者一样,写出属于自己优秀的类库(哪怕是基于 jquery的插件).那么,你请务必要学习javascript面向对象,否则你无法更灵活的使用javascript这门语言. 什么事闭包?到底什么是原型?(知道闭包和原型的,就算得上是javascript的高手了.但真正能够理解,并且灵活运用的人并不多)到底该如何学习javascript中的面向对象呢?在javascript这么语言正如日中天,相信不少人正在为