定数の重複を減らすにはどうすればよいですか?
-
04-07-2019 - |
質問
この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"
};
機能しません。
個別の「定数を使用」を使用するブロックはこの問題を回避しますが、多くの不要なコードが追加されます。
これを行う正しい方法は何ですか?
ありがとう。
解決
おそらく次のように書きます:
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 { おそらく次のように書きます:
<*>
しかし、それでもまだ多くのコードがありますが、重複は取り除かれ、それが勝利です。
なぜログファイルは数値なのですか? 0で始まる場合、配列はハッシュよりも良い選択です。名前が付けられている場合は、より説明的です。
=> "$ETC/configおそらく次のように書きます:
<*>
しかし、それでもまだ多くのコードがありますが、重複は取り除かれ、それが勝利です。
なぜログファイルは数値なのですか? 0で始まる場合、配列はハッシュよりも良い選択です。名前が付けられている場合は、より説明的です。
.xml" } 1 .. 5;
しかし、それでもまだ多くのコードがありますが、重複は取り除かれ、それが勝利です。
なぜログファイルは数値なのですか? 0で始まる場合、配列はハッシュよりも良い選択です。名前が付けられている場合は、より説明的です。
他のヒント
個別の「定数を使用」を使用するブロック この問題を回避しますが、それは 不要なコードが大量に追加されます。
本当にそうですか?
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",
};
これに関する多くの問題は見当たりません。ベースパスを1つのポイントでのみ指定したため、DRYの原則が守られています。 BASE_PATHに環境変数を割り当てた場合:
use constant BASE_PATH => $ENV{MY_BASE_PATH} || "/etc/app1";
...これで、コードを編集せずに定数を再構成する安価な方法が得られます。これについて気に入らないことは何ですか?
繰り返しの&quot; BASE_PATHを本当に削減したい場合。 &quot;連結する場合、定数を自分でインストールしてそれを考慮に入れるための機械を少し追加できます:
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をgrepして、それがどこで定義されているかを確認することはできません。
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",
),
};
残念ながら、それはうまくいきません。この理由は、定義される前に関数(「定数」)を使用しているためです。 constant-&gt; import
を呼び出す前に評価します。
useステートメントはコンパイル時に評価されるため、変数の使用は機能しません。変数への割り当ては実行時にのみ行われるため、まだ定義されていません。
提供できる唯一の解決策は、複数の use constant
ステートメントに分割することです。この場合、2つのステートメントが実行されます( LOG_DIR
と CONF_DIR
に1つ、残りに1つ)。
実行内容によっては、定数がまったく必要ない場合があります。ほとんどの場合、他の人が自分の仕事を成し遂げるために使用するものを書くので、他のプログラマーに柔軟性を与える方法でこの問題を解決します。これらをメソッドにします:
sub base_log_dir { '...' }
sub get_log_file
{
my( $self, $number ) = @_;
my $log_file = catfile(
$self->base_log_dir,
sprintf "foo%03d", $number
);
}
このようにすることで、物事を簡単に拡張またはオーバーライドできます。
これを行うと、定数の折り畳みの価値が失われるため、それがあなたにとってどれほど重要かを考えなければなりません。