重构perl子例程的参数列表

重构perl子例程的参数列表,perl,refactoring,Perl,Refactoring,我目前正在重构一些应用程序代码,希望能够从子例程中删除一些参数。例如,假设我有以下[1]: sub do_something { my ( $param1, $param2, $param3, $param4 ) = ( @_ ); .... } 然而,作为重构的一部分,我将参数2和3设置为冗余的。更新这个方法签名很容易,但是有没有一种简单的方法来更新对它的所有调用 我一直在做一些定制的grep/sed/perl来做这件事,但是对sub的一些调用是通过多行进行的,这让我很痛苦,

我目前正在重构一些应用程序代码,希望能够从子例程中删除一些参数。例如,假设我有以下[1]:

sub do_something {
    my ( $param1, $param2, $param3, $param4 ) = ( @_ );
    ....
}
然而,作为重构的一部分,我将参数2和3设置为冗余的。更新这个方法签名很容易,但是有没有一种简单的方法来更新对它的所有调用

我一直在做一些定制的grep/sed/perl来做这件事,但是对sub的一些调用是通过多行进行的,这让我很痛苦,每次我在一个项目上做这件事都是定制的。有没有适合进行这种特定重构的工具

[1] -我向您保证,不是实际的参数或子例程名称

有一些重构功能,但我不知道它是否能实现您想要的

将接口更改为接受哈希而不是位置列表,这将使将来的更改比现在少

sub do_something {
    my (%param) = (@_);
    ...
}

do_something(foo => 23, bar => 42);

如果您有一个代码覆盖率接近100%的测试套件,您可以使用它来查找所有调用站点

给定调用堆栈中位置的参数,
调用方
内置返回

  • 调用方的包名
  • 文件名
  • 行号
  • 完全限定子名称
  • ……还有更多
  • 我们现在可以添加一些代码来记录调用的位置。我们可以将结果放入某些数据结构中进行自动处理,也可以将报告写入日志文件。例如

    sub this_logs {
      {
        # seperate scope to not pollute your sub
        state $log_fh //= do {
          open my $fh, ">", "record_callsites.log"; # assuming autodie;
          $fh;
        };
        state $seen = {};
        my (undef,    undef, undef, $sub) = caller(1);
        my ($package, $file, $line,     ) = caller(0);
        my $site = $sub ? "$sub()" : "pkg $package";
        unless ($seen->{$file}{$line}++) {
          say {$log_fh} "CALL from $site at $file line $line";
        }
      }
      my ($param1, $param2) = @_;
      # etc
    }
    
    假设你所有的代码都是

    this_logs(1, 2, 3);         # direct call
    foo();                      # call from same package
    my $sub = "this_" . "logs"; 
    baz($sub);                  # call by name
    Foo::bar();                 # call from different package
    foo();                      # duplicate call
    
    sub foo {
      return this_logs(5, 6, 7);
    }
    sub baz {
      shift()->(1, 2, 3);  # no strict refs for this, please
    };
    
    package Foo;
    sub bar {
      main::this_logs();
    }
    
    这将生成日志文件

    CALL from pkg main at - line 20
    CALL from main::foo() at - line 28
    CALL from main::baz() at - line 31
    CALL from Foo::bar() at - line 3
    
    (文件名
    -
    表示标准输入)

    因此,如果有一个合适的测试套件,它就能够找到无法使用grep的呼叫站点

    如果您有一个非傻瓜编辑器,您还可以发出一个脚本,依次打开每个文件并将光标定位在正确的行上:

    say "kate -l $line $file";
    say "vim +$line $file";
    

    我不知道有任何快捷方式,但您是否难以可靠地找到方法中的调用,或者编辑它们以符合新接口的要求?找到它们很容易。所有实例都在一个包中,可以使用grep找到。编辑才是问题所在。我可以编写一些脚本来分解调用参数列表并将其重新组合起来,但这可能是定制的。也许是时候给我自己写一个重构模块了。你说调用有时会分散在多行上,所以我假设生成这些参数需要进行非平凡的计算。因此,我认为手动清理许多调用站点是必要的,因为仅仅删除参数可能只会删除一些提到的计算。例如,在通话前可能会有多余的线路,在通话前肯定会有多余的线路。我的计划是删除参数,然后使用git diff的奇迹来检查哪些变量现在是冗余的,并删除这些变量。对参数使用哈希似乎有点错误(不确定为什么,可能只是因为我目前没有这么做!),但我认为,一旦一个子程序有多个参数,这可能是一种方法。我也会好好教训教士的。你不应该有那种感觉。这是一个。啊哈!另一本书要添加到我的购买清单中。谢谢我想补充一点,以这种方式使用散列并不能免除您对参数的详细处理,包括清理方法的非必要输入。根据经验,我可以告诉您,在几乎没有验证的情况下上下传递散列引用(例如,
    %param
    随处可见)的Perl代码堆栈可能会花费大量时间来维护和调试。不过,如果您能很好地命名这些键,daxim在这里的回答将帮助您找到冗余参数的传入位置。有趣的是,这是测试套件内部的一个函数,所以我希望它有100%的覆盖率!这看起来非常有用。我会尽快给它试运行。