Каков хороший способ рефакторинга огромного Perl-модуля на подмодули?

У меня есть модуль Perl для проекта. У меня на нем висит с десяток программ, и многие из них — мусор. Раньше я не проводил много личного времени с DBI, так что эту часть можно исправить, но важно то, что она большая. Буквально 2KLOC.

Было бы легко разбить эту функцию (назовем ее Dumb.pm) на отдельные модули (Dumb::FormTools, Dumb::Database и т. д.), за исключением того, что, как я уже сказал, существует множество программ, которые уже «используют Dumb»; '

Я хотел бы экспортировать экспортируемые функции Dumb::Database через Dumb без необходимости иметь вариации этого снова и снова:

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

Дело не в том, что я выше этого. Просто это кажется глупым и неэлегантным способом решения проблемы. Однажды я использовал оправдание «Не знаю ничего лучше», и один раз действительно больше, чем вы можете получить. Помощь?


person Dave Jacoby    schedule 28.01.2010    source источник
comment
У меня нет времени писать правильный ответ, но вы можете использовать пользовательскую функцию import в Dumb, которая направляет вызовы import в различные дочерние модули.   -  person daotoad    schedule 28.01.2010
comment
Только 2k LOC? Вау, хороший небольшой модуль! ;)   -  person Robert P    schedule 28.01.2010
comment
...а потом был 14-килобайтный монстр с 7-килобайтным журналом коммитов, который я унаследовал от своей последней работы...   -  person Penfold    schedule 28.01.2010
comment
*my_dumb_function = \&Dumb::Database::my_dumb_function Почти то же самое делает Экспортер.   -  person Brad Gilbert    schedule 29.01.2010


Ответы (4)


Не уверен, как вы его используете в настоящее время (экспортирует ли он в настоящее время методы?), но вы можете настроить новые дочерние модули, чтобы позволить вам импортировать их функции (используя 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;
person macabail    schedule 28.01.2010
comment
Я смог заставить его работать, только если использовал use Exporter qw(import) ;, но это означает, что я смог заставить его работать. Благодарю вас! - person Dave Jacoby; 28.01.2010
comment
Вы также можете наследовать от Exporter, что я и имел в виду. Я исправил это, извините. - person macabail; 29.01.2010

Трудно дать вам конкретный совет, потому что разные кодовые базы требуют разных стратегий. Я реорганизую модуль с подпрограммами из 500 строк иначе, чем модуль с небольшими подпрограммами и большим количеством повторяющегося кода. Если мне нужно изменить интерфейс, для этого есть разные стратегии.

  1. Получите все в системе контроля версий. Вам нужно сохранить исходную и промежуточную версии.
  2. Если у вас еще нет набора тестов, напишите его. Добейтесь максимально возможного охвата тестами. Этот набор тестов является основой для сохранения того же поведения в будущих версиях, ошибок и прочего. Вы, вероятно, столкнетесь с программой, которая зависит от ошибки в исходном модуле.
  3. Начинайте взламывать. На каждом шаге проверяйте, что остальные по-прежнему проходят исходные тесты и что опубликованный интерфейс по-прежнему ведет себя так же.

Я думаю, что ваш реальный вопрос, однако, звучит так: «Как мне экспортировать в исходный модуль, который загрузил Dumb?». Вы можете предоставить свою собственную процедуру import, использующую метод Exporter import_to_level. Вы можете импортировать на более высокие уровни, чем тот, который вас загрузил. Таким образом, Dumb::Database import может загружать свой экспорт в пространство имен, которое загрузило Dumb, даже если Dumb загружает Dumb::Database.

person brian d foy    schedule 28.01.2010
comment
Не понимаю, почему вы рекомендуете import_to_level. Dumb будет модулем обратной совместимости до тех пор, пока use Dumb; не будет заменено отдельными модулями, которые нужны конкретной программе. Почему вы должны писать собственный import в каждом новом модуле, который должен решить, на какой уровень экспортировать, когда стандартный import позволит Dumb реэкспортировать функции, которые он импортирует из новых модулей? - person cjm; 28.01.2010
comment
Если Dumb нужно разделить на отдельные модули, а программы верхнего уровня по-прежнему хотят получить экспорт из этих отдельных модулей, просто используя Dumb, то вам не нужно импортировать в Dumb. Однако я не рекомендую это как решение, если ОП хочет сделать что-то еще. Есть много способов пойти сюда. Ваш ответ работает, но я думаю, что это неэлегантно, что вам нужно импортировать в Dumb только для того, чтобы вы могли экспортировать то же самое на более высокий уровень. - person brian d foy; 29.01.2010
comment
Насколько я понял вопрос ОП, у него есть гигантский модуль, который делает кучу разных вещей. Он хотел бы использовать отдельные модули, чтобы скрипт мог загружать только те, которые он действительно использовал. Но он не хочет отслеживать каждую программу, которая говорит use Dumb, и исправлять ее, чтобы импортировать правильные модули. Ему нужен Dumb.pm с существующими экспортами для обратной совместимости, чтобы он мог постепенно исправлять программы, которые use Dumb используют только те модули, которые им действительно нужны. - person cjm; 29.01.2010
comment
Это один из способов увидеть вопрос, но я думаю, что это добавляет некоторые предположения, которых нет в вопросе. Я не делал того же предположения, что и вы. Это может быть оправдано, но только он может уточнить, что он хочет сделать, и какой ответ ему больше подходит. :) - person brian d foy; 29.01.2010

Я предполагаю, что 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;.

person cjm    schedule 28.01.2010

Вы также можете изучить Sub::Exporter.

person Adam Flott    schedule 29.01.2010