最近的Perl Coding遇到了一个问题:需要对一系列常量进行合法性检测。

在Research&Develop后,有一些心得。

本文从Perl的常量定义上,给出一个_自认为_优雅的解决方案。

Perl中一般的常量定义

在写项目的时候为了避免Magic Number的情况,我们经常需要定义常量。

当然了,根据如何写出无法维护的代码的指导我们不应当定义常量,Number越Magic越好。

一般来说,Perl中的常量如下:

    use constant CONST_PI => 3.1416;

在模块化的编码过程中,随着常量的增多,我们会需要把这些常量放到一个常量模块(Perl Module)里面:

这样的做法也符合DRY(Don’t Repeat Yourself)的原则。#DRYBestPractice?

比如说在哼哧哼哧地写了一阵子以后,我们有下面这个常量模块。

    package Lirian::Constants;
    use strict;
    use warnings;

    #Math Const
    use constant CONST_PI => 3.1416;
    use constant CONST_E  => 2.718;

    #Default Config Parm Name
    use constant PARM_DB_NAME  => 'dbName';
    use constant PARM_DB_HOST  => 'dbHost';
    use constant PARM_GIT_USER => 'gitUser';

    require Exporter;
    our @ISA = qw(Exporter);

    our @EXPORT_OK = qw(
        CONST_PI
        CONST_E
        PARM_DB_NAME
        PARM_DB_HOST
        PARM_GIT_USER
    );

    our %EXPORT_TAGS = (
        all  => \@EXPORT_OK,
        math => [qw(
            CONST_PI
            CONST_E
        )],
        parm => [qw(
            PARM_DB_NAME
            PARM_DB_HOST
            PARM_GIT_USER
        )],
    );

上面这段代码简单粗暴地展示了Perl中的常量模块。

其中的our @ISA = qw(Exporter);是表示本模块继承了Exporter这个模块。

通过这样的定义,我们便有了一个常量的“中心仓库”了。

至于怎么使用,我们就来看下一节。

使用常量模块

在上一节中,通过对Exporter的继承,我们有了一个Lirian::Constants模块。

而一般地,我们会这么使用:

    package Lirian::Math;

    use Lirian::Constants qw(:all);
    use strict;
    use warnings;

    sub get_circum {
        my ($r) = @_;

        return 2 * CONST_PI * $r;
    }

    1;

在正常的情况下,常量的使用方法比较简单,但假如需求有些绕:

在程序的开始时,我们需要检验用户的配置文件,确保每个条目用户都是有效的。

比如说需求文件如下:

    dbName=github
    dbHost=localhost
    gitUser=LKI

显然地,一种简单但稍微蠢了点的方法是这么做:

    sub validate_config_dumb {
        my ($conf) = @_;

        validate_parm($conf, PARM_DB_NAME);
        validate_parm($conf, PARM_DB_HOST);
        validate_parm($conf, PARM_GIT_USER);
    }

这样做有几个问题:

1.config parameter一多,代码就很长,丑
2.增加config constant时,不仅要改常量模块,这里也要加,累
3.这样的代码提交上去感觉好丢脸,蠢
4.以上三点在撞大运编程里都不是问题

那怎么样做才算是优雅的做法呢?

利用Perl的Export机制

在Exporter中,定义在%EXPORT_TAGS中的文件可以直接引用:

    my $tags      = \$Lirian::Constants::EXPORT_TAGS;
    my $math_tags = $tags->{math}; # ['CONST_PI', 'CONST_E'];
    my $parm_tags = $tags->{parm}; # ['PARM_DB_NAME', 'PARM_DB_HOST', 'PARM_GIT_USER'];

这样就可以拿到常量的名字了,此时我们便需要在Perl中根据变量名获取变量值

    my $name  = 'CONST_PI';
    my $value = Lirian::Constants->$name; # 3.1416

所以在上一小节的问题中,我们可以得出一个更优雅的解决方案啦~

    sub validate_config {
        my ($conf) = @_;

        my $tags      = \$Lirian::Constants::EXPORT_TAGS;
        my $parm_tags = $tags->{parm};
        foreach my $parm_name (@$parm_tags) {
            my $parm_value = Lirian::Constants->$parm_name;
            validate_parm($conf, $parm_value);
        }
    }

大功告成!从此以后在加常量的同时,validate_config会自动地检验新增的配置选项啦~

此时便可以使用一个酷炫的Git Commit Message来提交工作啦!