【问题标题】:How can I reduce duplication in constants?如何减少常量中的重复?
【发布时间】:2010-09-18 17:49:56
【问题描述】:

我有这个 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" 
};

不工作。

使用单独的“使用常量”块可以解决这个问题,但会增加很多不需要的代码。

这样做的正确方法是什么?

谢谢。

【问题讨论】:

    标签: perl refactoring constants


    【解决方案1】:

    使用单独的“使用常量”块 确实解决了这个问题,但是 添加了很多不需要的代码。

    真的吗?

    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";
    

    但在这一点上,我认为天平已经从正确转向糟糕:) 首先,您不能再 grep 查找 CONF_FILE1 并查看它的定义位置。

    【讨论】:

      【解决方案2】:

      我可能会这样写:

      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 开头,则数组是比散列更好的选择。如果它们被命名,它们将更具描述性。

      【讨论】:

      • 感谢您的回答,日志名并不是真正的数字 - 我只是为示例更改了它们。
      【解决方案3】:
      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",
          ),
      };
      

      【讨论】:

        【解决方案4】:

        很遗憾,这行不通。这样做的原因是您在定义函数(“常量”)之前就使用了它们。您在调用 constant->import 之前评估它们。

        使用变量不起作用,因为 use 语句是在编译时评估的。分配给变量只在运行时完成,所以它们还不会被定义。

        我能给出的唯一解决方案是将其拆分为多个use constant 语句。在这种情况下,可以使用两条语句(一条用于LOG_DIRCONF_DIR,另一条用于其余的)。

        【讨论】:

          【解决方案5】:

          根据您正在做的事情,您可能根本不需要常量。大多数情况下,我编写其他人用来完成工作的东西,所以我以一种给其他程序员灵活性的方式来解决这个问题。我把这些东西变成了方法:

           sub base_log_dir { '...' }
          
           sub get_log_file
                {
                my( $self, $number ) = @_;
          
                my $log_file = catfile( 
                  $self->base_log_dir, 
                  sprintf "foo%03d", $number
                  );
                }
          

          通过这样做,我可以轻松地扩展或覆盖事物。

          这样做会失去不断折叠的价值,所以你必须考虑这对你有多重要。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-03-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-08
            相关资源
            最近更新 更多