第七章 對(duì)象(三)-反射、對(duì)象進(jìn)階

反射

反射是指計(jì)算機(jī)程序在運(yùn)行時(shí)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。通過將代碼視為數(shù)據(jù),這樣就能像管理數(shù)據(jù)一樣管理代碼。聽起來可能并沒什么新奇的,但是這是對(duì)現(xiàn)代編程的深刻洞察,也是代碼生成背后所遵循的原則。

檢查模塊是否被加載

如果你知道模塊名字,你就可以通過查看%INC來檢測模塊是否被加載。當(dāng)使用use或require加載代碼時(shí),Perl會(huì)在%INC中保存記錄:鍵是模塊的文件路徑,值是該模塊的在磁盤上的全路徑。舉個(gè)例子,加載 Modern::Perl時(shí):

$INC{'Modern/Perl.pm'} = '.../lib/site_perl/5.12.1/Modern/Perl.pm';

#具體路徑依賴你的安裝路徑,可能和這里不同。

要測試是否加載一個(gè)模塊。就是看是否存在該鍵:

sub module_loaded
{
(my $modname = shift) =~ s!::!/!g;
return exists $INC{ $modname . '.pm' };
}

任何地方任何代碼都可以操縱%INC。有時(shí)候這也有好處,比如 Test::MockObject和Test::MockModule就使用了該特性。

CPAN模塊Class::Load的is_class_loaded()函數(shù)也能檢測模塊的加載。

檢測一個(gè)包是否存在

任何繼承UNIVERSAL的包都會(huì)提供一個(gè)can()方法。如果包不存在,Perl會(huì)拋出異常(無效的調(diào)用者),可以使用eval來包裝下:

say "$pkg exists" if eval { $pkg->can( 'can' ) };

還有一種檢測方法就是通過Perl的符號(hào)表。

檢測一個(gè)類是否存在

因?yàn)樵赑erl中沒辦法嚴(yán)格的區(qū)分包和類,所以你可以使用檢測包的方法來檢查類,但是這不一定準(zhǔn)確。

檢測一個(gè)模塊的版本

模塊并非一定要提供版本號(hào),但是每一個(gè)包都會(huì)從UNIVERSAL類繼承VERSION()方法.

my $version = $module->VERSION;

VERSION() 返回模塊的版本號(hào)(如果有);否則返回undef;如果模塊不存在也返回undef。

檢測一個(gè)函數(shù)是否存在

通過將傳參給can()函數(shù)來檢測函數(shù)是否存在:

say "$func() exists" if $pkg->can( $func );

要注意的是在包沒有預(yù)先聲明或者重寫了can()函數(shù)的情況下,在AUTOLOAD()里實(shí)現(xiàn)的函數(shù)可能檢測不出來。

使用這個(gè)技術(shù)可以偵測模塊的import()方法是否往當(dāng)前名字空間里導(dǎo)入了一個(gè)函數(shù):

say "$func() imported!" if __PACKAGE__->can( $func );

檢測一個(gè)方法是否存在

暫時(shí)沒有萬無一失的方法來區(qū)分函數(shù)和方法。

去符號(hào)表里翻翻

符號(hào)表是一個(gè)特殊的哈希,鍵都是(包全局)符號(hào)的名字,值是類型團(tuán)。類型團(tuán)是一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),它可以包含一個(gè)標(biāo)量、一個(gè)數(shù)組、一個(gè)哈希、一個(gè)文件句柄、一個(gè)函數(shù),也可可以一次性包含所有這些。
在perldoc perlmod中可以查看有關(guān)符號(hào)表的細(xì)節(jié)。如果你真的想操作符號(hào)表和類型團(tuán),試試CPAN上的Package::Stash模塊。

OO進(jìn)階

通過使用Moose,創(chuàng)建和使用面向?qū)ο笞兊梅浅:唵?。但是設(shè)計(jì)一個(gè)好的程序就沒這么容易了,過度設(shè)計(jì)或設(shè)計(jì)得過少都不好。只有實(shí)踐經(jīng)驗(yàn)?zāi)軒椭阏莆兆钪匾脑O(shè)計(jì)方法(技術(shù)),但這里仍然有些原則能夠幫助到你:

優(yōu)先使用組合而非繼承

新手通常會(huì)過度使用繼承特性,導(dǎo)致的結(jié)果就是類的層次變得很深,相互作用的關(guān)系變得復(fù)雜,這樣的代碼極難維護(hù)--誰知道該在哪個(gè)地方添加和修改?這處的代碼和別處的代碼發(fā)生沖突了會(huì)怎么樣?

繼承只是面向?qū)ο缶幊痰钠渲幸环N工具,而且通常不是一個(gè)好的工具。
Car(車子)可以從Wheel(輪子)繼承而來,但是更好的選擇是Car(車子)包含幾個(gè)Wheel(輪子)屬性。

單一責(zé)任原則
當(dāng)你設(shè)計(jì)一個(gè)對(duì)象時(shí)要考慮讓對(duì)象的責(zé)任單一。例如,Employee對(duì)象可能有名字、聯(lián)系方式、和其他的一些個(gè)人信息,而Job對(duì)象則是一些職責(zé)相關(guān)的信息。這樣根據(jù)責(zé)任不同就劃分出2個(gè)類:Employee類只需要關(guān)注是誰(個(gè)人信息相關(guān))而不關(guān)心做什么;Job類則只需要關(guān)注做什么(職責(zé)相關(guān))而不關(guān)心是誰來做。(比如2個(gè)員工可以做同一份工作,也可以一個(gè)員工同時(shí)做2份工作)

當(dāng)每個(gè)類都是單一的責(zé)任時(shí),就能減少耦合,提高封裝。

別干重復(fù)的事(Don’t Repeat Yourself)

復(fù)雜性和重復(fù)讓開發(fā)和維護(hù)變得困難。該原則(Don’t Repeat Yourself)提醒我們要找出并消除系統(tǒng)內(nèi)部的重復(fù)部分--重復(fù)的代碼、重復(fù)的數(shù)據(jù)。無論是配置文件還是用戶數(shù)據(jù),或者是其他重要的信息,都要將它們?cè)O(shè)計(jì)成單一的、規(guī)范的表示形式。這個(gè)原則可以幫助你找到系統(tǒng)和數(shù)據(jù)的最佳表示方式,并且能減少因重復(fù)而導(dǎo)致不同步的可能性。

里氏替換原則

里氏替換原則簡單來說就是子類要能符合所以基類的要求,遵循該原則能保證基類被真正復(fù)用。這個(gè)原則很好理解,設(shè)想動(dòng)物相當(dāng)于基類,貓為子類。動(dòng)物要求具有的特性,貓都會(huì)符合,根據(jù)里氏替換原則,在測試套件測試動(dòng)物類時(shí),用貓類來代替也應(yīng)該能通過全部測試項(xiàng)?;惛哂衅毡樾?,子類更具體。在使用基類的地方都能用子類替代,這就是里氏替換原則。

亞型和強(qiáng)制轉(zhuǎn)換

Moose允許你聲明和使用類型(Perl原生數(shù)據(jù)類型),并且還能擴(kuò)展它們使用亞型(自創(chuàng)的類型)來形成更專業(yè)的數(shù)據(jù)表述。所有類型都有助于表達(dá)你的意圖,有助于驗(yàn)證數(shù)據(jù)和方法是否適配。在必要時(shí)明確指定數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)換。

例如,你期望一個(gè)日期類型的數(shù)據(jù),但是外界輸入通常是字符串類型。這時(shí)你可以自己新建一個(gè)日期的類型(這個(gè)類型是自創(chuàng)的,Perl語言哪有這種類型?。?,并且增加從字符串到該日期類型的強(qiáng)制轉(zhuǎn)換。這樣就更容易讓人明白你的代碼意圖。

Moose::Util::TypeConstraints 和MooseX::Types有這方面的細(xì)節(jié)信息。

不變性

一個(gè)精心設(shè)計(jì)的對(duì)象,你只需要告訴它該做什么,而不是怎么去做。如果你發(fā)現(xiàn)自己需要從對(duì)象之外接觸內(nèi)部數(shù)據(jù)(即使是通過訪問器、設(shè)置器),就要意識(shí)到可能封裝得不夠。

你可以讓對(duì)象不可改變,來阻止這種不恰當(dāng)?shù)慕佑|。在構(gòu)造方法中提供必要的數(shù)據(jù),然后阻止一切來自外部的修改。一旦你構(gòu)建了這樣的對(duì)象,你就知道它總是有效的,因?yàn)槟銦o法修改它的數(shù)據(jù)讓它變到無效的狀態(tài)。

這需要很強(qiáng)的自律,但是由此產(chǎn)生的系統(tǒng)是可靠的、可測試、可維護(hù)的。有些設(shè)計(jì)甚至還盡可能禁止內(nèi)部修改,當(dāng)然這更難實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容