7

次のコードを想定します。

package Thing;
sub new {
    my $this=shift;
    bless {@_},$this;
}
sub name {
    my $this=shift;
    if (@_) {
        $this->{_name}=shift;
    }
    return $this->{_name};
}

次のようにオブジェクトをインスタンス化したとします。

my $o=Thing->new();
$o->name('Harold');

十分です。次のいずれかを使用して、同じものをより迅速にインスタンス化することもできます。

my $o=Thing->new(_name=>'Harold');  # poor form
my $o=Thing->new()->name('Harold');

確かに、属性をコンストラクターに渡して、「フレンドリーな」クラスがより完全にオブジェクトを作成できるようにしました。また、次のコードで clone-type オペレーターを許可することもできます。

my $o=Thing->new(%$otherthing);  # will clone attrs if not deeper than 1 level

これはすべて順調です。検証などを可能にするために、メソッドの背後に属性を隠す必要があることを理解しています。

$o->name;  # returns 'Harold'
$o->name('Fred'); # sets name to 'Fred' and returns 'Fred'

しかし、これが許可しないのは、それ自体に基づく属性の簡単な操作です。たとえば、次のようになります。

$o->{_name}=~s/old/ry/;  # name is now 'Harry', but this "exposes" the attribute

1 つの代替方法は、次のようにすることです。

# Cumbersome, not syntactically sweet
my $n=$o->name;
$n=~s/old/ry/;
$o->name($n);

別の可能性は、次の方法です。

sub Name :lvalue {  # note the capital 'N', not the same as name
    my $this=shift;
    return $this->{_name};
}

これで、次のことができます。

$o->Name=~s/old/ry/;

だから私の質問はこれです...上記の「コーシャ」ですか?それとも、そのように属性を公開するのは悪い形ですか? つまり、それを行うと、「name」メソッドで見つかる可能性のある検証が取り除かれます。たとえば、'name' メソッドが最初の文字を大文字にし、その後に小文字を強制する場合、'Name' (大文字の 'N') はそれをバイパスし、クラスのユーザーにその使用を強制します。

それで、「Name」左辺値メソッドが正確に「コーシャ」でない場合、そのようなことを行うための確立された方法はありますか?

私は、関連付けられたスカラーのようなものを属性として考えました (しかし考えるとめまいがします)。確かに、それは進むべき道かもしれません。

また、おそらく役立つオーバーロードはありますか?

または、次のような代替メソッドを作成する必要があります(機能する場合):

sub replace_name {
    my $this=shift;
    my $repl=shift;
    my $new=shift;
    $this->{_name}=~s/$repl/$new/;
}
...
$o->replace_name(qr/old/,'ry');

前もって感謝します...そして注意してください、私はOOP自体にかなり精通していますが、PerlのブランドのOOPについてはあまり経験がありません。

追加情報: 私は自分のインターフェイスで本当にクリエイティブになれると思います... これは私がいじくり回したアイデアですが、実際には限界がないことを示していると思います:

sub name {
    my $this=shift;
    if (@_) {
        my $first=shift;
        if (ref($first) eq 'Regexp') {
            my $second=shift;
            $this->{_name}=~s/$first/$second/;
        }
        else {
            $this->{_name}=$first;
        }
    }
    return $this->{_name};
}

これで、 name 属性を次のいずれかで設定できます

$o->name('Fred');

または私はそれを操作することができます

$o->name(qr/old/,'ry');  # name is now Harry

これはまだ $o->name.=' Jr.'; のようなものを許可していません。しかし、それを追加するのはそれほど難しくありません。一体、calllback 関数を渡せるようにすることはできたのではないでしょうか?

4

4 に答える 4

4

最初のコード例はまったく問題ありません。これは、アクセサーを記述する標準的な方法です。もちろん、これは置換を行うときに醜くなる可能性があります。最良の解決策は次のとおりです。

$o->name($o->name =~ s/old/ry/r);

/rフラグは置換の結果を返します。同様に:

$o->name(do { (my $t = $o->name) =~ s/old/ry/; $t });

そうですね、この 2 番目のソリューションは確かに醜いです。しかし、フィールドへのアクセスは、設定よりも一般的な操作だと思います。

個人的なスタイルの好みに応じて、 と など、取得と設定に 2 つの異なる方法を使用できnameますset_name。(接頭辞は良い考えではないと思いますget_- 4 つの不要な文字)。

名前の一部を置き換えることがクラスの中心的な側面である場合、これを特別なsubstitute_nameメソッドにカプセル化することは良い考えのように思えます。それ以外の場合、これは不必要なバラストであり、時折の構文上の問題を回避するための悪いトレードオフです。

これらは実験的であるため、左辺値メソッドを使用することはお勧めしません。

結合されたスカラーを返す「巧妙な」コードを表示 (およびデバッグ) したくありません。これは機能しますが、そのようなソリューションに慣れるには少し壊れやすいと感じています.

演算子のオーバーロードは、アクセサーの記述には役立ちません。特に割り当ては、Perl ではオーバーロードできません。


アクセサーを書くのは、特に検証を行わない場合は退屈です。Class::Accessorなど、自動生成を処理できるモジュールがあります。これにより、汎用アクセサーがクラスに追加getsetれ、要求に応じて特定のアクセサーが追加されます。例えば

package Thing;
use Class::Accessor 'antlers';  # use the Moose-ish syntax
has name => (is => 'rw');  # declare a read-write attribute

# new is autogenerated. Achtung: this takes a hashref

それで:

Thing->new({ name => 'Harold'});
# or
Thing->new->name('Harold');
# or any of the other permutations.

Perl 用の最新のオブジェクト システムが必要な場合は、互換性のある実装が並んでいます。これらの中で最も機能が豊富なのはMooseで、属性に検証、型制約、デフォルト値などを追加できます。例えば

package Thing;
use Moose; # this is now a Moose class

has first_name => (
  is => 'rw',
  isa => 'Str',
  required => 1, # must be given in constructor
  trigger => \&_update_name, # run this sub after attribute is set
);
has last_name => (
  is => 'rw',
  isa => 'Str',
  required => 1, # must be given in constructor
  trigger => \&_update_name,
);
has name => (
  is => 'ro',  # readonly
  writer => '_set_name', # but private setter
);

sub _update_name {
  my $self = shift;
  $self->_set_name(join ' ', $self->first_name, $self->last_name);
}

# accessors are normal Moose methods, which we can modify
before first_name => sub {
  my $self = shift;
  if (@_ and $_[0] !~ /^\pU/) {
    Carp::croak "First name must begin with uppercase letter";
  }
};
于 2013-09-12T14:45:04.227 に答える