我有这个Perl脚本,其中包含许多已定义的配置文件常量。例如:
use constant {
LOG_DIR => "/var/log/",
LOG_FILENAME => "/var/log/file1.log",
LOG4PERL_CONF_FILE => "/etc/app1/log4perl.conf",
CONF_FILE1 => "/etc/app1/config1.xml",
CONF_FILE2 => "/etc/app1/config2.xml",
CONF_FILE3 => "/etc/app1/config3.xml",
CONF_FILE4 => "/etc/app1/config4.xml",
CONF_FILE5 => "/etc/app1/config5.xml",
};
我想减少“/ etc / app1”和“/ var / log”的重复,但使用变量不起作用。使用先前定义的常量也不能在相同的“使用常量块”中工作。例如:
use constant {
LOG_DIR => "/var/log/",
FILE_FILENAME => LOG_DIR . "file1.log"
};
不起作用。
使用单独的“使用常量”块可以解决此问题,但这会增加许多不需要的代码。
这样做的正确方法是什么?
谢谢。
答案 0 :(得分:8)
使用单独的“使用常量”块 解决这个问题,但那 添加了许多不需要的代码。
真的吗?
use constant BASE_PATH => "/etc/app1";
use constant {
LOG4PERL_CONF_FILE => BASE_PATH . "/log4perl.conf",
CONF_FILE1 => BASE_PATH . "/config1.xml",
CONF_FILE2 => BASE_PATH . "/config2.xml",
CONF_FILE3 => BASE_PATH . "/config3.xml",
CONF_FILE4 => BASE_PATH . "/config4.xml",
CONF_FILE5 => BASE_PATH . "/config5.xml",
};
我没有看到很多问题。您仅在一个点中指定了基本路径,从而遵守DRY原则。如果为BASE_PATH分配环境变量:
use constant BASE_PATH => $ENV{MY_BASE_PATH} || "/etc/app1";
...然后您可以通过廉价的方式重新配置常量,而无需编辑代码。有什么不喜欢这个?
如果你真的想减少重复的“BASE_PATH。”连接,你可以添加一些机器来自己安装常量并将其考虑在内:
use strict;
use warnings;
use constant BASE_PATH => $ENV{MY_PATH} || '/etc/apps';
BEGIN {
my %conf = (
FILE1 => "/config1.xml",
FILE2 => "/config2.xml",
);
for my $constant (keys %conf) {
no strict 'refs';
*{__PACKAGE__ . "::CONF_$constant"}
= sub () {BASE_PATH . "$conf{$constant}"};
}
}
print "Config is ", CONF_FILE1, ".\n";
但是在这一点上,我认为平衡已经从正确转向讨厌了:)首先,你不能再为CONF_FILE1寻找并查看它的定义。
答案 1 :(得分:7)
我可能会这样写:
use Readonly;
Readonly my $LOG_DIR => "/var/log";
Readonly my $LOG_FILENAME => "$LOG_DIR/file1.log";
Readonly my $ETC => '/etc/app1';
Readonly my $LOG4PERL_CONF_FILE => "$ETC/log4perl.con";
# hash because we don't have an index '0'
Readonly my %CONF_FILES => map { $_ => "$ETC/config$_.xml" } 1 .. 5;
然而,这仍然是很多代码,但它确实消除了重复,这是一个胜利。
为什么您的日志文件是数字的?如果它们以0开头,则数组是比散列更好的选择。如果它们被命名,则它们更具描述性。
答案 2 :(得分:4)
use constant +{
map { sprintf $_, '/var/log' } (
LOG_DIR => "%s/",
LOG_FILENAME => "%s/file1.log",
),
map { sprintf $_, '/etc/app1' } (
LOG4PERL_CONF_FILE => "%s/log4perl.conf",
CONF_FILE1 => "%s/config1.xml",
CONF_FILE2 => "%s/config2.xml",
CONF_FILE3 => "%s/config3.xml",
CONF_FILE4 => "%s/config4.xml",
CONF_FILE5 => "%s/config5.xml",
),
};
答案 3 :(得分:3)
遗憾的是,这不会起作用。原因是您在定义函数('常量')之前使用它们。您在调用constant->import
之前评估它们。
使用变量不起作用,因为在编译时会评估use语句。分配给变量只在运行时完成,因此它们尚未定义。
我能给出的唯一解决方案是将其拆分为多个use constant
语句。在这种情况下,会有两个语句(一个用于LOG_DIR
和CONF_DIR
,另一个用于其他语句。
答案 4 :(得分:0)
根据您的工作情况,您可能根本不需要常量。大多数情况下,我编写其他人用来完成他们的工作的东西,所以我以一种给其他程序员灵活性的方式解决这个问题。我把这些东西变成了方法:
sub base_log_dir { '...' }
sub get_log_file
{
my( $self, $number ) = @_;
my $log_file = catfile(
$self->base_log_dir,
sprintf "foo%03d", $number
);
}
通过这种方式,我可以轻松地扩展或覆盖事物。
这样做会失去常数折叠的价值,所以你必须考虑这对你有多重要。