[路楊Perl随筆]為什麼你的Perl程序維護困難

提記:最近老有人抱怨自己的Perl程序越來越難維護,也在抱怨Perl的執行效率低下,特寫下自己的不成熟的體會,和同好者共勉之。

1。你的程序使用 use strictuse warnings 了嗎?
不得不承認,Perl語法的随意性雖然很靈活多變,但有時間給維護和效率都帶來了很大麻煩,如果碰見自己的糟糕的編程習慣和随意的語法,那麼太長的代碼就意味着一場噩夢,但是幸好我們有 use strictuse warnings . 有了這個,我們可以很快的找到變量的拼寫錯誤(類似“$xxx 隻使用了一次”的提醒),use strict 迫使你的語法變的嚴謹。當然如果你是維護别人的舊代碼,而原始作者又是習慣使用 全局變量(Global symbol ) 的家夥,那麼,加上 use strict 将讓你陷入更大的維護危機(除非你想全部重寫他的代碼)。加 use strictuse warnings 困難嗎? 不,你隻需要在 你的程序的開頭 '#!/usr/local/bin/perl' 後面加上下句就行:

use strict;
use warnings;

我的建議:
養成良好的編程習慣,盡管 Perl 語法允許你靈活和随意,新寫的程序一定要加上use strict 和 use warnings .這樣可以迫使你定義變量的範圍,免避以後出現的效率低下和變量污染(一般都是Global symbol惹的禍 )

2。你使用模塊了嗎?
噢。。懶惰是 Perl 文化中的美德,難道是我們要使用現成模塊而少寫大量的代碼的原因?我的回答是:不全是。
合理的使用Perl的标準模塊或者CPAN上的成熟模塊能讓你的程序的後期維護變的簡單,而且還可能(一般是肯定)使你的程序變的健壯和運行效率提高。
說的簡單的例子,在國内的的多如牛毛的Perl在線資料(不知道經過多少次轉載的東西了,歎息)中不乏有這樣的 Perl CGI應用之CGI提交變量獲取:

if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});
} elsif ($ENV{'REQUEST_METHOD'} eq "GET") {
$buffer=$ENV{'QUERY_STRING'};
}
@pairs = split(/&/, $buffer); #(注1)

foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$forms{$name} = $value;
}


OK,我承認這段 code 很經典,但是有必要嗎? 如果對于一個大的網站的CGI實現,每個需要處理提交參數的腳本程序都在前面加上這段話,不說其他的,就是不小心寫錯一個字符,也估計是維護人員的噩夢吧?不抱怨才怪。
另外,看見我标明 #(注1) 紅色的代碼吧?這段代碼隻能識别以 & 為分割的參數,而現在多數流行以 ; (分号)為分割的參數,如果要改的話,對維護人員也是個頭疼的事情吧。
再分析,這段代碼隻能處理 key1=val1&key2=val2 這樣格式的參數,如果要處理key=val1&key=val2 ( 即一個變量多個值的情況)就無能為力了:)
其實這段代碼完全可以用 CGI.pm 改寫,隻需要以下代碼:

use CGI;
$my $query =CGI->new();
my %forms = map { $_ =>$query->param($_) } $query->param(); #(注2)

就可以了,不單解決了維護人員的代碼噩夢,而且可以完全處理分号隔離和一鍵多值問題。更可貴的,CGI.pm是Perl的标準模塊(無須安裝),運行效率有興趣的朋友可以測試下,效率絕對要高的多:)

OK,關于代碼維護困難的問題就簡單的說這些,一家之言,隻是我在學習使用Perl中的一點體會而已,請各位斧正。下次有時間,我準備和大家探讨下Perl程序效率低下的原因。



#(注2): 其實這句還是存在這安全漏洞,就好象原來的那段長代碼也存在安全隐患一樣,在正式的程序,還需要對獲取的變量進行安全處理(比如過濾掉特殊的字符),防止産生 WebShell,讓Cracker有機可乘。
辦法也很簡單:
my %forms = map { $_ => mysub($query->param($_)) } $query->param();

其中 mysub() 就是自定義的安全過濾函數:)
另外, 加上 my 是我的習慣,也是為了符合 use strict;,如果要徹底和上面的那一長段code等價的話,去掉這個就是了。