2012-10-19 11 views
6

私がfooという名前の基本クラスを、持っている:: Baseが、私は「新しい」のように、そのメソッドを継承すると範囲内のいくつかのサブルーチン名をインポートする必要があります。Perl:基本クラスからサブルーチンをインポートするには?

package Foo::Base; 

sub new { ... } 

sub import { 
    no strict 'refs'; 

    my $caller = caller; 

    *{"${caller}::my_sub"} = sub { 1 }; 
} 

1; 

だから、私はこのベースを使用する必要があります私の第二のクラスでは、クラス、Fooの::子供:

use base 'Foo::Base'; 

...そして、それは継承のために動作しますが、それがスコープ内に「my_sub」をインポートしません。私はそれのために、文字列

use Foo::Base; 

を追加することができ、それが役立ちますが、私はこのような何かを書きたくない:

use base 'Foo::Base'; 
use Foo::Base; 

これは奇妙なの一種に見える...のための任意の提案はありますこの問題?

答えて

6

use baseと書くと、baseモジュールの機能が使用されています。そして、基本クラスにしたいモジュールのパラメータを渡しています。

OO IS-A関係では、インポートは必要ありません。オブジェクト指向パターン($object_or_class->method_name(@args))を使用してメソッドを呼び出します。ベースモジュールと継承で定義されたあなたは使用する場合ユーティリティ、

sub inherited_util { 
    my (undef, @args) = @_; 
    ... 
} 

または

sub inherited2 { 
    shift; 
    ... 
} 

しかし:時々、それはあなたがインボカントがそうのような、誰であるか気にしないことを意味しそのモジュールで定義されたクラスの振る舞いからのの場合、まさに2つのuseステートメントが示すものです。

しかし、モジュールで使用する2つの異なるタイプの動作がある場合は、ユーティリティタイプのものをそれぞれのモジュールに分割して両方のモジュールから使用するほうがよいでしょう。いずれにしても、明示的な動作は暗黙的な動作よりも優れていることがよくあります。

しかしは、私が前にこのパターンを使用しています

sub import { 
    shift; 
    my ($inherit_flag) = @_; 
    my $inherit 
     = lc($inherit_flag) ne 'inherit' ? 0 
     : shift() && !!shift()    ? 1 
     :         0 
     ; 
    if ($inherit) { 
     no strict 'refs'; 
     push @{caller().'::ISA'}, __PACKAGE__; 
     ... 
    } 
    ... 
} 

そのように、私は用法を明示的に束ねて1本の電話をかけます。

use UtilityParent inherit => 1, qw<normal args>; 
+0

はい、これはMojo :: BaseまたはClass :: Simpleと非常によく似ています。しかし、私はより良いコードアーキテクチャをあきらめることにしました=) – shootnix

14

あなたがやっていることをしたいかもしれない2つの理由があります。どちらも悪いです。

まず、何らかの理由で親クラスからメソッドをインポートしようとしていますか?多分OOの仕組みを誤解しているかもしれません。あなたはそれをする必要はありません。継承されたメソッドをメソッドとして呼び出すだけで、それらのメソッドが何か変なことをしていなければ正常に動作します。

これは、いくつかがメソッドであり、そのうちのいくつかがインポート関数である混合使用モジュールである可能性があります。そしてそのためにあなたはすることができます...

use base 'Foo::Base'; 
use Foo::Base; 

そしてあなたは正しく見えますが、それは奇妙なものです。エクスポートするクラスでも、イディオムが混在していて、それは奇妙な使用パターンにつながります。

関数をエクスポートしたり、関数を独自のモジュールに分割したり、クラスメソッドにするのではなく、クラスを再設計することをお勧めします。関数が本当にクラスとはあまり関係がない場合は、それらをスピンさせるのが最善です。彼らがクラスに関連している場合は、それらをクラスメソッドにします。

use base 'Foo::Base'; 

Foo::Base->some_function_that_used_to_be_exported; 

これにより、インターフェイスの不一致がなくなり、ボーナスとして、他の方法と同様にサブクラスがクラスメソッドの動作をオーバーライドできます。

package Bar; 

use base 'Foo::Base'; 

# override 
sub some_function_that_used_to_be_exported { 
    my($class, @args) = @_; 

    ...do something extra maybe... 

    $class->SUPER::some_function_that_used_to_be_exported(@args); 

    ...and maybe something else... 
} 

あなたは、基本クラスのコントロールを持っていない場合、あなたはまだ方法にエクスポート機能をオンサブクラスを書くことでSANEインターフェースを作ることができます。

package SaneFoo; 

use base 'Foo::Base'; 

# For each function exported by Foo::Base, create a wrapper class 
# method which throws away the first argument (the class name) and 
# calls the function. 
for my $name (@Foo::Base::EXPORT, @Foo::Base::EXPORT_OK) { 
    my $function = Foo::Base->can($name); 
    *{$name} = sub { 
     my $class = shift; 
     return $function->(@_); 
    }; 
} 
+0

実際に私はMojo :: Baseのようなこのクラスに文法的な砂糖を追加したかったのですが、あなたは正しいです、それは私の現在の過労です私がMojo :: Baseを使うことができたら、私はやっただろうが、私は傾ける。 – shootnix

+0

その場合は、Mojo :: Baseのような 'use Foo -base'の規約を使用してください。 'use Foo'が輸出と継承の両方を行うMooseルートに行くことはお勧めしません。これは、J :: Random :: Moduleがそれを行うためのあまりにも多くの規則を破ります。これがあなた自身のプロジェクトのMooseと等価でない限り。 Soooo ...なぜMojo :: Baseを使えないの? – Schwern

1

もう少しの質問明確にするために「?しかし、なぜFooの中にインポートがないの::子供仕事は」、ここで実際に何が起こっているか綴るするプログラムです:

use Foo::Child; 
print my_sub(); # Really? Yes, really! 
print Foo::Child::my_sub()," done\n"; 

とはFoo ::ベース::インポート()ルーチンにこれを追加しました:

print "Caller is $caller\n"; 

をあなたはこれを実行すると、あなたが出力としてこれを参照してください。

Caller is main 
1 
Undefined subroutine &Foo::Child::my_sub called at foo_user.pl line 4. 

Foo :: Baseは、発信者が誰であるかを報告しています。これはメインプログラムです。私たちは '1'を参照します。これは、yes、main :: my_subが存在することを示しています。そして、インポートが間違った名前空間に移動したために失敗しました。

これはなぜですか?インポートプロセスはすべてメインプログラムによって処理されているためです。 Foo :: Baseのインポートは、Foo :: Childの何ものによっても呼び出されません。モジュールをロードする過程でメインプログラムから呼び出されています。実際には、メソッドとは違ってサブタイプを強制的にFoo :: Childにインポートしたい場合、Foo :: Child自体で明示的にそれを実行させる必要があります。 'use Foo :: Base'は、Foo :: Childを呼び出し元としてインポートを実行するため、これを行います。あなたがインポートを主張しているが、二重使用があなたをあまりにも嫌うなら、 'use base'の直後にFoo :: Base :: import()を呼び出すことができます。とにかくこれは二重の「使用」とまったく同じです。

しかし、私はSchwernのクラスメソッドがより好きです。私はその代替方法をお勧めします。

関連する問題