【问题标题】:What's a good way to refactor a monster Perl module into submodules?将怪物 Perl 模块重构为子模块的好方法是什么?
【发布时间】:2010-01-28 17:42:28
【问题描述】:

我有一个项目的 Perl 模块。我可能有十几个程序挂在上面,其中很多都是垃圾。我之前没有在 DBI 上花费太多私人时间,所以这部分是可以修复的,但重要的是它很大。字面意思是 2KLOC。

将这个函数(我们称之为 Dumb.pm)分解成单独的模块(Dumb::FormTools、Dumb::Database 等)很容易,但正如我所说,有很多程序已经使用哑巴;'

我想通过 Dumb 导出 Dumb::Database 的可导出函数,而不必一遍又一遍地修改它:

sub my_dumb_function { return Dumb::Database::my_dumb_function( @_ ) ; }

这并不是说我在上面。只是这似乎是处理问题的愚蠢和不雅的方式。我曾经用过一次“不知道没有更好”的借口,而且一次真的比你得到的要多。帮忙?

【问题讨论】:

  • 我没有时间写一个正确的答案,但是您可以在Dumb 中使用自定义的import 函数,它将对import 的调用路由到各个子模块。
  • 只有 2k LOC?哇,一个不错的小模块! ;)
  • ...然后是我在上一份工作中继承的 7K 提交日志的 14K 怪物...
  • *my_dumb_function = \&Dumb::Database::my_dumb_function 这几乎就是 Exporter 所做的。

标签: perl perl-module


【解决方案1】:

很难给你具体的建议,因为不同的代码库需要不同的策略。我重构一个包含 500 行子例程的模块,与重构一个包含小子例程和大量重复代码的模块不同。如果我也需要更改界面,则有不同的策略。

  1. 将所有内容都纳入源代码管理。您需要保留原始版本和中间版本。
  2. 如果您还没有测试套件,请编写一个。尽可能提高测试覆盖率。此测试套件是在未来版本、错误和所有内容中保留相同行为的基准。您可能会遇到依赖于原始模块中的错误的程序。
  3. 开始破解。在每个步骤中,检查其余部分是否仍通过原始测试,并且已发布的接口仍会产生相同的行为。

不过,我认为您的实际问题是“如何导出到加载 Dumb 的原始模块?”。您可以提供自己的 import 例程,该例程使用 Exporter 的 import_to_level 方法。您可以导入到比加载您的直接级别更高的级别。因此Dumb::Databaseimport 可以将其导出加载到加载Dumb 的命名空间中,即使加载Dumb::Database 的是Dumb

【讨论】:

  • 我不明白你为什么推荐import_to_levelDumb 将成为一个向后兼容的模块,直到 use Dumb; 可以被特定程序需要的单个模块替换。为什么要在每个新模块中编写自定义import,当库存import 允许Dumb 重新导出它从新模块导入的函数时,它必须决定导出到哪个级别?
  • 如果Dumb 需要拆分成单独的模块,而顶层程序仍然希望仅使用Dumb 从这些单独的模块中获取导出,那么您不想导入Dumb。但是,如果 OP 想做其他事情,我不推荐它作为解决方案。有很多方法可以去这里。您的答案有效,但我认为您必须导入 Dumb 才能将相同的内容导出到更高级别是不雅的。
  • 据我了解 OP 的问题,他有一个巨大的模块,可以做很多不同的事情。他希望他使用单独的模块,因此脚本只能加载它实际使用的模块。但他不想跟踪每个写有use Dumb 的程序并修复它以导入正确的模块。他需要一个带有现有导出的 Dumb.pm 以实现向后兼容性,因此他可以逐步修复 use Dumb 的程序,使其仅使用他们实际需要的模块。
  • 这是看待问题的一种方式,但我认为这是在添加一些问题中没有的假设。我没有和你做同样的假设。这些可能是有道理的,但只有他才能澄清他想做的事情以及哪个答案更适合他。 :)
【解决方案2】:

不确定您当前如何使用它(它当前是否导出方法?),但您可以设置新的子模块以允许您导入它们的功能(使用 Exporter),然后只让原始模块显式导入现在破碎的碎片。比如:

package Dumb;

use Dumb::Database qw(my_dumb_function);

1;

package Dumb::Database;

use base qw(Exporter);

our @EXPORT_OK = qw(my_dumb_function);

sub my_dumb_function { 1; }

1;

【讨论】:

  • 只有使用“use Exporter qw(import) ;”才能让它工作,但这确实意味着我能够让它工作。谢谢!
  • 您也可以从 Exporter 继承,这就是我的意思。抱歉,我已经更正了。
【解决方案3】:

我假设 Dumb.pm 当前使用 Exporter。假设您不想重命名函数(只需将它们拆分为单独的模块),您应该能够保留现有的 @EXPORT 定义,从子模块中导入所有内容,然后简单地重新导出函数。

package Dumb;
use Dumb::FormTools ':all';
use Dumb::Database  ':all';

use Exporter 'import';

our @EXPORT = ...;    # Unchanged from original version
our @EXPORT_OK = ...; # Unchanged from original version

1;

默认情况下未定义:all 标签。您必须手动定义它(在每个子模块中)。

our %EXPORT_TAGS = ( all => [ @EXPORT, @EXPORT_OK ] );
# or, for a module that doesn't export anything by default:
our %EXPORT_TAGS = ( all => \@EXPORT_OK );

另一方面,如果子模块没有@EXPORT_OK 函数,那么你可以跳过:all 标签,直接说use Dumb::Submodule;

【讨论】:

    【解决方案4】:

    您可能还想查看Sub::Exporter

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      • 2010-09-12
      • 2010-10-11
      • 1970-01-01
      • 2012-12-22
      • 1970-01-01
      • 2011-07-24
      相关资源
      最近更新 更多