2010-02-15 44 views
12

我只是討厭如何CGI::ApplicationCGI對象的訪問器被稱爲query在Perl中,子類在基類中爲方法別名的正確方法是什麼?

我希望我的實例類能夠使用名爲cgi的訪問器來獲取與我的CGI::Application子類的當前實例關聯的CGI對象。

下面是我在做什麼一個自包含的例子:

package My::Hello; 

sub hello { 
    my $self =shift; 
    print "Hello @_\n"; 
} 

package My::Merhaba; 

use base 'My::Hello'; 

sub merhaba { 
    goto sub { shift->hello(@_) }; 
} 

package main; 

My::Merhaba->merhaba('StackOverflow'); 

這是工作,因爲我認爲它應該和我看不出有任何問題(比如,如果我想從My::Merhaba繼承:子類不需要知道任何關於merhaba)。

難道是更好/更正確的寫

sub merhaba { 
    my $self = shift; 
    return $self->hello(@_); 
} 

什麼是使用goto &NAME混疊方法名稱的目的的優勢/劣勢?有沒有更好的辦法?

注:如果你有一種衝動,有迴應goto是邪不這樣做,因爲這使用Perl的goto的比你心裏有什麼不同。

+0

我沒有看到使用'goto&NAME'這種方式。 'sub {}'包裝器向調用堆棧添加一個新的框架。你也可以直接調用這個方法。 – 2010-02-15 22:25:09

+0

@Michael Carman在@Eric Strom的答案的第一部分中使用'can'消除了我相信的問題。 – 2010-02-15 22:35:31

+0

我只是對'Method :: Alias'的任何進展感到好奇。 – 2010-04-01 20:34:21

回答

10

您的方法與goto是正確的,因爲它將確保caller/wantarray等保持正常工作。

我會建立這樣的新方法:

sub merhaba { 
    if (my $method = eval {$_[0]->can('hello')}) { 
     goto &$method 
    } else { 
     # error code here 
    } 
} 

或者,如果你不想使用繼承,你可以從你的調用代碼添加新的方法,以現有的包:

*My::Hello::merhaba = \&My::Hello::hello; 
    # or you can use = My::Hello->can('hello'); 

那麼你可以撥打:

My::Hello->merhaba('StackOverflow'); 

,並得到想要的結果。

無論哪種方式工作,繼承路由更易於維護,但將該方法添加到現有軟件包會導致更快的方法調用。

編輯:

正如在評論中指出,有少數病例水珠分配將與繼承相抵觸運行,所以如果有疑問,使用第一種方法(在子包中創建一個新方法)。

邁克爾·卡曼認爲這兩種技術組合成一個自我重新定義函數:

sub merhaba { 
    if (my $method = eval { $_[0]->can('hello') }) { 
     no warnings 'redefine'; 
     *merhaba = $method; 
     goto &merhaba; 
    } 
    die "Can't make 'merhaba' an alias for 'hello'"; 
} 
+0

是否會有任何繼承的情況下,* * My :: Hello :: merhaba = My :: Hello-> can('hello')'不起作用?任何子類都會將merhaba視爲一種合適的方法,即使「hello」由父包中的繼承處理,它仍然會找到正確的方法。如果'hello'與自動加載器一起提供,並且自動加載器保持某種狀態(在功能意義上不是純粹的),它將不起作用。 – 2010-02-15 18:30:32

+0

+1好工作。抱歉,我發佈了一個相互競爭的答案起初,我只是不明白這個問題是如何與調用者/ wantarray相關的。 – 2010-02-15 18:49:04

+0

Storm * * My :: Hello :: merhaba = My :: Hello-> can('hello')'如果任何子類覆蓋'hello',將不會做正確的事情。我不知道是否有任何方法讓glob賦值繼承工作。我寧願接受一個不會陷入問題領域的答案。 – 2010-02-15 19:30:31

3

您可以通過操縱符號表的別名子程序:

*My::Merhaba::merhaba = \&My::Hello::hello; 

一些例子可以發現here

+1

無論繼承如何,總是會調用'My :: Hello :: hello'。 – 2010-02-15 16:11:38

2

我不知道正確的方法是什麼,但亞當·肯尼迪將使用Method::Alias你的第二個方法(即沒有0​​)(click here to go directly to the source code )。

+0

謝謝。這有助於我理解Eric Strom的答案。就在你提到的引號之前,亞當說,「注意,這給調用者數組增加了額外的條目,但除非你偏執於這些事情,否則這並不重要。」*所以,這意味着使用' goto'更好,因爲它避免了向調用者數組添加*「額外條目」*。這會干擾'wantarray'(正如Eric Strom所提到的),因爲它會改變對真實方法「想要」的看法。是對的嗎? – 2010-02-15 18:51:37

+1

我不認爲'wantarray'會受到影響;上下文應該在任何一種情況下都能正確傳播,只要調用是包裝器中的最後一個語句即可。即它不會像'my @results = $ self-> method(); return @ results'。如果方法使用'caller'(不太可能)或者像'carp'或'croak'(更可能)的東西,使用'goto'會隱藏這個調用框架。 – 2010-02-15 22:34:27

+1

@molecules =>假定方法在正確的返回上下文中調用(未分配給變量,然後返回或其他類似的東西(Sinan的示例很好)),那麼無論額外的堆棧幀如何,該上下文都會向前傳播,在我的回答中提到它只是指出'goto'處理 – 2010-02-16 03:24:42

1

這是Quick-n-Dirty與使用UNIVERSAL::can的一種間接修改的組合。

package My::Merhaba; 
use base 'My::Hello'; 
# ... 
*merhaba = __PACKAGE__->can('hello'); 

,你就會有稱爲子在這個包,別名My::Hello::hello「merhaba」。你只是在說,無論這個軟件包在hello這個名字下做什麼,它都可以用merhaba這個名字做。

但是,這是不夠的可能性,某些代碼修飾器可能會更改*My::Hello::hello{CODE}指向的子。在這種情況下,Method::Alias可能是指定方法的適當方式,正如分子所示。

但是,如果你控制了雙方家長和孩子一類比較好控制的庫,那麼上面的方法是slimmmer

相關問題