4

質問というクラスがあり、質問の種類に応じてサブクラスがたくさんあります。サブクラスに対してオブジェクトを作成することはできますが、クラスQuestion自体のオブジェクトを作成することはできません。

#! /usr/bin/env perl

use strict;
use warnings;

#
# LOAD IN YOUR QUESTIONS HERE
#

my @list_of_questions;
for my $question_type qw(Science Math English Dumb) {
    my $class = "Question::$question_type";
    my $question = $class->new;
    push @list_of_questions, $question;
}

package Question;
use Carp;

sub new {
    my $class = shift;

    my $self = {};

    if ( $class = eq "Question" ) {
       carp qq(Need to make object a sub-class of "Question");
       return;
    }

    bless $self, $class;
    return $self;
}
yadda, yadda, yadda...

package Question::Math;
use parent qw(Question);
yadda, yadda, yadda...

package Question::Science;
use parent qw(Question);
yadda, yadda, yadda...

package Question::English;
use parent qw(Question);
yadda, yadda, yadda...

これらはモジュールではなく、プログラムで使用するために定義したクラスにすぎないことに注意してください。したがって、実行時にモジュールのロードをテストすることはできません。

上記を実行すると、次のようになります。

Can't locate object method "new" via package "Question::Dumb" (perhaps you forgot to load "Question::Dumb"?)

この特定のエラーをキャッチする方法はありますか?それで私はそれを自分で処理できますか?有効な型の配列を作成できることはわかっていますが、配列を更新することを忘れずに、新しい質問型を追加できることを何らかの方法で望んでいました。

4

3 に答える 3

4

AFAICTで実行したいのは、シンボルテーブルをチェックして、「クラス」(別名「パッケージ」)が定義されているかどうかを確認することです。手動で行うのは難しいことではありませんが、Class :: Loadは少し読みやすい砂糖を提供し、「ヒューリスティック」を適用します。このモジュールを使用したくない場合は、is_class_loadedのソースコードを使用すると、実際に探している答えが見つかります。

use Class::Load qw(is_class_loaded);

for my $question_type (qw(Math English Science Dumb)) {
   my $class = "Question::$question_type";
   if(!is_class_loaded($class)) {
         # construct your new package at runtime, then
   }

   new_question($class);

} 

変数名( "class_type")がおかしいので、修正しました。Module :: Loadの方が優れているかどうかもわかりませんが、これにはClass::Loadを使用しています。

編集:裸のqw()は、新しいPerl(5.14?)の1つで非推奨になりました。これはばかげた非推奨ですが、そこにあるので、qw()foreachsを括弧で囲むことを学ぶ必要があります。

于 2013-01-14T18:58:42.590 に答える
0

これが私がついにやったことです:

package Question;
use Carp;

sub new {
    my $class = shift;
    my %params = @_;

    #
    # Standardize the Parameters
    # Remove the dash, double-dash in front of the parameter and
    # lowercase the name. Thus, -Question, --question, and question
    # are all the same parameter.
    #

    my %option_hash;

    my $question_type;
    for my $key (keys %params) {

        my $value = $params{$key};

        $key =~ s/^-*//;    #Remove leading dashes
        $key = ucfirst ( lc $key ); #Make Key look like Method Name

        if ( $key eq "Type" ) {
            $question_type = ucfirst (lc $value);
        }
        else {
            $option_hash{$key} = $value;
        }
    }

    if ( not defined $question_type ) {
        carp qq(Parameter "type" required for creating a new question.);
        return;
    } 

    #
    # The real "class" of this question includes the question type
    #

    my $self = {};
    $class .= "::$question_type";
    bless $self, $class;

    #
    # All _real does is return a _true_ value. This method is in this
    # class, so all sub-classes automatically inherit it. If the eval
    # fails, this isn't a subclass, or someone wrote their own `_real_
    # method in their sub-class.
    #

    eval { $self->_real; };
    if ( $@ ) {
        carp qq(Invalid question type of $question_type);
        return;
    }

    #
    # Everything looks good! Let's fill up our question object
    #

    for my $method ( keys %option_hash ) {
        my $method_set;
        eval { $method_set = $self->$method( $option_hash{$method} ) };
        if ( $@ or not $method_set ) {
            carp qq(Can't set "$method" for question type "$question_type");
            return;
        }
    }

    return $self;
}

今、私はこのように私の質問を設定しています:

my $question = Question->new(
    --type     => Integer,
    --question => "Pick a number between 1 and 10.",
    --help     => "Try using the top row of your keyboard...",
    --from     => "1",
    --to       => "10",
);

if ( not defined $question ) {
    die qq(The question is invalid!);
}

のダーチ使用Try::Tinyはいいです。すべてをでラップするよりもずっと見栄えがしますeval。残念ながら、これは標準モジュールではありません。このプログラムはほぼ100の個別のシステムで実行されており、CPANモジュールの使用は非常に困難です。これらのシステムはファイアウォールの背後にあり、CPAN Webサイトにアクセスできないため、これは特に当てはまります。

私は基本的にDarchのメソッドを使用しますが_real、オブジェクトを祝福した後に試すスーパークラスのメソッドを作成します。それが実行される場合(それが私が本当に気にするすべてです)、これは私のスーパークラスのサブクラスです。

これは私が本当に望んでいることを行います:私のスーパークラスの後ろに私のサブクラスを隠します-そうするのと同じようFile::Specに。私のクラスのほとんどは同じメソッドを持っており、いくつかは1つまたは2つの追加のメソッドを持っています。たとえば、私の正規表現の質問タイプには、指定された回答が指定されたパターンと一致することを確認できるパターンメソッドがあります。

于 2013-01-15T16:04:24.667 に答える
0

呼び出し元のコードで例外をスローしないような式を使用することはできませんが、Invalid::Class->new()例外処理でラップしてメソッド内でラップすることはできます。標準パターンは、ファクトリメソッドに作成するサブクラスを説明する「type」引数を提供することです。一般的なアンチパターンは、そのファクトリメソッドを基本クラスに配置し、循環依存関係を作成し、必要以上の作業を行う必要があることです。

通常、インターフェイスクラスにファクトリメソッドを設定し、関連のない専用の基本クラスのサブクラスを構築します。失敗すると、警告またはスローが発生する可能性があります。コードでは、それは次のようになります。

package Question;

use Try::Tiny;
use Carp qw/carp/;

sub new {
    my ($class, $type, @args) = @_;

    # could do some munging on $type to make it a class name here
    my $real_class = "Question::$type";

    return try {
        $real_class->new(@args);
    } catch {
        # could differentiate exception types here
        carp qq(Invalid Question type "$type");
    };
}

package Question::Base;

sub new {
    my ($class) = @_;

    return bless {} => $class;
}

package Question::Math;
use base 'Question::Base'; # `use parent` expects to load a module

package main;

use Test::More tests => 2;
use Test::Warn;

isa_ok(Question->new('Math'), 'Question::Math');
warning_like(
    sub { Question->new('Dumb') }, # I hear there's no such thing
    qr/^Invalid Question/
);
于 2013-01-17T20:23:38.237 に答える