當前位置:編程學習大全網 - 源碼破解 - 什麽是OOP

什麽是OOP

在本文(也是本系列的第五部分)中,Teodor 解釋了什麽是面向對象編程,何時使用它以及它是如何在 Perl 中工作的。面向對象編程(OOP)是壹種強大的編程技術,但它不是萬能藥。優秀的程序員必須理解如何使用它,並且必須知道何時依賴更傳統的編程技術。在 Perl 中使用 OOP 很簡單。與 C++ 和 Java 等限制性更強的 OOP 語言不同,Perl 中的 OOP 很少對程序員施加強制性約束。OOP 是對每壹名程序員的工具箱的必要補充,並且是用於擴展可用 Perl 解決的問題範圍的非常有用的技術。什麽是面向對象編程(OOP)?OOP 是壹種用於解決問題的編程方法或通用方法。與之相反,算法是用於解決特定問題的 特定方法。OOP 天生是壹種強有力的方法;它往往使過程型和函數型編程方法與該問題較少相關,並且除非它與過程型和函數型編程方法的混合對其極其有益,否則它們之間不會很好地結合在壹起。在 Perl 中,這種強大的力量有所減弱,但仍然很好地存在著。 本文討論 Perl 中的 OOP 對比函數型和過程型編程的基本知識,並演示如何在 Perl 程序和模塊中使用 OOP。請記住,本文只是壹篇概述,而不是對 Perl 中 OOP 所有方面的詳盡解釋。這樣的詳盡解釋需要幾本書才能講完,並且已經被寫過幾次了。有關詳細信息,請參閱本文稍後的 參考資料。 究竟什麽是 OOP?OOP 是壹種通過使用對象來解決問題的技術。在編程術語中,對象是這樣壹些實體:它們的特性和行為是解決手頭問題所必需的。這個定義本應該更詳細些,但是做不到,因為當今計算機行業中 OOP 方法的種類之多簡直難以想象。在Perl 編程環境中,OOP 並不是使用該語言所必需的。Perl 版本 5 和更高版本鼓勵使用 OOP,但確切地說不要求這樣做。所有 Perl 庫都是模塊,這意味著它們至少使用 OOP 的基本部分。而且,大多數 Perl 庫都作為對象實現,這意味著用戶必須通過良好定義的接口將它們作為具有特定行為和特性的 OOP 實體來使用。回頁首基本的 OO 編程語言特性通常,有三個語言特性是 OOP 編程語言所必需的。它們是繼承、多態性和封裝。Perl 支持繼承。當壹個對象(子對象)使用另壹個對象作為起點(父對象),並且隨後在需要時修改其特性和行為時,就要用到 繼承。子-父關系是 OOP 所必需的,因為它使得在其它對象基礎上構建對象成為可能。這種重用是使 OOP 成為程序員寵兒的好處之壹。 有兩種繼承:單壹繼承和多重繼承。 單壹繼承要求子對象只有壹個父對象,而 多重繼承更自由(與實際生活壹樣,在編程過程中具有兩個以上的父對象會導致混亂並使子對象難以工作,因此,不要過多使用多重繼承)。盡管兩個或兩個以上的父對象實際上很少見,但 Perl 支持多重繼承。 多態性(來自希臘語,表示“很多形態”)是使壹個對象被看成另壹個對象的技術。這有點復雜,那麽我舉個例子。比方說,您有壹個綿羊牧場,裏面有四只綿羊(綿羊屬),但是您剛剛買了兩只山羊(山羊屬)和壹只德國牧羊犬(犬科犬屬)。您壹***有多少動物?您得把所有的綿羊、山羊和狗加起來,結果是 7 只。其實您剛剛應用了多態性,即為了計算,把三種不同種類的動物當成壹種通用類型(“動物”)對待。如果您把綿羊、山羊和狗當成哺乳動物看待,這就是壹個簡單的信心飛躍。生物學家每天都以這種方式使用多態性,而程序員則以從其它科學領域“竊用”(我是指“重用”)好主意聞名。Perl 完全支持 多態性。但它用得不是很頻繁,因為 Perl 程序員看起來更喜歡用對象特性、而不是通過修改繼承的行為來修改通用行為。這意味著您更可能看到創建三個 IO::Socket::INET 對象的代碼:壹個對象用於在端口 234 接收和發送 UDP 包、壹個對象用於在端口 80 接收 TCP 包,還有壹個對象在端口 1024 發送 TCP 包,而不大會看到對第壹種情況使用 IO::Socket::INET::UDPTransceiver 、對第二種情況使用 IO::Socket::INET::TCPReceiver 而對第三種情況使用 IO::Socket::TCPTransmitter 的代碼。這就象是在生物學術語中說狗和山羊都是哺乳動物,但是山羊屬於山羊屬,而狗屬於犬屬。 OOP 純化論者認為每件事都應該正確分類,但是 Perl 程序員根本不是純化論者。他們往往更不拘束於 OOP 規則,這使得他們在聚會中比 OOP 純化論者更快樂。封裝指的是以這樣壹種方式包含對象行為和特性:除非對象作者允許,否則用戶無法訪問該對象的行為和特性。在這種方式下,對象用戶無法做不準他們做的事,無法訪問不準他們訪問的數據,並且通常是有害數據。Perl 通常用松弛的方法封裝。請參閱 清單1。 回頁首為什麽說 OOP 是壹種強有力的方法?返回到我們最初的 OOP 如何是壹種強有力的方法這壹主題,我們現在可以看到 OOP 結合了幾個關鍵的概念,這使得它很難與過程型和函數型編程(PP 和 FP)混合使用。首先,PP 和 FP 都沒有繼承或類多態性的概念,因為在 PP 和 FP 中根本就沒有類。在 PP 和 FP 中存在封裝,但只在過程型級別,從來不作為類或對象屬性封裝。既然程序員不怕麻煩來使用這些基本的 OOP 工具,那就意味著程序員通常更可能對整個項目使用 OOP,而不是混合不兼容的方法。有人可能爭論說所有程序最終都歸結為指令的過程型執行,因此無論 OOP 程序實現得有多純,每個 OOP 程序都在其函數(也稱為方法)和創建第壹個對象(該對象做其余工作)的代碼中包含過程型代碼。甚至象 Java 那樣接近“純”OOP 的語言都無法避免地需要壹個 main() 函數。因此,看起來 OOP 只是 PP 的壹個子集。但是這種 OOP 向序列指令的歸結和實際為每個操作所執行的匯編程序指令壹樣,都不是系統架構設計師或程序員所關心的事。請記住,OOP 本身只是壹種方法,而不是目的。 OOP 與過程型編程方法合作得不是很好,因為它集中在對象上,而過程型編程基於過程(我們將 過程大致定義為不使用 OOP 技術就可以得到的函數,而將 方法定義為只有在對象中才能得到的函數)。正如方法壹樣,過程只是由用戶調用的函數,但是二者之間有壹些差異。 過程不使用對象數據。必須在它們的參數列表中為它們傳遞數據,或者它們必須使用所在作用域中的數據。過程可以訪問調用它時傳遞給它的任何數據,甚至整個程序的全局數據。方法應該只訪問它們對象的數據。實際上,方法的函數作用域通常是包含該方法的對象。常常發現過程使用全局數據,盡管只有在絕對必要時才應該這樣做。應該盡快重寫使用全局數據的方法。過程通常用幾個參數調用其它過程。方法應該只有幾個參數,並且它們調用其它方法的次數比其它過程更多。函數型編程(FP)與 OOP 配合不好有幾個原因。最重要的原因是 FP 基於用來解決問題的詳細函數型方法,而 OOP 則使用對象來表達概念,並且,與 OOP 方法只能在包含它們的對象中使用不同,FP 過程得到處使用。綜上所述,我們現在可以解釋 Perl 為什麽是混合 OOP、FP 和 PP 方法的最佳語言之壹。回頁首Perl 是如何將 OOP 與過程型和函數型編程結合起來的?Perl 是壹種松弛的語言。它極力讓程序員以他們認為方便的任何方式做他們想做的任何事。這與 Java 和 C++ 之類的語言截然不同。例如,如果程序員原本沒有聲明變量,Perl 樂於允許程序員自動創建變量(盡管不鼓勵這樣做,並且可以通過使用高度推薦的“use strict”編譯指示阻止)。如果您要向自己的腳開槍,Perl 會給您十發子彈和壹個激光瞄準鏡,然後站在壹旁鼓勵您。因此,Perl 是壹種非常便於濫用方法的語言。別害怕。沒關系。例如,訪問內部的對象數據、實時更改類和實時重定義方法都是允許的。Perl 方式是:允許程序員為了編碼、調試和執行效率的目的而去打破規則。如果這有助於完成工作,那麽沒關系。因此,Perl 本身可能是程序員最好的朋友,也可能是最壞的敵人。如果混合 OOP、FP 和 PP 意味著打破規則,那麽為什麽任何人都想要混合 OOP、FP 和 PP 呢?讓我們回頭想想這個問題。什麽是 OOP、FP 和 PP?它們只是現有的為編程團隊服務的編程方法、概念集和規則集。OOP、FP 和 PP 是工具,每名程序員的首要工作就是要了解他的工具。如果壹名程序員在排序散列時不能使用 FP 的 Schwartzian 變換,而是編寫他自己的 Sort::Hashtable ,或者不能重用 Sys::Hostname 模塊,而是編寫過程代碼來獲得系統主機名,那麽這個程序員是在浪費時間、精力和金錢,並且降低了代碼質量和可靠性。 壹個編程團隊可能會因為它們最熟知的工具而沾沾自喜,對它們來說,這可能正是最壞的事。如果壹個團隊只使用象計算機編程行業那樣令人沖動和充滿創新的行業中所保證的可用工具的壹部分,那麽它在幾年之後註定要變得毫無用處。程序員應該能夠結合任何使工作更有效、代碼更好以及使團隊更具創新能力的方法。Perl 認可並鼓勵這種態度。回頁首OOP 的好處OOP 的好處太多,本文難以列舉。正如我在前面提到的那樣,有很多關於該主題的書籍。這些好處中的壹小部分是:易於代碼重用、代碼質量的改進、壹致的接口和可適應性。因為OOP 建立在類和對象的基礎之上,所以重用 OO 代碼意味著在需要時只需簡單地導入類。至今為止,代碼重用是使用 OOP 的唯壹最主要原因,也是 OOP 在當今業界中的重要性和流行性日益增加的原因所在。這裏有壹些陷阱。例如,在當前的情況下,以前問題的解決方案可能不理想,並且文檔庫編制得很差,以至於理解和使用文檔編制很差的庫所花的時間可能與重新編寫庫的時間壹樣長。系統架構設計師的工作是看到和避免這些陷阱。使用OOP 可以提高代碼質量,因為封裝減少了數據毀壞(“友好之火”),而繼承和多態性則減少了必須編寫的新代碼數量和復雜性。在代碼質量和編程創新之間有壹個微妙的平衡,這最好留給團隊去發現,因為它完全取決於團隊的構成和目的。OOP 繼承和重用使得在代碼中實現壹致的接口變得簡單,但是並不能說所有的 OO 代碼都有壹致的接口。程序員仍然必須遵循通用的體系結構。例如,團隊應該在錯誤日誌記錄的格式和接口方面達成壹致,最好通過壹個允許日後擴展並且極其易用的示範模塊接口來這樣做。只有在那時,每名程序員才能承諾使用該接口,而不是無規則的 print 語句,因為他們會認識到在出現下壹個錯誤日誌記錄函數時,學習該接口的努力不會白費。可適應性在編程中是壹個有些含糊的概念。我願意把它定義成對環境和用法更改的接受性和預見性。對於編寫良好的軟件來說,可適應性很重要,因為所有的軟件必須隨著外部世界而進化。編寫良好的軟件應該很容易進化。OOP 通過模塊設計、改進的代碼質量和壹致的接口確保新操作系統或者新報告格式不要求對體系結構的核心作出根本更改,從而有助於軟件的進化。回頁首如何在 Perl 中使用 OOP不管您是否相信,Perl 中的 OOP 對初級和中級用戶都不難,甚至對高級用戶也沒那麽復雜。根據我們到目前為止所討論的有關 OOP 的復雜工作方式,您可能不這麽認為。然而,Perl 卻樂意對程序員施加盡可能少的限制。Perl OOP 就象烤肉(恕我比喻不當)。每個人都帶來自己的肉,並以自己喜愛的方式烤肉。甚至還有烤肉的團隊精神也是那樣,就象可以輕易在不相關的對象之間***享數據壹樣。我們必須采取的第壹步是理解 Perl 包。包類似於 C++ 中的名稱空間和 Java 中的庫:象用來將數據限制在特定區域的圍欄。然而,Perl 包只是為程序員提供建議。缺省情況下,Perl 不限制包之間的數據交換(盡管程序員可以通過詞法變量這樣做)。清單1. 包名、切換包、在包之間***享數據和包變量#!/usr/bin/perl # note: the following code will generate warnings with the -w switch, # and won‘t even compile with "use strict". It is meant to demonstrate # package and lexical variables. You should always "use strict". # pay attention to every line! # this is a global package variable; you shouldn‘t have any with "use strict" # it is implicitly in the package called "main" $global_sound = " "; package Cow; # the Cow package starts here # this is a package variable, accessible from any other package as $Cow::sound $sound = "moo"; # this is a lexical variable, accessible anywhere in this file my $extra_sound = "stampede"; package Pig; # the Pig package starts, Cow ends # this is a package variable, accessible from any other package as $Pig::sound $Pig::sound = "oink"; $::global_sound = "pigs do it better"; # another "main" package variable # we‘re back to the default (main) package package main; print "Cows go: ", $Cow::sound; # prints "moo" print "\nPigs go: ", $Pig::sound; # prints "oink" print "\nExtra sound: ", $extra_sound; # prints "stampede" print "\nWhat‘s this I hear: ", $sound; # $main::sound is undefined! print "\nEveryone says: ", $global_sound; # prints "pigs do it better" 請註意,可以在所有三個包(“main”、“Pig”和“Cow”)中訪問文件作用域內的詞法變量 $extra_sound ,因為在該示例中它們是在同壹文件中定義的。通常,每個包在它自己文件內部定義,以確保詞法變量為該包所私有。這樣就可以實現封裝。(有關詳細信息,請運行“ perldoc perlmod”。) 接下來,我們要將包與類關聯。就 Perl 而言,類只是壹個奇特的包(相反,對象由 bless() 函數特別創建)。同樣,Perl 對 OOP 規則實施得不是很嚴格,以便程序員不為其所約束。 new() 方法是類構造器的慣用名稱(盡管按照 Perl 慣有的不嚴格方式,您可以使用任意名稱)。當將類實例化成對象時都要調用它。 清單2. barebones 類#!/usr/bin/perl -w package Barebones; use strict; # this class takes no constructor parameters sub new { my $classname = shift; # we know our class name bless {}, $classname; # and bless an anonymous hash } 1; 可以通過將清單 2 中的代碼放入任何目錄內名為 Barebones.pm 的文件中,然後在該目錄中運行以下命令來測試該代碼(這表示:“在庫路徑中包括當前目錄,使用 Barebones 模塊,然後創建壹個新的 Barebones 對象”):perl -I. -MBarebones -e ‘my $b = Barebones->new()‘ 例如,可以在 new() 方法中放入 print 語句,以便看到 $classname 變量所擁有的內容。 如果調用 Barebones::new() 而不是 Barebones->new() ,類名將不會傳遞到 new() 。換句話說, new() 將不作為構造器,而只是普通的函數。 您可能要問:為什麽需要傳入 $classname 。為什麽不直接用 bless {}, "Barebones"; ?因為繼承的緣故,這個構造器可能被壹個從 Barebones 繼承、但名稱卻不是 Barebones 的類調用。您可能正在用錯誤的名稱享有錯誤的事,而在 OOP 中,那是個壞主意。 除了new() 之外,每個類都需要成員數據和方法。定義它們就象編寫幾個過程壹樣簡單。 清單3. 帶有成員數據和方法的類#!/usr/bin/perl -w package Barebones; use strict; my $count = 0; # this class takes no constructor parameters sub new { my $classname = shift; # we know our class name $count++; # remember how many objects bless {}, $classname; # and bless an anonymous hash } sub count { my $self = shift; # this is the object itself return $count; } 1; 可以用以下命令測試該代碼:perl -I. -MBarebones -e ‘my $b = Barebones->new(); Barebones->new(); print $b->count‘ 您應該得到 ‘2‘ 這個結果。構造器被調用兩次,它修改詞法變量( $count),該變量被限制在 Barebones 包的作用域,而 不是每個Barebones 對象的作用域。應該將對象本身範圍內的數據存儲在對象本身中。在 Barebones 的示例中,被享有成對象的是匿名散列。請註意我們怎樣才能在每次調用該對象的方法時訪問該對象,因為對該對象的引用是傳遞給那些方法的第壹個參數。 有幾個特殊的方法,例如 DESTROY() 和AUTOLOAD(),Perl 在某些條件下會自動調用它們。 AUTOLOAD() 是用來允許動態方法名稱的全捕獲(catch-all)方法。 DESTROY() 是對象析構器,但是除非您確實非常非常需要,否則不應該使用它。在 Perl 中使用析構器通常表明您還在象 C/C++ 程序員那樣考慮問題。 讓我們看壹下繼承。在 Perl 中通過更改 @ISA 變量來這樣做。您只需將壹個類名表賦值給該變量即可。就是這樣。您可以在 @ISA 中放入任何東西。您可以使您的類成為 Satan 的子類。Perl 不在乎(盡管您的牧師、部長、教長、猶太學者等可能在乎)。 清單4. 繼承#!/usr/bin/perl -w package Barebones; # add these lines to your module‘s beginning, before other code or # variable declarations require Animal; # the parent class @ISA = qw(Animal); # announce we‘re a child of Animal # note that @ISA was left as a global default variable, and "use # strict" comes after its declaration. That‘s the easiest way to do it. use strict; use Carp; # make your new() method look like this: sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new(); # use the parent‘s new() method bless ($self, $class); # but bless $self (an Animal) as Barebones } 1; 這些是 Perl 中 OOP 的最基本知識。Perl 語言中還有很多您應該探索的知識。人們已經撰寫了很多有關這壹主題的書籍。如果想閱讀的話,請參考 參考資料。 回頁首h2xs:您最好的新朋友您不想擁有壹個可以為您編寫 Perl 類、還可以編寫文檔(POD)框架並且通常可以通過正確地完成這些事而使您的生活輕松壹些的工具嗎?Perl 正好帶有這種工具: h2xs。 別忘了使用幾個重要標誌:“ -A -n Module”。利用這些標誌,h2xs 將生成壹個名為“Module”、且裏面全是有用文件的框架目錄。這些文件是: Module.pm ,模塊本身,帶有已經編寫好的框架文檔。 Module.xs ,用於將您的模塊與 C 代碼鏈接。(有關詳細信息,請運行“ perldoc perlxs”。) MANIFEST ,用於打包的文件列表。 test.pl ,框架測試腳本。 Changes ,對該模塊所做更改的日誌。 Makefile.PL,makefile 生成器(用“ perl Makefile.PL ”運行它。) 您無需使用所有這些文件,但是在您確實需要它們時知道它們在那裏是很好的。

  • 上一篇:酒店冰箱尺寸壹般是多少?
  • 下一篇:單機版NBA2008遊戲怎麽玩啊?
  • copyright 2024編程學習大全網