-6

基本的にプロパティのオーバーロードに要約されるPHPからいくつかのコードを移植しようとしています。つまり、実際にはクラスの一部として定義されていないクラス プロパティを取得または設定しようとすると、その情報が関数に送られ、その情報を使ってほとんどの処理が行われます。(この場合は諦める前に連想配列をクラス内で検索したい。)

ただし、Perl は... クラスが既にハッシュであることを考えると、PHP とはかなり異なります。実際にプロパティを取得または設定しようとするものに対して透過的に、そのパッケージに完全にカプセル化されたままになるPerlの「クラス」__get()と同等のものを適用できる方法はありますか?__set()

編集:これを説明する最良の方法は、コードを表示し、出力を表示してから、出力したいものを表示することです。

package AccessTest;

my $test = new Sammich;   #"improper" style, don't care, not part of the question.

say 'bacon is: ' . $test->{'bacon'};
say 'cheese is: ' . $test->{'cheese'};

for (keys $test->{'moreProperties'}) {
 say "$_ => " . $test->{'moreProperties'}{$_};
}

say 'invalid is: ' . $test->{'invalid'};

say 'Setting invalid.';
$test->{'invalid'} = 'true';
say 'invalid is now: ' . $test->{'invalid'};

for (keys $test->{'moreProperties'}) {
 say "$_ => " . $test->{'moreProperties'}{$_};
}


package Sammich;

sub new
{
 my $className = shift;
 my $this = {
  'bacon' => 'yes',
  'moreProperties' => {
  'cheese' => 'maybe',
  'ham' => 'no'
  }
};

return bless($this, $className);
}

これは現在出力します:

bacon is: yes
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 11.
cheese is: 
cheese => maybe
ham => no
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 17.
invalid is: 
Setting invalid.
invalid is now: true
cheese => maybe
ham => no

ここで、最初の AccessTest パッケージにはまったく変更を加えずに、Sammich のみに変更を加える必要があります。これにより、次のようになります。

bacon is: yes
cheese is: maybe
cheese => maybe
ham => no
invalid is: 0
Setting invalid.
invalid is now: true
cheese => maybe
ham => no
invalid => true

ご覧のとおり、望ましい効果は、'cheese' プロパティがテスト オブジェクトの一部ではないため、'moreProperties' ハッシュから取得されることです。「無効」は同じことを試みますが、直接のプロパティでも「moreProperties」でもないため、プログラムされた方法で動作します。この場合、エラーや警告。「無効な」プロパティを設定しようとすると、オブジェクトにまだ追加されていないため、オブジェクトに直接追加されませんが、代わりに「moreProperties」ハッシュに追加されます。

これには、PHP で必要な 6 行程度よりも多くの行が必要になると予想していますが、これは OOP の非常に重要な概念であるため、Perl が何らかの形で処理することを完全に期待しています。

4

4 に答える 4

10

コメントで述べたように、この問題が難しい理由は、オブジェクト指向プログラミングの黄金のルールの1つ、つまりカプセル化に従わないためです。メソッドではない呼び出しをインターセプトすることをどのように期待できますか?公開されたAPIがgetter/setterで構成されている場合は、AUTOLOADメソッドを使用して不明なメソッド呼び出しをインターセプトできます。そうでない場合は、タイドハッシュを使用するという@pilcrowの高貴な提案を使用できます(編集:または@tchristのoverloadプラグマの勇敢な使用)。それでも、これはAPIよりもPerlの柔軟性への賛辞です。

これをより正確に行うには(そうです、APIを変更しないことが「必要」です。これを無視する場合は、この投稿を将来の読者へのメッセージと呼んでください)。

#!/usr/bin/env perl

use v5.10; # say
use strict;
use warnings;

use MooseX::Declare;
use Method::Signatures::Modifiers;

class Sammich {
  has 'bacon' => ( isa => 'Str', is => 'rw', default => 'yes' );
  has 'more' => ( 
    isa => 'HashRef', 
    is => 'rw', 
    default => sub{ {
      cheese => 'maybe',
      ham => 'no',
    } },
  );

  our $AUTOLOAD;
  method AUTOLOAD ($val?) {
    # get unknown method name
    my $attr = (split( /::/, $AUTOLOAD))[-1];

    my $more = $self->more;

    # see if that method name exists in "more"
    if (exists $more->{$attr}) {
      # if so, are there new values? then set
      if (defined $val) {
        $more->{$attr} = $val;
      } 
      # ... and return
      return $more->{$attr};
    }

    # attr does not exist, so set it or initialize it to 0
    $more->{$attr} = defined $val ? $val : 0;
    return $more->{$attr};
  }
}

# I don't care that you don't care
my $test = Sammich->new();

say 'bacon is: ' . $test->bacon;
say 'cheese is: ' . $test->cheese;

for (keys %{ $test->more }) {
 say "$_ => " . $test->more->{$_};
}

say 'invalid is: ' . $test->invalid;

say 'Setting invalid.';
$test->invalid( 'true' );
say 'invalid is now: ' . $test->invalid;

for (keys %{ $test->more }) {
 say "$_ => " . $test->more->{$_};
}

ここでの私の言い回しは厳しいと言う人もいるかもしれませんが、おそらくそうです。私は助けられる人を助けようとしているので、次のような太字のメッセージが表示されます

最初のAccessTestパッケージにまったく変更を加えずに、Sammichのみに変更を加える必要があります。

次に、Perlが気まぐれに屈することを要求します

これにはPHPで必要となる6行以上かかると予想していますが、これはOOPの非常に重要な概念であるため、Perlが何らかの形で処理することを完全に期待しています。

面倒です。将来の読者が、これをカプセル化が長期的に役立つ理由の事例として見てくれることを願っています。

于 2012-06-10T04:38:42.627 に答える
6

アップデートにアップデート

(おそらくあなたの誤ったアプローチを助長したために、匿名の反対票を受け取っています。:))

明確にするために、あなたが提起する質問は「XY問題」であり、具体的にはPHPからPerlへのオブジェクト指向技術の誤った翻訳の成果物です。たとえば、この質問でpassimが言及されているように、perl のオブジェクト プロパティは、通常、直接アクセスされる hash(ref) 要素として実装されるべきではありません。それは間違った「X」です。

ある言語から別の言語へのジャンプは、単なる構文の違い以上のものをもたらします。

アップデート

次のようなもので、必要なことを達成できます。

package UnfortunateHack; {

use Tie::Hash;
our @ISA = qw(Tie::StdHash);

sub FETCH {
  my ($self, $key) = @_;
  return exists $self->{$key}
         ? $self->{$key}
         : $self->{moreProperties}->{$key};
}

}

...

package Sammich;

sub new {
  my $class = shift;

  tie my %this, 'UnfortunateHack';
  %this = ( bacon => 'yes',
            moreProperties => { ... } );

  bless \%this, $class;
}

元の回答

必ずしも定義されていない$obj->property呼び出しをインターセプトするというあなたの意図を理解できれば、OO コンテキストでの perl のAUTOLOADはあなたが望むことを実行します。TheClass::property

リンクされたドキュメント(perlobj)から:

クラスに存在しないメソッドを呼び出すと、Perl はエラーをスローします。ただし、そのクラスまたはその親クラスのいずれかで AUTOLOAD メソッドが定義されている場合は、その AUTOLOAD メソッドが代わりに呼び出されます。

于 2012-06-08T13:27:40.047 に答える
4

カプセル化に完全に違反しています。blessそれを証明するために、 fromをコメントアウトして&Sammich::new、プレーンなハッシュ参照を返すようにします。

package Sammich;

sub new {
  my $className = shift;
  my $this = {
    'bacon' => 'yes',
    'moreProperties' => {
      'cheese' => 'maybe',
      'ham' => 'no'
    }
  };

  # don't even bother blessing it
  # return bless($this, $className);
}

必要なものを取得する唯一の方法は、 を使用することmagicです。

于 2012-06-10T15:50:28.420 に答える
1

Perl では、クラスはハッシュ以上のものであり、パッケージに基づいて構築されており、必要なメソッドをそこに定義でき、そのパッケージ/クラスにカプセル化されたままになります。

Object-Oriented Programming in Perl Tutorial でコード例を確認できます。

于 2012-06-08T13:21:50.640 に答える