Spiffy - 適合您的 Spiffy Perl 介面框架
package Keen; use Spiffy -Base; field 'mirth'; const mood => ':-)'; sub happy { if ($self->mood eq ':-(') { $self->mirth(-1); print "Cheer up!"; } super; }
「Spiffy」是在 Perl 中進行物件導向 (OO) 程式設計的框架和方法。 Spiffy 將 Exporter.pm、base.pm、mixin.pm 和 SUPER.pm 的最佳部分結合到一個神奇的基礎類別中。它試圖以一種乾淨、直接和(也許有一天)標準的方式修復傳統 Perl OO 的所有缺陷。
Spiffy 借鑒了其他 OO 語言(如 Python、Ruby、Java 和 Perl 6)的想法。
如果你看一下 CPAN,你會發現有大量與 OO 相關的模組。當開始一個新專案時,您需要選擇最有意義的模組集,然後需要在每個類別中使用這些模組。另一方面,Spiffy 在一個模組中擁有您可能需要的所有內容,並且您只需在一個類別中使用它一次。如果您將 Spiffy.pm 作為專案中最基礎類的基類,那麼 Spiffy 會自動將其所有魔力傳遞給您的所有子類。您最終可能會忘記您正在使用它!
Spiffy 和其他 Perl 物件導向基底類別之間最顯著的差異是它具有導出內容的能力。如果您建立 Spiffy 的子類,則除了您想要匯出的任何其他內容之外,Spiffy 匯出的所有內容都會自動由您的子類別匯出。如果有人創建了您的子類的子類,所有這些內容都會自動導出,依此類推。將其視為“繼承導出”,它使用熟悉的 Exporter.pm 規範語法。
若要使用 Spiffy 或 Spiffy 的任何子類別作為類別的基類,請為use
指令指定-base
參數。
use MySpiffyBaseModule -base;
您也可以使用傳統的use base 'MySpiffyBaseModule';
語法和一切都將完全相同。唯一要注意的是 Spiffy.pm 必須已經載入。這是因為 Spiffy 動態地重新連接 base.pm 來完成 Spiffy 的所有魔法。
Spiffy 支援類似 Ruby 的 mixin 和類似 Perl6 的角色。就像base
一樣,您可以使用以下任一呼叫:
use mixin 'MySpiffyBaseModule'; use MySpiffyBaseModule -mixin;
只有當混合的類別是 Spiffy 的子類別時,第二個版本才有效。只要 Spiffy 已經加載,第一個版本就可以在所有情況下工作。
若要限制混合的方法,請使用角色。 (提示:它們的運作方式就像出口商清單):
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
在物件導向的 Perl 中,幾乎每個子程式都是一個方法。每個方法都取得傳遞給它的物件作為其第一個參數。這意味著幾乎每個子程式都以以下行開頭:
my $self = shift;
Spiffy 提供了一個簡單的、可選的過濾機制來為您插入該行,從而產生更清晰的程式碼。如果您認為平均方法有 10 行程式碼,那麼這就是您程式碼的 10%!要開啟此選項,只需使用- Base
選項而不是-base
選項,或新增-selfless
選項。如果來源過濾讓您感到不安,請不要使用該功能。我個人發現它在我追求編寫乾淨、可維護的程式碼的過程中讓人上癮。
Spiffy 的一個有用功能是它會匯出兩個函數: field
和const
,可用於宣告類別的屬性,並自動為它們產生存取器方法。兩個函數唯一的差別是const
屬性不能修改;因此訪問器速度要快得多。
OO 程式設計的一個有趣的方面是當一個方法從父類別呼叫相同的方法時。這通常稱為呼叫超級方法。 Perl 執行此操作的工具非常醜陋:
sub cleanup { my $self = shift; $self->scrub; $self->SUPER::cleanup(@_); }
Spiffy 使得呼叫超級方法變得非常容易。你只需使用super
功能即可。您不需要向它傳遞任何參數,因為它會自動為您傳遞它們。這是與 Spiffy 相同的函數:
sub cleanup { $self->scrub; super; }
Spiffy 有一個特殊的方法來解析參數,稱為parse_arguments
,它也用來解析自己的參數。您可以使用兩個稱為boolean_arguments
和paired_arguments
的特殊方法來聲明哪些參數是布林值(單例)以及哪些參數是配對的。解析參數提取布林值和對,並以匿名雜湊形式傳回它們,後面跟著不匹配參數的列表。
最後,Spiffy 可以匯出一些偵錯函數WWW
、 XXX
、 YYY
和ZZZ
。它們每個都會產生其參數的 YAML 轉儲。 WWW 警告輸出,XXX 死亡並輸出,YYY 列印輸出,ZZZ 承認輸出。如果 YAML 不適合您的需求,您可以使用-dumper
選項將所有轉儲切換為 Data::Dumper 格式。
那是斯皮菲!
Spiffy 在 Perl 中實現了一個全新的想法。既充當物件導向的類別又導出函數的模組。但它使 Exporter.pm 的概念更進一步;它遍歷類別的整個@ISA
路徑並遵循每個模組的導出規範。由於 Spiffy 呼叫 Exporter 模組來執行此操作,因此您可以使用 Exporter 擁有的所有精美介面功能,包括標籤和否定。
Spiffy 考慮所有不以破折號開頭的參數來構成導出規範。
package Vehicle; use Spiffy -base; our $SERIAL_NUMBER = 0; our @EXPORT = qw($SERIAL_NUMBER); our @EXPORT_BASE = qw(tire horn); package Bicycle; use Vehicle -base, '!field'; $self->inflate(tire);
在這種情況下, Bicycle->isa('Vehicle')
以及Vehicle
和Spiffy
導出的所有內容都將進入Bicycle
,除了field
。
當您設計了一個包含數百個類別的系統,並且您希望它們都可以存取某些函數或常數時,匯出會非常有用
or variables. Just export them in your main base class and every subclass
將獲得他們需要的功能。
您幾乎可以完成 Exporter 所做的所有事情,因為 Spiffy 將這項工作委託給 Exporter(在添加一些 Spiffy 魔法之後)。 Spiffy 提供了一個@EXPORT_BASE
變量,它類似於@EXPORT
,但僅適用於使用-base
的用法。
如果您在 Perl 中做過很多 OO 編程,那麼您可能使用過多重繼承 (MI),如果您做過很多 MI,您可能會遇到奇怪的問題和頭痛。某些語言(例如 Ruby)嘗試使用稱為 mixins 的技術來解決 MI 問題。基本上,所有 Ruby 類別僅使用單一繼承 (SI),然後根據需要混合其他模組的功能。
Mixin 可以簡單地被認為是將另一個類別的方法匯入到您的子類別中。但從實施的角度來看,這並不是最好的方法。 Spiffy 做 Ruby 所做的事情。它會建立一個空的匿名類,將所有內容匯入到該類中,然後將新類別連結到您的 SI ISA 路徑中。換句話說,如果你說:
package AAA; use BBB -base; use CCC -mixin; use DDD -mixin;
您最終會得到一個類別的單一繼承鏈,如下所示:
AAA << AAA-DDD << AAA-CCC << BBB;
AAA-DDD
和AAA-CCC
是產生的類別的實際套件名稱。這種風格的好處是,在 CCC 中混合不會破壞 AAA 中的任何方法,並且 DDD 也不會與 AAA 或 CCC 衝突。如果您在 CCC 中混合了 AAA 中的方法,您仍然可以使用super
來獲取它。
當 Spiffy 在 CCC 中混合時,它會引入 CCC 中所有不以下劃線開頭的方法。事實上,事情遠不止於此。如果 CCC 是子類,它將引入 CCC can
透過繼承執行的所有方法。這非常強大,甚至可能太強大了。
為了限制您混合的內容,Spiffy 借用了 Perl6 中的角色概念。不過,「角色」一詞在 Spiffy 中的使用更為寬鬆。它很像 Exporter 模組使用的導入列表,您可以使用群組(標籤)和否定。如果清單的第一個元素使用否定,Spiffy 將從 mixin 類別可以執行的所有方法開始。
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
在此範例中, walk
和run
是 EEE 可以執行的方法, tools
和sharp_tools
是 EEE 類別的角色。 EEE 類別如何定義這些角色?它非常簡單地定義了名為_role_tools
和_role_sharp_tools
的方法,它們傳回更多方法的清單。 (可能還有其他角色!)這裡的巧妙之處在於,由於角色只是方法,因此它們也可以被繼承。以 Perl6為例!
透過使用-Base
標誌而不是-base
您永遠不需要編寫以下行:
my $self = shift;
使用來源過濾器將此語句新增到類別中的每個子程式中。其神奇之處在於簡單且快速,因此創建與 Ruby 和 Python 同等的乾淨程式碼的效能損失很小。
package Example; use Spiffy -Base; sub crazy { $self->nuts; } sub wacky { } sub new() { bless [], shift; }
與以下完全相同:
package Example; use Spiffy -base; use strict;use warnings; sub crazy {my $self = shift; $self->nuts; } sub wacky {my $self = shift; } sub new { bless [], shift; } ;1;
請注意,子程式new
之後的空括號阻止其添加 $self。另請注意,額外的程式碼將添加到現有行中,以確保行號不會更改。
-Base
還打開嚴格和警告編譯指示,並添加煩人的“1;”線到你的模組。
當您使用“-Base”過濾器機制時,Spiffy 現在支援私有方法。您只需使用my
關鍵字聲明 subs,並在前面加上'$'
來呼叫它們。像這樣:
package Keen; use SomethingSpiffy -Base; # normal public method sub swell { $self->$stinky; } # private lexical method. uncallable from outside this file. my sub stinky { ... }
XXX 函數對於偵錯來說非常方便,因為您幾乎可以將它插入到任何地方,並且它會將您的資料轉儲到乾淨的 YAML 中。採取以下聲明:
my @stuff = grep { /keen/ } $self->find($a, $b);
如果您對該語句有問題,可以透過以下任何一種方式進行偵錯:
XXX my @stuff = grep { /keen/ } $self->find($a, $b); my @stuff = XXX grep { /keen/ } $self->find($a, $b); my @stuff = grep { /keen/ } XXX $self->find($a, $b); my @stuff = grep { /keen/ } $self->find(XXX $a, $b);
XXX 易於插入和移除。用 XXX 標記不確定的程式碼區域也是一種傳統。如果您忘記取出調試轉儲器,這將使它們很容易被發現。
WWW 和 YYY 很好,因為它們轉儲參數然後返回參數。透過這種方式,您可以將它們插入到許多地方,並且仍然可以像以前一樣運行程式碼。當您需要使用 YAML 轉儲和完整堆疊追蹤結束時,請使用 ZZZ。
如果使用-base
選項,則預設會匯出偵錯函數,但前提是您之前使用過-XXX
選項。若要匯出所有 4 個函數,請使用匯出標籤:
use SomeSpiffyModule ':XXX';
要強制調試功能使用 Data::Dumper 而不是 YAML:
use SomeSpiffyModule -dumper;
本節介紹 Spiffy 導出的函數。只有在使用-base
或-Base
選項時,才會匯出field
、 const
、 stub
和super
函數。
場地
為類別的欄位定義存取器方法:
package Example; use Spiffy -Base; field 'foo'; field bar => []; sub lalala { $self->foo(42); push @{$self->{bar}}, $self->foo; }
傳遞給field
第一個參數是正在定義的屬性的名稱。可以為存取器指定一個可選的預設值。如果物件中未設定該欄位的值,則將傳回該值。
常量
const bar => 42;
const
函數與 <field> 類似,只是它是不可變的。它也不在物件中儲存資料。您可能總是想給const
一個預設值,否則產生的方法將有些無用。
存根
stub 'cigar';
stub
函數產生一個方法,該方法將帶有適當的訊息而結束。這個想法是子類別必須實作這些方法,這樣存根方法就不會被呼叫。
極佳的
如果在呼叫此函數時不帶任何參數,它將呼叫 ISA 樹中較高層的相同方法,並向其傳遞所有相同的參數。如果使用參數呼叫它,它將使用前面帶有$self
這些參數。換句話說,它就像您期望的那樣工作。
sub foo { super; # Same as $self->SUPER::foo(@_); super('hello'); # Same as $self->SUPER::foo('hello'); $self->bar(42); } sub new() { my $self = super; $self->init; return $self; }
如果沒有 super 方法, super
將不執行任何操作。最後, super
在 AUTOLOAD 子程式中做了正確的事情。
本節列出了 Spiffy 的任何子類別自動繼承的所有方法。
混合
在運行時混合類別的方法。採用與use mixin ...
相同的參數。使目標類別成為呼叫者的混合。
$self->mixin('SomeClass'); $object->mixin('SomeOtherClass' => 'some_method');
解析參數
此方法採用參數清單並將它們分組。它允許布林參數,該參數可能有值也可能沒有值(預設為 1)。該方法傳回所有對的哈希引用作為哈希中的鍵和值。任何無法配對的參數都會作為列表返回。這是一個例子:
sub boolean_arguments { qw(-has_spots -is_yummy) } sub paired_arguments { qw(-name -size) } my ($pairs, @others) = $self->parse_arguments( 'red', 'white', -name => 'Ingy', -has_spots => -size => 'large', 'black', -is_yummy => 0, );
在此呼叫之後, $pairs
將包含:
{ -name => 'Ingy', -has_spots => 1, -size => 'large', -is_yummy => 0, }
@others
將包含“紅色”、“白色”和“黑色”。
布林參數
傳回被識別為布林值的參數清單。重寫此方法來定義您自己的清單。
配對參數
傳回被辨識為配對的參數清單。重寫此方法來定義您自己的清單。
當您use
Spiffy 模組或其子類別時,您可以向其傳遞參數清單。這些參數使用上述parse_arguments
方法進行解析。特殊參數-base
,用於使目前套件成為正在使用的 Spiffy 模組的子類別。
任何不成對的參數都像普通的導入列表一樣;就像與 Exporter 模組一起使用的那些一樣。
使用 Spiffy 模組作為基底類別的正確方法是在use
語句中使用-base
參數。這與您想要use base
典型模組不同。
package Something; use Spiffy::Module -base; use base 'NonSpiffy::Module';
現在可能很難區分什麼是 Spiffy,什麼不是。因此 Spiffy 實際上是為了與 base.pm 一起工作而設計的。你可以說:
package Something; use base 'Spiffy::Module'; use base 'NonSpiffy::Module';
當您的類別不是實際的模組(單獨的檔案)而只是某個已載入的檔案中的套件時, use base
也非常有用。無論該類別是否是模組, base
都會起作用,而-base
語法則不能那樣工作,因為use
總是嘗試載入模組。
為了讓 Spiffy 與 base.pm 一起工作,人們玩了一個骯髒的把戲。 Spiffy 將base::import
替換為自己的版本。如果基本模組不是 Spiffy,則 Spiffy 會呼叫原始的 base::import。如果基本模組是 Spiffy,那麼 Spiffy 會做自己的事情。
有兩個注意事項。
必須先載入 Spiffy。
如果未載入 Spiffy 並且在 Spiffy 模組上呼叫use base
,則 Spiffy 將終止並顯示一個有用的訊息,告訴作者閱讀此文件。這是因為 Spiffy 需要事先進行導入交換。
如果您收到此錯誤,只需在程式碼中新增這樣的語句即可:
use Spiffy ();
禁止混合
base.pm
可以採用多個參數。只要所有基底類別都是 Spiffy,或者它們都是非 Spiffy,這都適用於 Spiffy。如果它們混合在一起,斯皮菲就會死。在這種情況下,只需使用單獨的use base
語句。
Spiffy 是在 Perl 中進行 OO 程式設計的絕佳方法,但它仍然是一項正在進行的工作。新的東西將會被添加,而那些效果不好的東西可能會被刪除。
Ingy döt Net <[email protected]>
版權所有 2004-2014。 Ingy döt 網。
該程式是免費軟體;您可以按照與 Perl 本身相同的條款重新分發它和/或修改它。
請參閱http://www.perl.com/perl/misc/Artistic.html