强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Perl 完全指南 / 第 10 章:模块与包

第 10 章:模块与包

“模块是代码复用的单位”

Perl 的模块系统是 CPAN 生态的基础。理解 packageuserequireExporter 是编写可维护代码的关键。


10.1 package — 命名空间

# 命名空间(Namespace)
package MyApp::Utils;

use strict;
use warnings;

our $VERSION = "1.00";

sub hello {
    my ($name) = @_;
    return "Hello, $name!";
}

# 包内函数是完全限定名
# MyApp::Utils::hello("World")

1;   # 模块必须返回真值

命名空间规则

# 包名使用 :: 分隔
package MyApp::Core::Database;      # 对应文件 MyApp/Core/Database.pm

# 变量在包空间内
package Config;
our $debug = 1;                     # $Config::debug

package main;                       # 默认包
print $Config::debug, "\n";         # 1

10.2 use vs require

特性userequire
执行时机编译时运行时
导入符号✅ 自动导入❌ 不导入
调用方式use Module LISTrequire Module
等价于BEGIN { require Module; Module->import(LIST) }直接加载
# use — 编译时加载并导入
use List::Util qw(max min sum);
print max(1, 2, 3), "\n";

# require — 运行时加载
require List::Util;
print List::Util::max(1, 2, 3), "\n";

# 动态加载
my $module = "JSON::XS";
my $file = "$module.pm";
$file =~ s{::}{/}g;
require $file;

10.3 @INC — 模块搜索路径

# 查看模块搜索路径
perl -e 'print join "\n", @INC, "\n"';

# 运行时添加路径
use lib "/home/user/mylibs";
use lib "/opt/perl/site/lib";

# 环境变量
# export PERL5LIB="/extra/libs:/another/path"

# 命令行
# perl -I/path/to/libs script.pl

10.4 创建自己的模块

步骤 1:创建模块文件

# lib/MyApp/Math.pm
package MyApp::Math;

use strict;
use warnings;
use Exporter 'import';

our $VERSION = "1.00";
our @EXPORT_OK = qw(add multiply PI);
our %EXPORT_TAGS = (
    all    => \@EXPORT_OK,
    basics => [qw(add multiply)],
);

use constant PI => 3.14159265358979;

sub add {
    my ($a, $b) = @_;
    return $a + $b;
}

sub multiply {
    my ($a, $b) = @_;
    return $a * $b;
}

1;   # 必须返回真值

步骤 2:使用模块

#!/usr/bin/env perl
use strict;
use warnings;
use lib "lib";

use MyApp::Math qw(:all);

print add(3, 5), "\n";          # 8
print multiply(4, 6), "\n";     # 24
print PI, "\n";                  # 3.14159265358979

10.5 Exporter — 符号导出

变量说明
@EXPORT默认导出的符号(不推荐)
@EXPORT_OK可选导出的符号(推荐)
%EXPORT_TAGS标签分组
package MyModule;
use Exporter 'import';

our @EXPORT      = qw(always_imported);      # 自动导入(不推荐)
our @EXPORT_OK   = qw(optional_func);        # 按需导入(推荐)
our %EXPORT_TAGS = (
    utils => [qw(func1 func2)],
    all   => [qw(func1 func2 optional_func)],
);

# 使用时:
use MyModule;                          # 导入 @EXPORT
use MyModule qw(optional_func);       # 导入指定函数
use MyModule qw(:utils);              # 导入标签组
use MyModule qw(:all);                # 导入所有
use MyModule ();                      # 不导入任何符号

10.6 Perl 的模块打包规范

MyApp-Utils-1.00/
├── lib/
│   └── MyApp/
│       └── Utils.pm
├── t/
│   ├── 01-basic.t
│   └── 02-functions.t
├── Changes
├── LICENSE
├── Makefile.PL
├── MANIFEST
├── README.md
└── cpanfile

Makefile.PL

use ExtUtils::MakeMaker;

WriteMakefile(
    NAME         => 'MyApp::Utils',
    VERSION_FROM => 'lib/MyApp/Utils.pm',
    ABSTRACT     => 'Utility functions for MyApp',
    AUTHOR       => 'Your Name <you@example.com>',
    LICENSE      => 'perl_5',
    PREREQ_PM    => {
        'JSON::XS'    => '4.0',
        'Try::Tiny'   => '0.30',
    },
    TEST_REQUIRES => {
        'Test::More'  => '0.96',
    },
);

10.7 业务场景:配置管理模块

# lib/MyApp/Config.pm
package MyApp::Config;

use strict;
use warnings;
use Exporter 'import';
use JSON::XS;
use File::Basename;

our @EXPORT_OK = qw(load_config get_config);
our $VERSION = "1.00";

my %CONFIG;

sub load_config {
    my ($file) = @_;
    die "配置文件不存在: $file\n" unless -f $file;

    open my $fh, '<:encoding(UTF-8)', $file
        or die "无法打开 $file: $!\n";
    local $/;
    my $json = <$fh>;
    close $fh;

    %CONFIG = %{decode_json($json)};
    return \%CONFIG;
}

sub get_config {
    my ($key, $default) = @_;
    return $CONFIG{$key} // $default;
}

1;

本章小结

要点内容
package定义命名空间
use编译时加载并导入模块
require运行时加载模块
@INC模块搜索路径
Exporter控制符号导出
@EXPORT_OK推荐的按需导出方式

练习

  1. 创建一个 MyApp::String 模块,提供 trimupperlower 函数
  2. 使用 %EXPORT_TAGS 将函数分为 :basic:all 两组
  3. 创建一个加载 JSON 配置文件的模块
  4. 研究 perldoc -l Module::Name 查看模块路径
  5. 编写一个支持 use MyApp::Config (file => "config.json") 的模块

扩展阅读