开发者

What's a good way to refactor a monster Perl module into submodules?

I have a Perl module for a project. I have maybe a dozen programs hanging off it and a lot of it is garbage. I hadn't spent much close personal time with DBI before, so that part is fixable, but the big thing is that it is big. Literally 2KLOCs.

It would be easy to break up this function (let's call it Dumb.pm) into separate modules ( Dumb::FormTools , Dumb::Database, etc.) except, as I said, there's lots of programs that already 'use Dumb ;'

I would like to export Dumb::Database's exportable functions through Dumb without having to have variations of this over and over again:

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

It isn't that I'm above that. It's just that this seems like the dumb and inelegant way of handling the issu开发者_高级运维e. I used the "Don't know no better" excuse once, and once is really more than you get. Help?


It's difficult to give you specific advice because different code bases require different strategies. I refactor a module with 500-line subroutines differently than one with small subroutines and a lot of repeated code. If I need to change the interface too, there are different strategies for that.

  1. Get everything into source control. You need to keep the original and intermediate versions.
  2. If you don't already have a test suite, write one. Get the test coverage as high as you can. This test suite is the baseline for preserving the same behavior in the future versions, bugs and all. You're probably going to encounter a program that depends on a bug in the original module.
  3. Start hacking away. At each step, check that the rest still passes the original tests and that the published interface still results in the same behavior.

I think your actual question, though, is "How to I export to the original module that loaded Dumb?". You can provide your own import routine that uses Exporter's import_to_level method. You can import to higher levels than the immediate one that loaded you. The Dumb::Database import can thus load its exports into the namespace that loaded Dumb even though it's Dumb that loads Dumb::Database.


Not sure how you are currently using it (does it currently export methods?), but you can set up the new child modules to allow you to import their functions (using Exporter), and then just have the original module explicitly import the now broken out pieces. Something like:

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;


I assume that Dumb.pm currently uses Exporter. Assuming that you don't want to rename the functions (just split them into separate modules), you should be able to keep the existing @EXPORT definitions, import everything from your submodules, and simply re-export the functions.

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;

The :all tag is not defined by default. You have to define it manually (in each submodule).

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

On the other hand, if a submodule has no @EXPORT_OK functions, then you can skip the :all tag and just say use Dumb::Submodule;.


You might also want to look into Sub::Exporter

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜