日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

什么是OOP

 snowccc 2007-01-12

什么是OOP

什么是OOP,或者說OOP是什么?相信大家我一樣在不少地方看到OOP這個詞
本文來源于IBM網站 http://www-128.ibm.com/developerworks/cn/linux/sdk/road/part5/index.html
本 developerWorks 文章系列完整介紹了如何用 Perl 進行更佳編程。在本文(也是本系列的第五部分)中,Teodor 解釋了什么是面向對象編程,何時使用它以及它是如何在 Perl 中工作的。面向對象編程(OOP)是一種強大的編程技術,但它不是萬能藥。優(yōu)秀的程序員必須理解如何使用它,并且必須知道何時依賴更傳統(tǒng)的編程技術。在 Perl 中使用 OOP 很簡單。與 C++ 和 Java 等限制性更強的 OOP 語言不同,Perl 中的 OOP 很少對程序員施加強制性約束。OOP 是對每一名程序員的工具箱的必要補充,并且是用于擴展可用 Perl 解決的問題范圍的非常有用的技術。

什么是面向對象編程(OOP)?

OOP 是一種用于解決問題的編程方法或通用方法。與之相反,算法是用于解決特定問題的 特定方法。OOP 天生是一種強有力的方法;它往往使過程型和函數(shù)型編程方法與該問題較少相關,并且除非它與過程型和函數(shù)型編程方法的混合對其極其有益,否則它們之間不會很好地結合在一起。在 Perl 中,這種強大的力量有所減弱,但仍然很好地存在著。

本文討論 Perl 中的 OOP 對比函數(shù)型和過程型編程的基本知識,并演示如何在 Perl 程序和模塊中使用 OOP。請記住,本文只是一篇概述,而不是對 Perl 中 OOP 所有方面的詳盡解釋。這樣的詳盡解釋需要幾本書才能講完,并且已經被寫過幾次了。有關詳細信息,請參閱本文稍后的 參考資料。

究竟什么是 OOP?

OOP 是一種通過使用對象來解決問題的技術。在編程術語中,對象是這樣一些實體:它們的特性和行為是解決手頭問題所必需的。這個定義本應該更詳細些,但是做不到,因為當今計算機行業(yè)中 OOP 方法的種類之多簡直難以想象。

在 Perl 編程環(huán)境中,OOP 并不是使用該語言所必需的。Perl 版本 5 和更高版本鼓勵使用 OOP,但確切地說不要求這樣做。所有 Perl 庫都是模塊,這意味著它們至少使用 OOP 的基本部分。而且,大多數(shù) Perl 庫都作為對象實現(xiàn),這意味著用戶必須通過良好定義的接口將它們作為具有特定行為和特性的 OOP 實體來使用。





回頁首


基本的 OO 編程語言特性

通常,有三個語言特性是 OOP 編程語言所必需的。它們是繼承、多態(tài)性和封裝。

Perl 支持繼承。當一個對象(子對象)使用另一個對象作為起點(父對象),并且隨后在需要時修改其特性和行為時,就要用到 繼承。子-父關系是 OOP 所必需的,因為它使得在其它對象基礎上構建對象成為可能。這種重用是使 OOP 成為程序員寵兒的好處之一。

有兩種繼承:單一繼承和多重繼承。 單一繼承要求子對象只有一個父對象,而 多重繼承更自由(與實際生活一樣,在編程過程中具有兩個以上的父對象會導致混亂并使子對象難以工作,因此,不要過多使用多重繼承)。盡管兩個或兩個以上的父對象實際上很少見,但 Perl 支持多重繼承。

多態(tài)性(來自希臘語,表示“很多形態(tài)”)是使一個對象被看成另一個對象的技術。這有點復雜,那么我舉個例子。比方說,您有一個綿羊牧場,里面有四只綿羊(綿羊屬),但是您剛剛買了兩只山羊(山羊屬)和一只德國牧羊犬(犬科犬屬)。您一共有多少動物?您得把所有的綿羊、山羊和狗加起來,結果是 7 只。其實您剛剛應用了多態(tài)性,即為了計算,把三種不同種類的動物當成一種通用類型(“動物”)對待。如果您把綿羊、山羊和狗當成哺乳動物看待,這就是一個簡單的信心飛躍。生物學家每天都以這種方式使用多態(tài)性,而程序員則以從其它科學領域“竊用”(我是指“重用”)好主意聞名。

Perl 完全支持 多態(tài)性。但它用得不是很頻繁,因為 Perl 程序員看起來更喜歡用對象特性、而不是通過修改繼承的行為來修改通用行為。這意味著您更可能看到創(chuàng)建三個 IO::Socket::INET 對象的代碼:一個對象用于在端口 234 接收和發(fā)送 UDP 包、一個對象用于在端口 80 接收 TCP 包,還有一個對象在端口 1024 發(fā)送 TCP 包,而不大會看到對第一種情況使用 IO::Socket::INET::UDPTransceiver 、對第二種情況使用 IO::Socket::INET::TCPReceiver 而對第三種情況使用 IO::Socket::TCPTransmitter 的代碼。這就象是在生物學術語中說狗和山羊都是哺乳動物,但是山羊屬于山羊屬,而狗屬于犬屬。

OOP 純化論者認為每件事都應該正確分類,但是 Perl 程序員根本不是純化論者。他們往往更不拘束于 OOP 規(guī)則,這使得他們在聚會中比 OOP 純化論者更快樂。

封裝指的是以這樣一種方式包含對象行為和特性:除非對象作者允許,否則用戶無法訪問該對象的行為和特性。在這種方式下,對象用戶無法做不準他們做的事,無法訪問不準他們訪問的數(shù)據(jù),并且通常是有害數(shù)據(jù)。Perl 通常用松弛的方法封裝。請參閱 清單 1。





回頁首


為什么說 OOP 是一種強有力的方法?

返回到我們最初的 OOP 如何是一種強有力的方法這一主題,我們現(xiàn)在可以看到 OOP 結合了幾個關鍵的概念,這使得它很難與過程型和函數(shù)型編程(PP 和 FP)混合使用。首先,PP 和 FP 都沒有繼承或類多態(tài)性的概念,因為在 PP 和 FP 中根本就沒有類。在 PP 和 FP 中存在封裝,但只在過程型級別,從來不作為類或對象屬性封裝。既然程序員不怕麻煩來使用這些基本的 OOP 工具,那就意味著程序員通常更可能對整個項目使用 OOP,而不是混合不兼容的方法。

有人可能爭論說所有程序最終都歸結為指令的過程型執(zhí)行,因此無論 OOP 程序實現(xiàn)得有多純,每個 OOP 程序都在其函數(shù)(也稱為方法)和創(chuàng)建第一個對象(該對象做其余工作)的代碼中包含過程型代碼。甚至象 Java 那樣接近“純”OOP 的語言都無法避免地需要一個 main() 函數(shù)。因此,看起來 OOP 只是 PP 的一個子集。但是這種 OOP 向序列指令的歸結和實際為每個操作所執(zhí)行的匯編程序指令一樣,都不是系統(tǒng)架構設計師或程序員所關心的事。請記住,OOP 本身只是一種方法,而不是目的。

OOP 與過程型編程方法合作得不是很好,因為它集中在對象上,而過程型編程基于過程(我們將 過程大致定義為不使用 OOP 技術就可以得到的函數(shù),而將 方法定義為只有在對象中才能得到的函數(shù))。正如方法一樣,過程只是由用戶調用的函數(shù),但是二者之間有一些差異。

過程不使用對象數(shù)據(jù)。必須在它們的參數(shù)列表中為它們傳遞數(shù)據(jù),或者它們必須使用所在作用域中的數(shù)據(jù)。過程可以訪問調用它時傳遞給它的任何數(shù)據(jù),甚至整個程序的全局數(shù)據(jù)。方法應該只訪問它們對象的數(shù)據(jù)。實際上,方法的函數(shù)作用域通常是包含該方法的對象。

常常發(fā)現(xiàn)過程使用全局數(shù)據(jù),盡管只有在絕對必要時才應該這樣做。應該盡快重寫使用全局數(shù)據(jù)的方法。過程通常用幾個參數(shù)調用其它過程。方法應該只有幾個參數(shù),并且它們調用其它方法的次數(shù)比其它過程更多。

函數(shù)型編程(FP)與 OOP 配合不好有幾個原因。最重要的原因是 FP 基于用來解決問題的詳細函數(shù)型方法,而 OOP 則使用對象來表達概念,并且,與 OOP 方法只能在包含它們的對象中使用不同,F(xiàn)P 過程得到處使用。

綜上所述,我們現(xiàn)在可以解釋 Perl 為什么是混合 OOP、FP 和 PP 方法的最佳語言之一。





回頁首


Perl 是如何將 OOP 與過程型和函數(shù)型編程結合起來的?

Perl 是一種松弛的語言。它極力讓程序員以他們認為方便的任何方式做他們想做的任何事。這與 Java 和 C++ 之類的語言截然不同。例如,如果程序員原本沒有聲明變量,Perl 樂于允許程序員自動創(chuàng)建變量(盡管不鼓勵這樣做,并且可以通過使用高度推薦的“use strict”編譯指示阻止)。如果您要向自己的腳開槍,Perl 會給您十發(fā)子彈和一個激光瞄準鏡,然后站在一旁鼓勵您。

因此,Perl 是一種非常便于濫用方法的語言。別害怕。沒關系。例如,訪問內部的對象數(shù)據(jù)、實時更改類和實時重定義方法都是允許的。Perl 方式是:允許程序員為了編碼、調試和執(zhí)行效率的目的而去打破規(guī)則。如果這有助于完成工作,那么沒關系。因此,Perl 本身可能是程序員最好的朋友,也可能是最壞的敵人。

如果混合 OOP、FP 和 PP 意味著打破規(guī)則,那么為什么任何人都想要混合 OOP、FP 和 PP 呢?讓我們回頭想想這個問題。什么是 OOP、FP 和 PP?它們只是現(xiàn)有的為編程團隊服務的編程方法、概念集和規(guī)則集。OOP、FP 和 PP 是工具,每名程序員的首要工作就是要了解他的工具。如果一名程序員在排序散列時不能使用 FP 的 Schwartzian 變換,而是編寫他自己的 Sort::Hashtable ,或者不能重用 Sys::Hostname 模塊,而是編寫過程代碼來獲得系統(tǒng)主機名,那么這個程序員是在浪費時間、精力和金錢,并且降低了代碼質量和可靠性。

一個編程團隊可能會因為它們最熟知的工具而沾沾自喜,對它們來說,這可能正是最壞的事。如果一個團隊只使用象計算機編程行業(yè)那樣令人沖動和充滿創(chuàng)新的行業(yè)中所保證的可用工具的一部分,那么它在幾年之后注定要變得毫無用處。程序員應該能夠結合任何使工作更有效、代碼更好以及使團隊更具創(chuàng)新能力的方法。Perl 認可并鼓勵這種態(tài)度。





回頁首


OOP 的好處

OOP 的好處太多,本文難以列舉。正如我在前面提到的那樣,有很多關于該主題的書籍。這些好處中的一小部分是:易于代碼重用、代碼質量的改進、一致的接口和可適應性。

因為 OOP 建立在類和對象的基礎之上,所以重用 OO 代碼意味著在需要時只需簡單地導入類。至今為止,代碼重用是使用 OOP 的唯一最主要原因,也是 OOP 在當今業(yè)界中的重要性和流行性日益增加的原因所在。

這里有一些陷阱。例如,在當前的情況下,以前問題的解決方案可能不理想,并且文檔庫編制得很差,以至于理解和使用文檔編制很差的庫所花的時間可能與重新編寫庫的時間一樣長。系統(tǒng)架構設計師的工作是看到和避免這些陷阱。

使用 OOP 可以提高代碼質量,因為封裝減少了數(shù)據(jù)毀壞(“友好之火”),而繼承和多態(tài)性則減少了必須編寫的新代碼數(shù)量和復雜性。在代碼質量和編程創(chuàng)新之間有一個微妙的平衡,這最好留給團隊去發(fā)現(xiàn),因為它完全取決于團隊的構成和目的。

OOP 繼承和重用使得在代碼中實現(xiàn)一致的接口變得簡單,但是并不能說所有的 OO 代碼都有一致的接口。程序員仍然必須遵循通用的體系結構。例如,團隊應該在錯誤日志記錄的格式和接口方面達成一致,最好通過一個允許日后擴展并且極其易用的示范模塊接口來這樣做。只有在那時,每名程序員才能承諾使用該接口,而不是無規(guī)則的 print 語句,因為他們會認識到在出現(xiàn)下一個錯誤日志記錄函數(shù)時,學習該接口的努力不會白費。

可適應性在編程中是一個有些含糊的概念。我愿意把它定義成對環(huán)境和用法更改的接受性和預見性。對于編寫良好的軟件來說,可適應性很重要,因為所有的軟件必須隨著外部世界而進化。編寫良好的軟件應該很容易進化。OOP 通過模塊設計、改進的代碼質量和一致的接口確保新操作系統(tǒng)或者新報告格式不要求對體系結構的核心作出根本更改,從而有助于軟件的進化。





回頁首


如何在 Perl 中使用 OOP

不管您是否相信,Perl 中的 OOP 對初級和中級用戶都不難,甚至對高級用戶也沒那么復雜。根據(jù)我們到目前為止所討論的有關 OOP 的復雜工作方式,您可能不這么認為。然而,Perl 卻樂意對程序員施加盡可能少的限制。Perl OOP 就象烤肉(恕我比喻不當)。每個人都帶來自己的肉,并以自己喜愛的方式烤肉。甚至還有烤肉的團隊精神也是那樣,就象可以輕易在不相關的對象之間共享數(shù)據(jù)一樣。

我們必須采取的第一步是理解 Perl 包。包類似于 C++ 中的名稱空間和 Java 中的庫:象用來將數(shù)據(jù)限制在特定區(qū)域的圍欄。然而,Perl 包只是為程序員提供建議。缺省情況下,Perl 不限制包之間的數(shù)據(jù)交換(盡管程序員可以通過詞法變量這樣做)。


清單 1. 包名、切換包、在包之間共享數(shù)據(jù)和包變量
#!/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 ,因為在該示例中它們是在同一文件中定義的。通常,每個包在它自己文件內部定義,以確保詞法變量為該包所私有。這樣就可以實現(xiàn)封裝。(有關詳細信息,請運行“ perldoc perlmod ”。)

接下來,我們要將包與類關聯(lián)。就 Perl 而言,類只是一個奇特的包(相反,對象由 bless() 函數(shù)特別創(chuàng)建)。同樣,Perl 對 OOP 規(guī)則實施得不是很嚴格,以便程序員不為其所約束。

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 模塊,然后創(chuàng)建一個新的 Barebones 對象”):

perl -I. -MBarebones -e ‘my $b = Barebones->new()‘
            

例如,可以在 new() 方法中放入 print 語句,以便看到 $classname 變量所擁有的內容。

如果調用 Barebones::new() 而不是 Barebones->new() ,類名將不會傳遞到 new() 。換句話說, new() 將不作為構造器,而只是普通的函數(shù)。

您可能要問:為什么需要傳入 $classname 。為什么不直接用 bless {}, "Barebones"; ?因為繼承的緣故,這個構造器可能被一個從 Barebones 繼承、但名稱卻不是 Barebones 的類調用。您可能正在用錯誤的名稱享有錯誤的事,而在 OOP 中,那是個壞主意。

除了 new() 之外,每個類都需要成員數(shù)據(jù)和方法。定義它們就象編寫幾個過程一樣簡單。


清單 3. 帶有成員數(shù)據(jù)和方法的類
#!/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 對象的作用域。應該將對象本身范圍內的數(shù)據(jù)存儲在對象本身中。在 Barebones 的示例中,被享有成對象的是匿名散列。請注意我們怎樣才能在每次調用該對象的方法時訪問該對象,因為對該對象的引用是傳遞給那些方法的第一個參數(shù)。

有幾個特殊的方法,例如 DESTROY()AUTOLOAD() ,Perl 在某些條件下會自動調用它們。 AUTOLOAD() 是用來允許動態(tài)方法名稱的全捕獲(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 ”運行它。)

您無需使用所有這些文件,但是在您確實需要它們時知道它們在那里是很好的。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多