NAME
Book::Chinese::MasterPerlToday::Moose - Moose, OO
DESCRIPTION
Perl 的面对对象系统很强大(Moose 是由 Perl 编写),但并不是很好使用。建议使用 Moose。
学习 Moose 最好的方法是阅读 Moose::Manual。
对象系统
如果没有 Moose,Perl5 的 OO 应该是比较繁琐的。
package Person;
use strict;
use warnings;
sub new {
my $class = shift;
my %p = ref $_[0] ? %{ $_[0] } : @_;
return bless \%p, $class;
}
基本上你在写一些无用的东西,没有任何一行代码是跟实际应用的代码有关的。bless
是什么?这样的代码让人很迷惑。
Moose 的意义在于它可以让你专注于写什么(what),而不是怎么写(how)。
package Person;
use Moose;
概念
Class 类
简单的讲,所以调用 use Moose 的包(package)都可以看做一个类。
更简单的,一个类是由属性和方法组成。还有 Role, Constructor 和 Destructor 等。
Attribute 属性
属性是一个类所拥有的性质。比如人都有一个名字。
你可以简单的认为是类似 $self->{something} 的东西。
一个属性最基本的用途是可读写。CPAN 上拥有上百个创建 Accessor 的模块。其中包括最流行的 Class::Accessor 和 Class::Accessor::Fast。Moose 不是最快的(Class::XSAccessor 目前来说最快),但却是最强大的。
Moose 的属性还拥有 delegation, lazy, clearer, predicate 等功能。
Method 方法
方法与其他地方相同,由 sub 关键字定义。方法与属性最大区别是它往往是动词而属性一般是名词。
Role
Role 与 use base 或者 extends 不同。with 'Role' 类似于将 Role 里的整个代码复制到 with 的模块里。它不显示在继承结构里。
Method modifiers
Type
类型主要用于变量检测。
Constructor & Destructor
原来的 Constructor 由 sub new 和 bless 组成。Moose 为你编写了一个 new, 如果你需要在初始化里做什么的话,可以编写
BUILD
, 该方法会在 new 后调用。或者如果你想支持对 new 进来的参数做更改的话,使用BUILDARGS
.原来的 Destructor 由 DESTROY 控制。Moose 中一般使用
DEMOLISH
查看 Moose::Object 源码可获得内部解释。
META
meta 是描述一个类的类,由
Class::MOP
提供。Moose 在 Class::MOP 的基础上提供了更多的功能。
通过 meta, 你可以得到一个包的内在细节,包括它的父类,所有属性方法等。不仅仅如此,你还可以动态增减属性方法,修改父类等等。
细节例子
no Moose
和 immutabilizepackage Person; use Moose; # extends, roles, attributes, etc. # methods no Moose; __PACKAGE__->meta->make_immutable; 1;
我把这个写在最前面是因为这是当你写完 use Moose; 之后就应该马上要写下来的。
还有
no Moose::Util::TypeConstraints; no Moose::Role;
default 和 builder
default 和 builder 都用于设置一个属性的初始化变量。
如果值是一个非引用的话,建议使用 default
default => 'apple'
当值是引用时,必须放在 sub 里
default => {}, # Wrong default => sub { {} } # Right, 默认空数组 sub { [] }
一般在匿名子程序时,推荐用 builder
builder => '_build_size', sub _build_size { return ( 'small', 'medium', 'large' )[ int( rand 3 ) ]; }
因为 builder 所调用的是一个方法,所以拥有两大好处。一是方法可以在子类中被覆盖,另外方法还可以由 Role 提供。具有更好的可控和扩展性。
handles
delegation 委托,委派。你可以将你自己所需要写的一些 subs 委托给其他的模块来处理,这样外部看起来就像这些 subs 就是属于这个包的。
package A; use Moose; has 'ua' => ( is => 'rw', isa => 'LWP::UserAgent', lazy => 1, default => sub { LWP::UserAgent->new }, handles => ['get', 'post', 'head', 'request'] );
使用
handles
后,A->new 之后可以直接调用 get, post 等方法。 A->new->get 等同于 A->new->ua->gethandles 接受 ARRAY | HASH | REGEXP | ROLE | DUCKTYPE | CODE, 上面的例子是 ARRAY。
HASH 可以让你使用不同的 sub 名字。比如
handles => { get_url => 'get' }
这种用法在当你的模块里已经有一个 get 的时候就非常有用。sub get 是你原来的 get, 而 A->new->get_url = A->new->ua->get
其他的诸如 REGEXP, ROLE, CODE, DUCKTYPE 都有点高级,在有需要的时候可以去 moose@perl.org 或者 irc.perl.org #moose 咨询。
init_arg
package AAAA; use Moose; has 'bigness' => ( is => 'ro', init_arg => 'size', );
初始化的时候就要用 AAAA->new( size => 1 );
如果你不允许一个变量在 new 里被初始化,可以参考如下:
has '_genetic_code' => ( is => 'ro', lazy_build => 1, init_arg => undef, );
around, before 和 after
一般如果只是简单的修改,我们可以使用
before
和after
, 但是这两个拥有很大的限制性。第一,他们两个不能修改 @_, 第二,他们不能中途退出(不能用 return 停止调用,除非 die),第三是他们的返回值会被忽略掉。
如果遇到以上三种情况,可以使用
around
具体的代码解释可以参考 Class::MOP::Method::Wrapped
inner 和 augment, override 和 super
inner
和augment
一般用于基础的父类定义通用代码,而继承的子类定义不同的代码。package Document; use Moose; sub as_xml { my $self = shift; my $xml = "<document>\n"; $xml .= inner(); $xml .= "</document>\n"; return $xml; } package Report; use Moose; extends 'Document'; augment 'as_xml' => sub { my $self = shift; my $xml = "<report>\n"; $xml .= inner(); $xml .= "</report>\n"; return $xml; }; package Report::IncomeAndExpenses; use Moose; extends 'Report'; augment 'as_xml' => sub { my $self = shift; my $xml = '<income>' . $self->income . '</income>'; $xml .= "\n"; $xml .= '<expenses>' . $self->expenses . '</expenses>'; $xml .= "\n"; $xml .= inner() || q{}; return $xml; };
可以在 argment 里再次调用 inner.
override
和super
与普通的$self->SUPER::display_name
并无特别大的不同。Role exclude/alias
当你使用两个或多个 Role 的时候,因为 Role 是平行无序的进入主模块的,所以如果有两个 Role 有相同的方法名字的时候,主模块就不知道采用哪个 Role 的方法。
这个时候,如果某个 Role 里的方法有用而另一个方法没用的时候,我们一般用 exclude
with 'Net::GitHub::V2::NoRepo' => { excludes => [ 'args_to_pass' ] };;
如果两个 Role 的方法都有用的时候,一般用 alias
with 'Breakable' => { alias => { break => 'break_bone' } }, 'Breakdancer' => { alias => { break => 'break_dance' } };
-
一般建议使用 MooseX::Types 而不是内置的 Moose Type。
Moose Type 是全局类型,当你使用多个外部包的时候就有可能会造成类型冲突(除非所有的外部包都是用各自的 'packageA.name.' 作为前缀)。
MooseX::Types
是属于当前包变量的类型,不容易冲突。Moose Type 是放在引号之内如 'Int',这种检测是在运行时检测,出错比较难发现。而 MooseX::Types 是不放在引号里的(如 Int),这种在编译时就会检测。
MooseX::Types 拥有更多类型,更容易扩展。常见的如 MooseX::Types::Path::Class
Roles 和 Traits
Traits 其实就是 Role。它们的区别在于:
Traits 可以拥有一个短名字
通常在编译时使用的是 Role,在运行时使用的是 Trait
这意味这 Role 是影响了整个 Class,而 Trait 仅仅影响了 Class 的某个实例(instance)
如果你的模块允许用户配置来使用哪些 traits 的话,一般参考 MooseX::Traits
MooseX
-
MooseX::Declare 可以说是当前 Perl5 最前沿和激动人心的模块。
它通过 Devel::Declare 定义了一些类似 Perl6 的关键字,使得你写代码的时候更加简洁和方便。
-
Devel::REPL 是 MooseX::Object::Pluggable 的一个绝好例子。开发过程的介绍(需要爬墙工具):
Moose 所开发的软件
但你在写一个小脚本的时候,你可能不会觉得 Moose 怎么有用。但当你写一个很大的系统的时候,你会由衷地为自己选择 Moose 而感到幸运。
-
Catalyst 应当是使用 Moose 最广的例子。而 Reaction 应该是最复杂的例子。
-
Dist::Zilla 拥有一个与 MooseX::Object::Pluggable 不同的写 Plugins 的技巧。
$_->before_build for $self->plugins_with(-BeforeBuild)->flatten; $_->gather_files for $self->plugins_with(-FileGatherer)->flatten; $_->prune_files for $self->plugins_with(-FilePruner)->flatten; $_->munge_files for $self->plugins_with(-FileMunger)->flatten; $_->setup_installer for $self->plugins_with(-InstallTool)->flatten; $_->after_build({ build_root => $build_root }) for $self->plugins_with(-AfterBuild)->flatten;
代码使用不同的 Role 来依次执行 Plugins,简洁而有效。
More
http://cpants.perl.org/dist/used_by/Moose
不要因为担心 Moose 的启动速度而放弃它,这点速度是绝对物有所值的。
资源
主页:http://www.iinteractive.com/moose/
有很多的演讲幻灯片
邮件列表
发送至 moose-subscribe@perl.org 订阅。网页浏览地址:http://news.gmane.org/gmane.comp.lang.perl.moose
Git: git clone git://jules.scsys.co.uk/gitmo/Moose.git
IRC: irc.perl.org#moose
SEE ALSO
AUTHOR
Fayland Lam, <fayland at gmail.com>
COPYRIGHT & LICENSE
Copyright (c) 2009 Fayland Lam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.