Provided by: manpages-zh_1.5-1_all bug

NAME

       perlboot - 初學者的惘V對象教程

DESCRIPTIONyz
       如果你對其他語言中的對象並不熟悉的話,
       那麼其他有關perl對象的檔案可能使你感到恐懼, 比如 perlobj ,
       這是基礎性的參考檔案, 和 perltoot, 這是介紹perl對象的特性的教程.

       所以, 讓我怢咱t一條路,假定你沒有任何關於對象的概念. 你需n了解子程式
       (perlsub), 引用 (perlref et. seq.), 和 包(或模塊) (perlmod),
       如果還不清楚的話,先把他抪d清楚.

       If we could talk to the animals...Introducing the method invocation arrowkbY號

       現在,我抳 "Class->method" 是調用了包(或模塊)"Class"中的 "method"
       方法。(Here, "Class" is used in its "category" meaning, not its
       "scholastic" meaning.) 不是很準確,不過我抪|一步一步的來做.
       現在,可以這樣做:

           # Cow::speak, Horse::speak, Sheep::speak as before
           Cow->speak;
           Horse->speak;
           Sheep->speak;

       輸出為:

           a Cow goes moooo!
           a Horse goes neigh!
           a Sheep goes baaaah!

       還不是很有趣. 一樣的字符,常量,沒有變量. 但是, 不同部分可以分開了. 請看:

           $a = "Cow";
           $a->speak; # invokes Cow->speak

       哇! 現在包名與子程式名可以分開了, 我怚i以用變量來表示包名. 這樣,在使用
       "use strict refs" 預編譯指令時也可以正常工作了.

       Invoking a barnyard@f棚

       現在讓我怬熀b頭用到牲口棚的例子中,範例:

           sub Cow::speak {
             print "a Cow goes moooo!\n";
           }
           sub Horse::speak {
             print "a Horse goes neigh!\n";
           }
           sub Sheep::speak {
             print "a Sheep goes baaaah!\n"
           }

           @pasture = qw(Cow Cow Horse Sheep Sheep);
           foreach $animal (@pasture) {
             $animal->speak;
           }

       現在我怍狾釭滌坁奕ㄞ鉬☆雂F, 而且不用使用代碼引用.

       不過注意到那些相同的代碼. 每 "speak" 子程式的結構是相同的: 一 "print"
       操作符和一荌禰賑萓P的字符串,只有兩茧不同. 如果我-
       怚i以析出相同的部分就更好了,如果將來n把 "goes" 替換為 "says"
       時就簡單得多了

       實際上這並不困難, 不過在這之前我抸雩蚢嚚b頭符號了解的更多一些.

       The extra parameter of method invocationkB~數

       語句:

           Class->method(@args)

       這樣調用函數 "Class::method":

           Class::method("Class", @args);

       (如果子程式找不到,"繼承,inheritance" 開始起作用,這在後捧|講到).
       這意味著我戔o到的第一-
       荌捊O類名(如果沒有給出其他參數,它就是調用時的唯一參數).所以我-
       怚i以像這樣姨g "Sheep" speaking 子程式:

           sub Sheep::speak {
             my $class = shift;
             print "a $class goes baaaah!\n";
           }

       另外的動物與此類似:

           sub Cow::speak {
             my $class = shift;
             print "a $class goes moooo!\n";
           }
           sub Horse::speak {
             my $class = shift;
             print "a $class goes neigh!\n";
           }

       每次 $class  都會得到與子程式相關的正確的. 但是,還是有很多相似的結構.
       可以再簡單些嗎? 是的. 可以通過在一蚚中調用其它的方法來實現.

       Calling a second method to simplify thingst@kH@

       我怞b "speak" 中調用 "sound". 這茪隤k提供聲答漱漁e.

           { package Cow;
             sub sound { "moooo" }
             sub speak {
               my $class = shift;
               print "a $class goes ", $class->sound, "!\n"
             }
           }

       現在, 當我抻掍 "Cow->speak" 時, 我怞b "speak" 中得到 "Cow" 的類
       $class. 他會選擇 "Cow->sound" 方法, 然後返回 "moooo". 那如果是 "Horse"
       呢?

           { package Horse;
             sub sound { "neigh" }
             sub speak {
               my $class = shift;
               print "a $class goes ", $class->sound, "!\n"
             }
           }

       僅僅包名和聲策傅雂. 因此我怚i以在Cow和Horse中共用 "speak" 嗎?
       是的,通過繼承實現!

       Inheriting the windpipes~管

       我抭衎堣@茪膠@函數包,命名為 "Animal",在其中定義 "speak":

           { package Animal;
             sub speak {
               my $class = shift;
               print "a $class goes ", $class->sound, "!\n"
             }
           }

       然後,在每荌坁咧綵 "繼承,inherits" "Animal" 類, 同時賦予每-
       荌坁咻U自的聲:

           { package Cow;
             @ISA = qw(Animal);
             sub sound { "moooo" }
           }

       注意增加的數組 @ISA  . 我怜角W講到它.

       現在當我抻掍 "Cow->speak" 時會發生什麼?

       漸, Perl構造參數列表. 在這種情況下, 只有 "Cow". 然後Perl 查找
       "Cow::speak". 但是找不到, 所以Perl檢查繼承數組 @Cow::ISA. 找到了,
       那裏只有一 "Animal"

       Perl 然後在 "Animal" 中查找 "speak", "Animal::speak". 找到了,
       然後調用該子程式, 參數在一開始就被固定了.

       在子程式 "Animal::speak" 中, $class 是 "Cow" (第一荌捊). 在我抻掍
       "$class->sound" 時, 漸尋找 "Cow->sound" , 找到了, 因此不用查看 @ISA.
       成功!

      @ISAOverriding the methodsk載

       讓我戽K上一只老鼠, 它的聲筑t不多聽不到:

           # Animal package from before
           { package Mouse;
             @ISA = qw(Animal);
             sub sound { "squeak" }
             sub speak {
               my $class = shift;
               print "a $class goes ", $class->sound, "!\n";
               print "[but you can barely hear it!]\n";
             }
           }

           Mouse->speak;

       輸出為:

           a Mouse goes squeak!
           [but you can barely hear it!]

       在這裏, "Mouse" 有它自己的speak 函數, 所以 "Mouse->speak"
       不會調用"Animal->speak". 這叫做姜 "overriding". 實際上, 我-
       怓雂ㄔ弇"Mouse" 是 "Animal", 因為 "speak" 所用到的所有方法在 "Mouse"
       中都有定義.

       但是有些代碼與 "Animal->speak" 的相同 , 這在程式維護時是荌暋D. 我-
       怉鄐ㄞ鉣 "Mouse" 與其它 "Animal" 作相同的事,但是給它加上特殊的部分呢?
       可以!

       漸,我怚i以直接調用 "Animal::speak" 方法:

           # Animal package from before
           { package Mouse;
             @ISA = qw(Animal);
             sub sound { "squeak" }
             sub speak {
               my $class = shift;
               Animal::speak($class);
               print "[but you can barely hear it!]\n";
             }
           }

       注意我怚眸楊洏 $class (幾乎肯定是"Mouse") 作為 "Animal::speak" 的第一-
       荌捊, 因為我怢S有用箭頭符號. 那為什麼不用呢? 嗯, 如果我怞b那兒調用
       "Animal->speak", 則第一荌捊O "Animal" 而不是 "Mouse" , 這樣當調用
       "sound" 時, 就找不到正確的函數了.

       雖然如此,直接調用 "Animal::speak" 確實不怎麼好. 萬一 "Animal::speak"
       不存在, 而是繼承自 @Animal::ISA 中的某蚚呢? 因為沒有使用箭頭符號, 我-
       怚u有一次機會去調用正確的函數.

       還n注意到,現在類名 "Animal" 直接在子程式中使用.
       如果維護代碼的人沒有注意到這一點, 改變了 <Mouse> 的 @ISA,沒有注意到
       "speak" 用到了 "Animal" 那就會出問題. 因此, 這可能不是一茼n方法.

       Starting the search from a different placeqa}lM找

       較好的解決辦法是讓Perl從繼承鏈的上一級開始尋找:

           # same Animal as before
           { package Mouse;
             # same @ISA, &sound as before
             sub speak {
               my $class = shift;
               $class->Animal::speak;
               print "[but you can barely hear it!]\n";
             }
           }

       這就對了. 使用這一語法, 我戔q "Animal" 尋找 "speak", 在找不到時尋找
       "Animal" 的繼承鏈.且第一荌捊O $class, 所以 "speak" 和"Mouse::sound"
       都會被正確地調用.

       但這還不是最好的方法.我攽晱眸源桴 @ISA 的元素順序. 更糟糕的是, 如果
       "Mouse" 有多茪鷖在 @ISA, 我攽揈知道蚚定義了 "speak".
       那麼,有沒有更好的辦法呢?

       The SUPER way of doing thingsSUPERWhere we're at so far...A horse is a horse, of course of course -- or is it?
      NOwwuOo?

       我戔q "Animal" 和 "Horse" 類的代碼開始:

         { package Animal;
           sub speak {
             my $class = shift;
             print "a $class goes ", $class->sound, "!\n"
           }
         }
         { package Horse;
           @ISA = qw(Animal);
           sub sound { "neigh" }
         }

       這樣使得我抻掍 "Horse->speak",從而向上調用 "Animal::speak",然後調用
       "Horse::sound" 來獲得指定的聲窗A輸出為:

         a Horse goes neigh!

       但是我怍狾釭滌迅ㄛO相同的. 如果我增加一茪l程式, 所有的馬都會共享它.
       這在創建相同的馬時確實不錯, 但是我怞p何能夠區分不同的馬呢? 比如,
       假設我想給我的第一匹馬起茼W字.
       應該有辦法使得它的名字和別的馬的名字不同.

       這可以通過創建一 "實例,instance" 來實現. 實例是由類創建的. 在Perl中,
       任何引用都可以是實例, 就讓我戔q最簡單的引用開始吧,一蚍迠q引用:

         my $name = "Mr. Ed";
         my $talking = \$name;

       現在 $talking 是指向實例特有數據( $name )的引用。把這-
       茪犍恓雃辛u正的實例的是一荅S殊的操作符,叫做 "bless":

         bless $talking, Horse;

       這蚞犑@符把包名 "Horse" 中的所有信息存放到引用所指向的東西中. 這時,我-
       抳 $talking 是 "Horse" 的一蚢磛 . 也就是說, 它是一匹獨特的馬.
       引用並沒有改變, 還可以用於間接引用操作符.

       Invoking an instance methodk

       箭頭符號可以用於實例. 那麼, 聽聽 $talking 的聲筆a:

         my $noise = $talking->sound;

       n調用 "sound", Perl 漸注意到 $talking 是一 blessed 引用 (因此是一-
       蚢磛). 它會構造一荌捊C表, 現在只有 $talking. (在後惕确會看到參數-
       怞b實例變量之後, 與使用類時相似.)

       然後,是真正有意思的部分: Perl 查找實例所屬的類, 這裏是 "Horse",
       在其中尋找對應的方法. 這裏, "Horse::sound" 直接可以找到(不用使用繼承),
       最後這樣調用:

         Horse::sound($talking)

       注意這裏的第一荌捊椄O實例本, 而不像前惕确學到的是類名. 最後返回O
       "neigh", 它被賦 $noise 變量.

       如果找不到 Horse::sound, 會在 @Horse::ISA 列表中查找.
       類方法與實例方法的唯一區別是調用時的第一荌捊O實例(一-
       羒lessed引用)還是一蚚名(一茼r符串).

       Accessing the instance dataX據

       因為我戔o到的第一荌捊O實例,我怚i以訪問實例特有的數據. 我-
       怚i以取得馬的名字:

         { package Horse;
           @ISA = qw(Animal);
           sub sound { "neigh" }
           sub name {
             my $self = shift;
             $$self;
           }
         }

       現在,我抻掍峖W字:

         print $talking->name, " says ", $talking->sound, "\n";

       在 "Horse::name" 中, @_ 數組僅含有 $talking, shift 將 $talking 賦給了
       $self. (傳統上我怞b處理實例方法時總是把第一茪葛擠廘 $self,
       所以你也應該這麼做, 除非你有不這樣做的充分理由.) 然後, $self
       被標量化,成為 "Mr. Ed", 這就行了. 輸出是:

         Mr. Ed says neigh.

       How to build a horsep@馬

       當然啦,如果我怳滶妘衎堜狾釭滌, 我抪|出很多錯誤. 不僅如此,我攽椑黑p了-
       惘V對象編程的特性,因為在那種情況下馬的"內臟"也可見了.
       如果你是獸醫的話,這迉縝n, 可是如果你僅僅是虓R馬者呢? 所以,我旼 Horse
       類來創建一匹新馬:

         { package Horse;
           @ISA = qw(Animal);
           sub sound { "neigh" }
           sub name {
             my $self = shift;
             $$self;
           }
           sub named {
             my $class = shift;
             my $name = shift;
             bless \$name, $class;
           }
         }

       現在,我怚i以用 "named" 方法創建一匹馬:

         my $talking = Horse->named("Mr. Ed");

       注意到我怞釵^到了類方法, 所以傳遞給 "Horse::named" 的兩荌捊O "Horse"
       和 "Mr. Ed". "bless" 操作符不僅將 $name 實例化, 且將指向 $name
       的引用作為返回藀^. 這樣, 我抴N創建了一匹馬.

       這裏,我抻掍峇F構造器 "named", 它的參數就是特定的 "Horse" 的名字.
       你可以使用不同的構造器用不同的名字建立不同的對象(比如記錄它的譜系或生日).
       但是, 你會發現多數使用Perl的人更喜歡把構造器命名為 "new",
       並使用不同的方法解釋 "new" 的參數. 兩種都挺好,只n你能創建對象就行.
       (你會自己創建一,對嗎?)

       Inheriting the constructor~cy器

       但是那茪隤k中有沒有什麼對於 "Horse" 來說比較特殊的東西呢? 沒有. 因此,
       從 "Animal" 創建其它任何東西也可以使用相同的方法,我怢虒楖::

         { package Animal;
           sub speak {
             my $class = shift;
             print "a $class goes ", $class->sound, "!\n"
           }
           sub name {
             my $self = shift;
             $$self;
           }
           sub named {
             my $class = shift;
             my $name = shift;
             bless \$name, $class;
           }
         }
         { package Horse;
           @ISA = qw(Animal);
           sub sound { "neigh" }
         }

       好了, 但是以實例調用 "speak" 會產生什麼結果呢?

         my $talking = Horse->named("Mr. Ed");
         $talking->speak;

       我戔o到的是:

         a Horse=SCALAR(0xaca42ac) goes neigh!

       為什麼?因為 "Animal::speak" 希望它的第一荌捊O類名, 而不是實例.
       當實例被傳入時,我怬璅洏峈漪O字符串而不是實例本,顯示的結果不是我-
       怍狶瑼.

       Making a method work with either classes or instances
      kPM例

       我抳愯做的是讓方法檢測它是被實例調用的還是被類調用的.
       最直接的方法是使用 "ref" 操作符.
       它在參數是實例時返回字符串,在參數是類名時返回 "undef". 我抮先改寫
       "name" 方法:

         sub name {
           my $either = shift;
           ref $either
             ? $$either # it's an instance, return name
             : "an unnamed $either"; # it's a class, return generic
         }

       在這兒, "?:" 操作符決定是選擇間接引用(dereference)還是派生字符串.
       現在我怚i以同時使用類或實例了. 注意我蚹鴾F第一荌捊 $either
       來表示期望的變化:

         my $talking = Horse->named("Mr. Ed");
         print Horse->name, "\n"; # prints "an unnamed Horse\n"
         print $talking->name, "\n"; # prints "Mr Ed.\n"

       我怚i以改寫 "speak" :

         sub speak {
           my $either = shift;
           print $either->name, " goes ", $either->sound, "\n";
         }

       而 "sound" 本來就可以工作. 那麼現在就一切完成了!

       Adding parameters to a methodk[數

       讓我怜V練動物怞Y飯:

         { package Animal;
           sub named {
             my $class = shift;
             my $name = shift;
             bless \$name, $class;
           }
           sub name {
             my $either = shift;
             ref $either
               ? $$either # it's an instance, return name
               : "an unnamed $either"; # it's a class, return generic
           }
           sub speak {
             my $either = shift;
             print $either->name, " goes ", $either->sound, "\n";
           }
           sub eat {
             my $either = shift;
             my $food = shift;
             print $either->name, " eats $food.\n";
           }
         }
         { package Horse;
           @ISA = qw(Animal);
           sub sound { "neigh" }
         }
         { package Sheep;
           @ISA = qw(Animal);
           sub sound { "baaaah" }
         }

       試試吧:

         my $talking = Horse->named("Mr. Ed");
         $talking->eat("hay");
         Sheep->eat("grass");

       輸出為:

         Mr. Ed eats hay.
         an unnamed Sheep eats grass.

       有參數的實例方法調用時漸得到實例的引用,然後得到參數的列表。因此第一-
       蚑掍庣篕琱W是這樣的:

         Animal::eat($talking, "hay");

       More interesting instancesh例

       如果實例需n更多的數據該怎麼辦呢? 更多的項目產生更有趣的實例, 每-
       荈等堨i以是一茪犍峏峈怓頇O一蚢龠H. 最簡單的方法是把它怞s放到哈希中.
       哈希中的關鍵詞叫做'實例變量"(instance variables)或者"成變量"(member
       variables),相應的]就是變量的C

       但是我怮蝏穨滶貝顐鴢╡予O? 回憶到對象是被實例化(blessed)的引用. 我-
       怚i以簡單地創建一荅牯痐F的哈希引用,同時相關的的內容也作些蚹鼢N可以了.

       讓我抭衎堣@只有名字有顏色的綿羊:

         my $bad = bless { Name => "Evil", Color => "black" }, Sheep;

       那麼 "$bad->{Name}" 是 "Evil", "$bad->{Color}" 是 "black". 但是我-
       抪Q通過 "$bad->name" 存取綿羊的名字name, 這有點的問題,因為現在它期望一-
       蚍迠q引用. 別擔心,因為蚰縞托僉眾:

         ## in Animal
         sub name {
           my $either = shift;
           ref $either ?
             $either->{Name} :
             "an unnamed $either";
         }

       "named" 當然還是創建標量的綿羊, 如下蚰艘N好了:

         ## in Animal
         sub named {
           my $class = shift;
           my $name = shift;
           my $self = { Name => $name, Color => $class->default_color };
           bless $self, $class;
         }

       預設顏色 "default_color" 是什麼? 嗯, 如果 "named" 只有一荌捊躪ame, 我-
       攽椄O希望有蚚C色, 所以我抭]定一蚚初始化顏色. 對綿羊來說, 白色比較好:

         ## in Sheep
         sub default_color { "white" }

       為了避免為每蚚定義顏色, 我怚i以在 "Animal" 中定義一
       "預設的預設,backstop" 的顏色:

         ## in Animal
         sub default_color { "brown" }

       現在, 因為只有 "name" 和 "named" 與對象的 "結構,structure" 相關,
       其餘的部分可以保持不變, 所以 "speak" 工作正常.

       A horse of a different color@PC馬

       但是如果所有的馬都是棕色的,也挺煩人的. 所以我怚i以寫-
       茪隤k來改變馬的顏色.

         ## in Animal
         sub color {
           $_[0]->{Color}
         }
         sub set_color {
           $_[0]->{Color} = $_[1];
         }

       注意到存取參數的不同方法了嗎: $_[0] 直接使用, 而沒有用 "shift".
       (這在我抸W繁存取時可以節省一些時間.) 現在我怚i以把Mr. Ed的顏色變過來:

         my $talking = Horse->named("Mr. Ed");
         $talking->set_color("black-and-white");
         print $talking->name, " is colored ", $talking->color, "\n";

       結果是:

         Mr. Ed is colored black-and-white

       Summary`結

       現在我攽縣F類方法,構造器,實例方法,實例數據,甚至還有存取器(accessor).
       但是這些還僅僅是開始. 我攽晲S有講到以兩茖蝻 getters,setters
       形式出現的存取器,析構器(destructor),間接對象(indirect object
       notation),子類(subclasses that add instance data),per-class data,-
       姜(overloading),"isa" 和 "can" 測試,公共類("UNIVERSAL" class),等等.
       這有待其它文件去講解了. 無論如何,希望本文使你對對象有所了解.

SEE ALSO見
       更多信息可參見 perlobj (這裏有更多的Perl對象的細節,而本文的是基礎),
       perltoot (惘V對象的中級教程),  perlbot  (更多的技巧),
       以及書籍,比如Damian Conway的不錯的書叫做《惘V對象的Perl (Object
       Oriented Perl)》。

       某些模塊可能對你有用,它怓O Class::Accessor, Class::Class,
       Class::Contract, Class::Data::Inheritable, Class::MethodMaker 還有
       Tie::SecureHash

COPYRIGHT

       Copyright (c) 1999, 2000 by Randal L. Schwartz and Stonehenge
       Consulting Services, Inc.  Permission is hereby granted to distribute
       this document intact with the Perl distribution, and in accordance with
       the licenses of the Perl distribution; derived documents must include
       this copyright notice intact.

       Portions of this text have been derived from Perl Training materials
       originally appearing in the Packages, References, Objects, and Modules
       course taught by instructors for Stonehenge Consulting Services, Inc.
       and used with permission.

       Portions of this text have been derived from materials originally
       appearing in Linux Magazine and used with permission.

@H
       redcandle <redcandle51@chinaren.com>

ss
       2001129http://cmpp.linuxforum.net