【问题标题】:How can I run the same code throughout my Perl scripts with as little code duplication as possible?如何在我的 Perl 脚本中运行相同的代码,同时尽可能少地重复代码?
【发布时间】:2021-10-10 21:16:34
【问题描述】:

我有一个子例程csay,它在 Perl 类(包)中定义。它有几个子类经常和自始至终运行该子例程。它的行为取决于一些变量(其中大部分必须作为参数给出),其中一个在任何词法范围内都是常量($debug_level)。我试图尽可能避免重复代码,包括每次都必须将“常量”变量作为参数传递或在每次使用前加上 $self->

我不反对使用编译时替换(如果 Perl 中存在这种情况),但我想尽可能避免使用实例(blessing 哈希等)。请参阅下面的示例,了解我正在尝试做的事情。有什么想法吗?

package SystemManager::Action::Generic;

sub csay($self, $cur_debug_level, $min_debug_level, $message) {
    if ($cur_debug_level >= $min_debug_level) { print("$message\n"); }
}

sub prepare_system($self)...

package SystemManager::Action::Backup;
use parent "SystemManager::Action::Generic";

sub backup_system($self,$debug_level,@files) {
    $self->csay($debug_level, 1, "Starting backup");
    $self->prepare_system();
    $self->csay($debug_level, 1, "Iterating through files");
    for my $file in (@files) {
        $self->csay($debug_level, 2, "Processing $file");
        ...
    }
    ...
}

csay 不需要 属于同一类;最重要的部分是不必每次都指定 $debug_level 作为参数。

【问题讨论】:

  • 你为什么不想在每个用法前​​加上$self->?这对我来说似乎是正确的模式:如果csay 依赖于调用它的对象的上下文,那么它实际上是一个方法,而不是一个子例程。没有?
  • 了解每种语言都有共同的习语很重要,如果你遵循这些习语,那么你必然会“复制”它们。这不是人们谈论消除代码重复时的意思。你不想在一堆地方复制实际的 logic,但是在你想调用 csay 的任何地方写 $self->csay(...) 是一种常见的模式,实际上并不是在感觉你应该避免。
  • @ruakh 这主要是一种偏好,但这样做不会破坏交易。我没有意识到包/“类”子例程也称为方法,但我认为您是对的,但是 csay 不需要任何无法作为参数提供的信息。我正在使用类和子类来获得更大的灵活性。
  • Re "我正在使用类和子类以获得更大的灵活性。",您的问题特别要求不要使用对象,这实际上是一个非常合理的解决方案到问题。你还说你不想使用调用者,它排除了类和对象。
  • 请提供一个最小示例来澄清您的问题。请参阅minimal reproducible example 了解更多信息

标签: function class perl subclass code-duplication


【解决方案1】:

作为the answer by Håkon Hægland 的替代方案,您可以使用可导出的our 变量:

package SystemManager::Action::Generic;
our $cur_debug_level = 0;
our @EXPORT = qw($cur_debug_level); # or @EXPORT_OK to export explicitly only

sub csay($self, $min_debug_level, $message) {
    if ($cur_debug_level >= $min_debug_level) { print("$message\n"); }
}
    
package SystemManager::Action::Backup;
use parent "SystemManager::Action::Generic";

sub backup_system($self,@files) {
    $self->csay(1, "Starting backup");
    $self->prepare_system();
    $self->csay(1, "Iterating through files");
    for my $file in (@files) {
        $self->csay(2, "Processing $file");
        ...
    }
    ...
}

package main;
use SystemManager::Action::Backup;
$cur_debug_level = 2;
my $backup = Backup->new();
$backup->backup_system(<*.txt>);

【讨论】:

  • 这就是我要找的!虽然我似乎也可以写一次use SystemManager::Action::Generic qw(csay),然后每次只写csay(1,"hello"),而不是$self-&gt;csay(1,"hello")。您介意更新您的答案以包含该信息吗?虽然现在我提到了它,但我将来会失去覆盖csay的灵活性。
  • 变量的使用与你选择如何定义/调用csay无关,当然。
【解决方案2】:

据我所知(如果我误解了,请澄清)您可以将 $debug_level 存储在父类中:

#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
use experimental qw(signatures);

my $debug_level = shift @ARGV || 0;
my $manager = SystemManager::Action::Backup->new($debug_level);
$manager->backup_system();
exit 0;

package SystemManager::Action::Generic;
use feature qw(say);
use strict;
use warnings;
use experimental qw(signatures);

sub new ($class, $debug_level) {
    my $pkg = ref($class) || $class ;
    my $self = bless {debug_level => $debug_level}, $pkg;
    return $self;
}
    
sub csay($self, $min_debug_level, $message) {
    if ($self->{debug_level} >= $min_debug_level) { print("$message\n"); }
}

package SystemManager::Action::Backup;
use feature qw(say);
use strict;
use warnings;
use experimental qw(signatures);

use parent -norequire, "SystemManager::Action::Generic";

sub backup_system($self, @files) {
    $self->csay(1, "Starting backup");
    #$self->prepare_system();
    #$self->csay(1, "Iterating through files");
    #for my $file in (@files) {
    #    $self->csay(2, "Processing $file");
    #    ...
    #}
    #...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2014-11-08
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多