内容

名称

perlmodstyle - Perl 模块风格指南

引言

本文档试图描述 Perl 社区的编写 Perl 模块的“最佳实践”。它扩展了 perlstyle 中的建议,在阅读本文档之前应将其视为必读内容。

虽然本文档旨在对所有模块作者有用,但它特别针对希望在 CPAN 上发布其模块的作者。

重点在于对模块用户可见的样式元素,而不是只有模块开发者才能看到的那些部分。但是,本文档中提出的许多准则都可以推断并成功应用于模块的内部结构。

本文档与 perlnewmod 的不同之处在于,它是一个风格指南,而不是创建 CPAN 模块的教程。它提供了一个清单,可以根据该清单比较模块以确定它们是否符合最佳实践,而不必详细描述如何实现这一点。

本文档中包含的所有建议都是从与经验丰富的 CPAN 作者和用户进行广泛的对话中收集的。这里给出的每一条建议都是以前错误的结果。此信息旨在帮助您避免犯同样的错误以及为修复错误而不可避免地需要做的额外工作。

本文档的第一部分提供了一个细化的清单;后续部分提供了对清单中项目更详细的讨论。最后一部分“常见陷阱”描述了 CPAN 作者犯的一些最常见的错误。

快速清单

有关此清单中每个项目的更多详细信息,请参见下文。

开始之前

API

稳定性

文档

发布注意事项

在开始编写模块之前

尝试不要在不花时间思考的情况下直接开始开发模块。稍加考虑可能会为你以后节省大量精力。

是否已经完成?

你甚至可能不需要编写模块。检查它是否已在 Perl 中完成,并且除非你有充分的理由,否则避免重新发明轮子。

寻找预先存在的模块的不错地方包括 MetaCPAN 和在 [email protected] (https://lists.perl.org/list/module-authors.html) 上提问。

如果现有模块几乎可以满足你的需求,请考虑编写补丁、编写子类或以其他方式扩展现有模块,而不是重写它。

做好一件事

冒着陈述显而易见的事实的风险,模块旨在成为模块化的。Perl 开发人员应该能够使用模块来组合其应用程序的构建块。然而,重要的是这些块具有正确的形状,并且开发人员不必在只需要小块时使用大块。

你的模块应该有一个明确定义的范围,不超过一个句子。你的模块可以分解成一个相关模块系列吗?

糟糕的示例

“FooBar.pm 提供了 FOO 协议和相关 BAR 标准的实现。”

好的示例

“Foo.pm 提供了 FOO 协议的实现。Bar.pm 实现了相关的 BAR 协议。”

这意味着,如果开发人员只需要一个用于 BAR 标准的模块,则不应该强制他们也安装 FOO 的库。

名称有什么意义?

确保尽早为模块选择一个合适的名称。这将帮助人们查找和记住您的模块,并使使用模块进行编程更加直观。

为模块命名时,请考虑以下事项

在发布前获取反馈

如果您之前从未将模块上传到 CPAN(即使您上传过),我们强烈建议您从已经熟悉模块的应用程序域和 CPAN 命名系统的人那里获取反馈。类似模块或具有类似名称的模块的作者可能是开始的好地方,例如 Perl Monks 等社区网站。

设计和编写模块

模块设计和编码的注意事项

面向对象还是不面向对象?

您的模块可以面向对象 (OO),也可以不面向对象,或者它可以同时提供两种类型的接口。每种技术都有利有弊,在设计 API 时应考虑这些利弊。

在《Perl 最佳实践》(版权所有 2004 年,由 O'Reilly Media, Inc. 出版)中,Damian Conway 提供了一系列标准,供在决定 OO 是否适合您的问题时使用

仔细考虑面向对象是否适合您的模块。无端的面向对象会导致复杂的 API,普通模块用户难以理解或使用。

设计您的 API

您的界面应该能让普通的 Perl 程序员理解。以下准则可以帮助您判断您的 API 是否足够直接

编写简单的例程来完成简单的事情。

拥有大量简单的例程比拥有几个庞大的例程更好。如果您的例程根据其参数显著改变其行为,则表明您应该有两个(或更多)单独的例程。

将功能与输出分开。

以最通用的形式返回您的结果,并允许用户选择如何使用它们。最通用的形式通常是 Perl 数据结构,然后可以使用它来生成文本报告、HTML、XML、数据库查询或您的用户需要的任何其他内容。

如果您的例程遍历某种列表(例如文件列表或数据库中的记录),您可以考虑提供一个回调,以便用户可以依次操作列表的每个元素。File::Find 通过其 find(\&wanted, $dir) 语法提供了一个示例。

提供明智的快捷方式和默认值。

不要要求每个模块用户都通过相同的步骤来实现一个简单的结果。您始终可以为更复杂或非标准的行为包括可选参数或例程。如果大多数用户在开始使用您的模块时必须键入几行几乎相同的代码,则表明您应该将该行为设为默认值。另一个表明您应该使用默认值的良好指标是,如果大多数用户使用相同的参数调用您的例程。

命名约定

您的命名应该一致。例如,最好有

display_day();
display_week();
display_year();

display_day();
week_display();
show_year();

这同样适用于方法名称、参数名称以及对用户可见的任何其他内容(以及大多数不可见的内容!)

参数传递

使用命名参数。使用此类哈希更容易

    $obj->do_something(
	    name => "wibble",
	    type => "text",
	    size => 1024,
    );

... 而不是像这样使用长长的未命名参数列表

$obj->do_something("wibble", "text", 1024);

虽然参数列表可能适用于一个、两个甚至三个参数,但更多参数会让模块用户难以记住,也让模块作者难以管理。如果您想添加一个新参数,则必须将其添加到列表末尾以保持向后兼容性,这可能会让您的列表顺序不直观。此外,如果许多元素可能未定义,您可能会看到以下难看的函数调用

$obj->do_something(undef, undef, undef, undef, undef, 1024);

为具有参数的参数提供合理的默认值。不要让您的用户指定几乎总是相同的参数。

是将参数传递到哈希还是哈希引用中,这个问题在很大程度上取决于个人风格。

使用以连字符(-name)或完全大写(NAME)开头的哈希键是旧版 Perl 的遗留问题,其中普通小写字符串不能由 => 运算符正确处理。虽然出于历史原因或个人风格,一些模块保留了大写或带连字符的参数键,但大多数新模块应使用简单的低级键。无论您选择什么,都要保持一致!

严格性和警告

您的模块应在 strict 严格模式下成功运行,并且应在不生成任何警告的情况下运行。您的模块还应在适当的情况下处理污点检查,尽管在许多情况下这可能会造成困难。

向后兼容性

“稳定”的模块不应破坏向后兼容性,除非至少有一个较长的过渡阶段和版本号的重大更改。

错误处理和消息

当您的模块遇到错误时,它应执行以下一项或多项操作

可配置的错误处理对您的用户非常有用。考虑为警告和调试消息提供级别选择、将消息发送到单独文件、指定错误处理例程或其他此类功能的方法。请务必将所有这些选项默认为最常见的用法。

记录您的模块

POD

您的模块应包括针对 Perl 开发人员的文档。您应该为您的常规技术文档使用 Perl 的“普通旧文档”(POD),尽管您可能希望以其他格式编写附加文档(白皮书、教程等)。您需要涵盖以下主题

Perl 模块文档中的详细程度通常从较少详细到更详细。您的 SYNOPSIS 部分应包含一个最小的使用示例(可能只有短短一行代码;跳过不常见的用例或大多数用户不需要的任何内容);DESCRIPTION 应以广泛的术语描述您的模块,通常只有几段;模块例程或方法的更多详细信息、冗长的代码示例或其他深入的材料应在后续部分中给出。

理想情况下,稍微熟悉您的模块的人应该能够在不按“向下翻页”的情况下刷新他们的记忆。随着您的读者继续阅读文档,他们应该获得越来越多的知识。

Perl 模块文档中建议的章节顺序为

将文档放在它所记录的代码附近(“内联”文档)。在给定方法的子例程正上方包含 POD。这使得保持文档最新变得更加容易,并且避免了对每段代码进行两次记录(一次在 POD 中,一次在注释中)。

README、INSTALL、发行说明、变更日志

模块还应包含一个 README 文件,描述该模块并提供指向更多信息的指针(网站、作者电子邮件)。

应包含一个 INSTALL 文件,其中应包含简单的安装说明。当使用 ExtUtils::MakeMaker 时,这通常为

perl Makefile.PL
make
make test
make install

当使用 Module::Build 时,这通常为

perl Build.PL
perl Build
perl Build test
perl Build install

应为每次软件发行制作发行说明或变更日志,描述用户可见的模块更改,并使用与用户相关的术语。

除非你有充分的理由使用其他格式(例如公司内部使用的格式),否则惯例是将变更日志文件命名为 Changes,并遵循 CPAN::Changes::Spec 中描述的简单格式。

发行注意事项

版本编号

版本号应至少指示主要版本和次要版本,还可能指示次次要版本。主要版本是其中大部分功能已更改或其中添加了主要新功能的版本。次要版本是其中添加或更改了少量功能的版本。次次要版本号通常用于不影响功能的更改,例如文档修补程序。

最常见的 CPAN 版本编号方案如下所示

1.00, 1.10, 1.11, 1.20, 1.30, 1.31, 1.32

正确的 CPAN 版本号是一个浮点数,小数点后至少有 2 位数字。你可以使用以下方法测试它是否符合 CPAN

perl -MExtUtils::MakeMaker -le 'print MM->parse_version(shift)' \
                                                        'Foo.pm'

如果你想发布模块的“测试版”或“早期版本”,但不想让 CPAN.pm 将其列为最新版本,请在常规版本号后使用“_”,后跟至少 2 位数字,例如 1.20_01。如果你这样做,建议使用以下惯用语

our $VERSION = "1.12_01"; # so CPAN distribution will have
                          # right filename
our $XS_VERSION = $VERSION; # only needed if you have XS code
$VERSION = eval $VERSION; # so "use Module 0.002" won't warn on
                          # underscore

利用此技巧,MakeMaker 将仅读取第一行,从而读取下划线,而 Perl 解释器将评估 $VERSION 并将字符串转换为数字。稍后将 $VERSION 视为数字的运算将能够执行此操作,而不会引发关于 $VERSION 不是数字的警告。

切勿发布任何内容(即使是一字文档补丁),而不增加数字。即使是一字文档补丁也应导致次要级别版本发生更改。

一旦选定,坚持您的版本方案非常重要,不要减少数字位数。这是因为“下游”打包程序(例如 FreeBSD 端口系统)以各种方式解释版本号。如果您更改版本方案中的数字位数,您可能会混淆这些系统,从而使您的模块版本混乱,这显然是不好的。

先决条件

模块作者应仔细考虑是否依赖其他模块,以及依赖哪些模块。

最重要的是,选择尽可能稳定的模块。按优先顺序

在 Makefile.PL 或 Build.PL 中的先决条件中指定其他 Perl 模块的版本要求。

务必在 Makefile.PL 或 Build.PL 中指定 Perl 版本要求,并使用 require 5.6.1 或类似命令。有关详细信息,请参阅 use VERSION 的文档。

测试

在分发之前应测试所有模块(使用“make disttest”),并且还应向安装模块的人员提供测试(使用“make test”)。对于 Module::Build,您将使用 make test 等效项 perl Build test

这些测试的重要性与模块声称的稳定性成正比。声称稳定或希望得到广泛使用的模块应尽可能遵守严格的测试方案。

对帮助您编写测试(对您的开发过程或时间影响最小)有用的模块包括 Test::Simple、Carp::Assert 和 Test::Inline。对于更复杂的测试套件,有 Test::More 和 Test::MockObject。

打包

应使用标准打包工具之一打包模块。目前,您可以在 ExtUtils::MakeMaker 和更独立于平台的 Module::Build 之间进行选择,从而允许以一致的方式安装模块。使用 ExtUtils::MakeMaker 时,您可以使用“make dist”创建您的包。存在一些工具可帮助您以 MakeMaker 友好的方式构建模块。其中包括 ExtUtils::ModuleMaker 和 h2xs。另请参阅 perlnewmod

许可

确保你的模块有许可证,并且其全文包含在发行版中(除非它是常见的,并且许可证条款不要求你包含它)。

如果你不知道使用什么许可证,那么在 GPL 和 Artistic 许可证(与 Perl 自身相同)下进行双重许可是一个好主意。请参见 perlgplperlartistic

常见陷阱

重复造轮子

CPAN 已经非常非常完善地服务于某些应用程序空间。模板系统就是一个示例,日期和时间模块是另一个示例,还有很多其他示例。虽然编写这些内容的自己的版本是一种惯例,但请仔细考虑 Perl 世界是否真的需要你发布它。

试图做太多事情

你的模块将成为开发人员工具包的一部分。它本身不会形成整个工具包。在你的代码成为一个整体系统而不是一组模块化构建块之前,添加额外功能很诱人。

不适当的文档

不要陷入为错误受众写作的陷阱。你的主要受众是具有至少中等程度理解你的模块的应用程序域的经验丰富的开发人员,他们刚刚下载了你的模块,并希望尽快开始使用它。

教程、最终用户文档、研究论文、常见问题解答等不适用于模块的主要文档。如果你真的想编写这些内容,请将它们作为子文档包含在内,例如 My::Module::TutorialMy::Module::FAQ,并在主要文档的另请参阅部分中提供链接。

另请参阅

perlstyle

通用 Perl 样式指南

perlnewmod

如何创建新模块

perlpod

POD 文档

podchecker

验证你的 POD 是否正确

打包工具

ExtUtils::MakeMakerModule::Build

测试工具

Test::SimpleTest::InlineCarp::AssertTest::MoreTest::MockObject

https://pause.perl.org/

Perl 作者上传服务器。包含模块作者信息链接。

任何一本优秀的软件工程书籍

作者

Kirrily "Skud" Robert <[email protected]>