使用 Moose 程式設計/解決的問題/型別系統
Perl 缺少,而 Moose 擁有。 至少,不是那麼多。
回到 Perl 的問題,更具體地說,是物件系統沒有涵蓋的另一個特性,而是作為 Moose 的恩賜提供的。所以在這個例子中,你正在編寫一個程式,就像一個優秀的程式設計師一樣,你所有的 URL 都由URI 物件處理,你所有的方法都對URI 物件進行操作 - 或者至少它們認為它們是在對URI 物件進行操作。讓方法依賴於URI 物件是一件好事。相信我,它很好,另一種選擇就是天堂-1。不幸的是,當你將模組釋出到一個充滿智力低下的人的世界時,你沒有簡便的方法來確保他們只發送 URI。[1]
這又是 Perl 缺少的地方。所有 URL 都儲存為URI 物件,但你無法輕鬆地確保它們僅為URI 物件。
思考一下符號的作用。符號的副作用之一是,函式在編譯時就知道傳送給它的輸入是否在正確的範圍內。Perl 的符號有點更像巫術,因為有時它們可以作為運算子來改變值。[2] 讓我們看看 Perl 如何處理輸入驗證。
my %foo = ( foo => bar );
# This will not compile because the CORE::values function
# only operates on a hash, and not an array as indicated
# by the sigil. See perl's prototypes for more info.
print for values @foo;
在下面的例子中,類 HashOperations 期望一個 HashObject
my $foo = HashObject->new;
$foo = 'stupid_mistake';
## Stupid non-intelligent error will happen here
## because the user made a stupid mistake.
HashOperations->load( $foo )->values;
所以這裡的問題是,過程式的非引用形式將在編譯時被捕獲,而所有其他物件變體都不會。
解決這個問題的舊的 Perl 方法實際上是在嘲笑使用者。核心團隊寧願讓程式設計師使用模組化的語言外掛,而不是向核心新增特定功能,或者對其進行徹底修改,這樣生產力就完全依賴於外掛。[3] 我們可以推測一下缺乏型別約束功能的原因。
- Perl 沒有其他解釋型語言可以作為模型,更不用說具有基本物件型別的解釋型語言了。
- Perl 的引用早於其物件。
- 一個雜湊是一個雜湊(標量的雜湊),一個陣列是一個數組(標量的陣列)。這其中很大一部分可以透過使用 Perl 的符號在編譯時確定。而物件完全是在執行時發生的,並且沒有被賦予唯一的符號。
在這組例子中,我們將展示為什麼 Class::Accessor 在建立訪問器方面自動地比 Moose 遜色,因為 Moose 能夠指定有效的型別。[4]
這裡我們建立一個自定義資料型別,即 NonMooseObject
package NonMooseObject;
use strict;
use warnings;
use base 'Class::Accessor';
BEGIN { __PACKAGE__->mk_accessors( 'uri' ) };
sub new {
my ( $class, $hash ) = @_;
my $self = bless $hash || {}, $class;
$self;
}
這是你希望使用者做的事情
package main;
my $uri = URI->new( 'http://moose.com' );
my $obj = NonMooseObject->new({ uri => $uri });
$obj->uri; ## prints php.
$obj->uri->path; ## joy
這是你不想讓使用者做的事情。在這個例子中,我們展示了 NonMooseObject 如何容易被濫用。有一件很重要的事情很難在例子中展示:死亡可能會也可能不會在呼叫->new時發生。希望,對於使用者和程式設計師來說,它是在呼叫 new 時死亡的;但是,假設你運行了 42 天,然後一些內部的東西在$obj->uri上呼叫->path,而->uri不包含 URI 物件。結果就是可能會更難除錯的錯誤。
package main;
my $obj = NonMooseObject->new({ uri => 'http://perl.com' });
$obj->uri; ## prints php.
$obj->uri->path; ## dies a horrid runtime death.
| 問題 | 在 Perl 的 Moose 時代之前,你如何解決這個問題? |
|---|---|
| 答案 | 我們降級了,失去了Class::Accessor的實用性。 |
我們告訴自己,這個功能,更加高階和細化,並不需要在所有地方都使用。為了實現這個功能,你必須首先將你的模組和C::A分離。所以,這個解決方案不僅很醜陋,而且你現在又要編寫訪問器了。嬰兒耶穌在這個時候哭了。
sub set_stupid {
my ( $self, $uri ) = @_;
die "bad uri" unless ref $uri eq 'URI';
$self->{uri} = $uri;
}
如果你發現自己在讀完這本書後使用 ref,你需要立即停止程式設計,找到一個新的職業。 |
回顧過去既枯燥又令人不安,所以現在讓我們展望未來,走向光明。
package MooseObject;
use Moose;
has 'uri' => ( is => 'rw', isa => 'URI', required => 1 );
不再需要其他例子了。你現在要麼向建構函式傳送一個URI 物件,要麼你就會死掉,並伴隨著一個堆疊跟蹤和 Moose 的預設訊息,告訴你你需要做什麼。