模块
之前我们介绍了如何使用函数、类和数据结构来组织代码,接下来要介绍的是另一个用来组织代码和扩展代码的机制--模块。一个模块就是一个文件包,可以用use或require 来加载。模块必须要是有效的Perl代码。模块必须以一个真值结尾,这样Perl解析器才能知道模块是否被成功的加载和编译。
当你加载一个模块时,Perl以双冒号分割模块名转换成文件路径。例如,use StrangeMonkey::Persistence;
Perl就会在@INC中查找每一个叫StrangeMonkey的目录,并且在每一个叫StrangeMonkey的目录中查找一个名字为Persistence.pm的文件(实际上就是找StrangeMonkey/Persistence.pm文件);use StrangeMonkey::UI::Mobile;
Perl就会在@INC中查找StrangeMonkey/UI/Mobile.pm文件。
使用模块来组织代码
Perl并不强制要求你使用模块、包或名字空间,你可以将所有代码都放入单个.pl文件或多个.pl文件,可以选择最舒服的方式灵活地来开发、管理、部署你的代码,当然这其中的风险和收益就取决于你自己的经验水平了。
尽管如此,我们建议你在代码量超过几百行后就使用模块来组织代码:
- 模块会强制在系统的不同实体之间进行逻辑分离。
- 无论你是否使用OO,模块都能提供API边界。
- 模块提倡以一种自然的方式来组织源代码。
- Perl生态系统中有很多工具专门用于创建、组织、部署和分发模块。
- 模块提供了代码重用的机制。
即使你不使用面向对象,模块也能帮助对代码进行更好地封装。
****使用和导入****
当你使用use加载一个模块时,Perl就会从磁盘中加载文件,然后调用它的import()方法(如果有参数的话还会传递参数)。按照惯例,一个模块的import()方法,会将一系列名字、导出函数和其他符号标记导入到自己的名字空间。当然这只是一个约定,模块也有可能拒绝提供import()函数或者import()函数执行的是其他行为,如编译指示strict ,作用是在调用者的词法范围内改变行为而不是导出符号:
use strict;
# ... calls strict->import()
use CGI ':standard';
# ... calls CGI->import( ':standard' )
use feature qw( say switch );
# ... calls feature->import( qw( say switch ) )
关键字no会调用模块的unimport()方法(有参数还会传递参数)。最常见的就是搭配import() 来改变程序的行为:
use strict;
# no symbolic references or barewords
# variable declaration required
{
no strict 'refs';
# symbolic references allowed
# strict 'subs' and 'vars' still in effect
}
use和no都会在编译的时候就生效:
use Module::Name qw( list of arguments );
#等同于
BEGIN
{
require 'Module/Name.pm';
Module::Name->import( qw( list of arguments ) );
}
类似的:
no Module::Name qw( list of arguments );
#等同于:
BEGIN
{
require 'Module/Name.pm';
Module::Name->unimport(qw( list of arguments ));
}
都是可选的!
如果模块没有import()或unimport()函数Perl也不会报错。
你也可以直接调用import()和unimport()函数,尽管在BEGIN块之外它们没有什么作用;编译结束后 import()和unimport()就没什么用了。
use和require都是区分大小写的,所以use strict和use Strict是不一样的:一个是加载strict.pm,一个是加载Strict.pm。但你的操作系统或文件系统可能不会区分大小写,对于一个不区分大小写的文件系统,use strict在找不到strict.pm文件时Perl会去加载Strict.pm,但是当你尝试调用Strict->import()时却什么都不会发生,所以谨慎对待大小写总是好的。
****导出****
一个模块可以通过导出自己的全局包符号,这样别的包就可以直接或间接调用import()来使用这些符号。
核心模块Exporter就是导出符号的标准途径。Exporter依赖2个全局变量@EXPORT_OK和@EXPORT,它们包含了一系列要导出的符号。
模块StrangeMonkey::Utilitie提供了几个单独的函数:
package StrangeMonkey::Utilities;
use Exporter 'import';
our @EXPORT_OK = qw( round translate screech );
...
任何其他的代码都可以使用这个模块和导入这些函数(round,translate,screech)。你也可以导出变量:
push @EXPORT_OK, qw( $spider $saki $squirrel );
也可以使用@EXPORT代替@EXPORT_OK来实现类似的功能:
our @EXPORT = qw( monkey_dance monkey_sleep );
这样在任何地方使用use StrangeMonkey::Utilities都会导入这些函数。要明白的是如果指定的要导入的符号,那就不会导入其他的符号;你只会得到你想要的。若要加载一个模块,但是不想导入任何符号,那就使用明确的空列表:
# make the module available, but import() nothing
use StrangeMonkey::Utilities ();
不使用导入列表,也总是可以通过使用完全限定名来调用函数的(这你应该知道的吧):
StrangeMonkey::Utilities::screech();
简化导出
CPAN模块Sub::Exporter提供了更友好的接口来导出函数。
CPAN模块Moose::Exporter用于基于Moose的系统。