Perl的。 OOP脚本中的重复代码

时间:2013-11-27 18:45:45

标签: perl oop

我正在构建一个脚本,需要一些关于重复代码的帮助。

这是我的构造函数:

sub new {
    my $class = shift;
    my $self = {
                  ip          => shift,
                  username    => shift,
                  password    => shift,
                  need_enable => 0,
                  enable => '',
    };
    my $session = Net::Telnet->new( Timeout => 2, Errmode => 'return');         
    $self->{session} = $session;

   bless($self, $class);
}

以下是一些方法:

sub _send_enable {
    my $self = shift;
    my $session = $self->{session};
    ...

sub _command {
    my $self = shift;
    my $session = $self->{session};
    my $command = shift;
    ...

sub _match_prompt {
    my $self = shift;
    my $session = $self->{session};
    my ( $prematchU , $matchU ) =
         $session->waitfor(match => '/(?m:^[\w.-]+\s?(?:\(config[^\)]*\))?\s?[\$#>]\s?(?:\(enable\))?\s*$)/',
                                        Timeout => 10);
    return  ($prematchU,$matchU);
}

正如您所看到的,在这些方法中,我需要回忆一下在构造函数中初始化的telnet实例。

我有更多方法,在这些方法中我需要再次回忆一下telnet实例。

此外,我需要在方法中调用一些方法。我总是需要将对象作为第一个参数:

_command($self, 'show privilege');

_check_enable($self);

可能我错过了我在“中级Perl”中学到的东西,我觉得我可以避免所有重复的代码。

感谢您的任何建议!

4 个答案:

答案 0 :(得分:5)

首先要做的事情。这样:

_command($self, 'show privilege');

_check_enable($self);

非常错误。要在Perl中调用方法,您需要使用

$self->_command( 'show privilege' );
$self->_check_enable();

(在第二行中,您可以省略parens,因为参数列表为空。)

->运算符告诉Perl您要调用方法而不是常规子例程。如果左侧的东西是受祝福的引用,则使用它的包被用于查找方法。否则,左侧的东西被假定为类名。在任何一种情况下,Perl都将左边的东西作为第一个项放入参数列表中。

使用->和直接调用子例程之间的重要区别是->语法使Perl查找可能在该类或父类中的方法。直接调用子程序不会通过方法调度程序。

至于访问对象本身,Perl中的标准约定是

my $self = shift;

但这不是必需的。由于您的方法的参数来自常规@_ arguments数组,因此您可以使用$_[0]。您也可以使用$_[0]{session}作为会话对象。但恕我直言,如果你将它解压缩到一个命名变量,它更整洁,更容易阅读。

如果您想避免所有重复输入以定义$self,您可以使用类似Method::Signatures的内容,它会为您提供一个花哨的method关键字来解包$self你。

所以这个:

sub _command {
    my $self = shift;
    my $session = $self->{session};
    my $command = shift;
    ...

成为这个:

method _command( $message ) { 
    # $self and $message are already defined, so just use them
    ...

答案 1 :(得分:3)

我的模块Moops旨在重复Perl OO。这是一个例子......

use Moops;
use Net::Telnet;

class My::Class :ro {
  has ip          => ();
  has username    => ();
  has password    => ();
  has need_enable => (default => 0);
  has enable      => (default => '');
  has session     => (builder => 1);

  method BUILDARGS ($ip, $username, $password) {
    return {
      ip       => $ip,
      username => $username,
      password => $password,
    };
  }

  method _build_session () {
    Net::Telnet->new(Timeout => 2, Errmode => 'return');
  }

  method _send_enable () {
    my $session = $self->session;
    ...;
  }

  method _command ($command) {
    my $session = $self->session;
    ...;
  }

  method _match_prompt () {
    $self->session->waitfor(
      match   => '/(?m:^[\w.-]+\s?(?:\(config[^\)]*\))?\s?[\$#>]\s?(?:\(enable\))?\s*$)/',
      Timeout => 10,
    );
  }
}

答案 2 :(得分:2)

你可以消除my $self = shift;。毕竟,那只是$_[0]。您只需使用$_[0]->{session}而不是经常my $session = $self->{session}。这也将解决接受性的一些

但是,重复性对于文档目的很有用。当我在my $self = shift;定义下看到sub时,我立即知道这是一个方法而不是构造函数或普通的子程序。

当我编写方法和构造函数时,我的前几行获取参数。我还从构造函数和方法中隐藏结构或我的对象。它确实意味着编写更多代码行,但该代码有助于记录我尝试做的事情。这是一个很好的方法来捕捉可能的错误,或帮助那些必须维护和调试我的代码的穷人。

例如,以下是我将如何构建构造函数:

sub new {
    my $class      = shift;
    my $ip         = shift;
    my $username   = shift;
    my $password   = shift;

    my $self = {};
    bless $self, $class;

    $self->ip($ip);
    $self->username($username);
    $self->password($password);
    $self->need_enable(0);
    $self->enable('');

    my $session = Net::Telnet->new( Timeout => 2, Errmode => 'return' );
    $self->session($session);

    return $self;
}

这是更多的写作,也许你称之为重复,但......

  • 第一行告诉你这是一个构造函数(my $class = shift;)。
  • 接下来的三行告诉你我希望按顺序有三个参数。
  • 没有迹象表明我的对象是如何实际存储的。相反,我使用方法来设置每个字段 - 许多真正的面向对象语言所坚持的东西。这可能意味着我有更多的方法,但这也意味着如果我必须修改我的类的结构,我不必修改每个构造函数或方法来执行它。

以下是我的session方法的样子:

my session {
    my $self     = shift;
    my $session  = shift;

    if ( defined $session ) {
        if ( not $self->isa("Net::Telnet") ) {
            croak qq(Session parameter must be a "Net::Telnet" object.);
        }
        $self->{session} = $session;
    }
    return $self->{session};
}

这是我的代码中唯一提到会话信息实际存储在我的对象中的地方。通过创建一个单独的方法来设置/获取我的会话,我可以做一些测试。例如,我想测试会话是否是Net::Telnet对象。如果我决定更改会话存储在我的对象中的方式,这是我必须触摸的代码中唯一的位置。

当然,现在我想要消除更多重复

sub _command {
    my $self    = shift;
    my $command = shift;

    my $session = $self->session;
    ...

根据我在这个答案开头给出的建议,你可以消除很多重复:

sub _command {
    my $command = $_[1];

    ... $_[0]->{session} ...  # Some line where you need session.

但是,你会得到什么?一些可怜的schlub维护你的代码有多容易?如果您改变存储会话的方式怎么办?例如,您决定使用inside out objects。想象一下,浏览代码并查找$[0]->{session}之类的内容并进行修改。谈论无聊,容易出错的重复任务!

然而,并非完全失去了。而不是像这样不断重复getter / setter方法:

sub some_method {
    my $self        = shift;
    my $some_method = shift;

    if ( defined $some_method ) {
       $self->{some_method} = $some_method;
    }
    return $some_method;
}

您可以使用Class::Struct模块。关于Class::Struct的好处是它是一个标准的Perl模块。它已经安装好了。

使用Class::Struct可以消除大多数getter / setter方法定义。相反,您需要做的就是编写一些代码实际上正在做有趣事情的方法。例如,我有一个程序有超过40个与之关联的方法,但使用Class::Struct将其降低到不到10个。

另一个选项是Moose,它非常灵活,功能强大,可能就像Perl对象的使用方式一样。它隐藏了许多获取/设置内容和对象的结构。与驼鹿有很多争议。它大,慢,笨重。它也可能是复杂而复杂的。有很多像Mouse这样的软件包试图消除很多问题,同时只丢失一些更深奥的功能。

答案 3 :(得分:0)

非常感谢你的时间。

我现在使用箭头操作符( - >)更改了方法调用。

我在编写更多行时没有问题,但我想确定这些行是否必不可少或者我缺少一些OOP概念(Intermediate Perl教授有关修复重复代码的内容)。

我也要检查Moops模块。

要了解这个论坛是如何运作的。我刚刚添加了4个空格,但事情没有缩进我的预期。

非常感谢!