名前¶
perltooc - トムによるPerlのクラスデータのためのオブジェクト指向チュートリアル
概要¶
When designing an object class, you are sometimes faced with the situation of wanting common state shared by all objects of that class. Such class attributes act somewhat like global variables for the entire class, but unlike program-wide globals, class attributes have meaning only to the class itself.
オブジェクトクラスを宣言するとき、共通の状態をクラスの全てのオブジェクトが共有したい状況に直面することがあります。 そのようなクラス属性は、いくぶん、クラス全体のグローバル変数のようにはたらきます。 ですが、プログラム全体のグローバル変数とは違い、クラス属性は、クラスそれ自身にとってのみグローバルです。
Here are a few examples where class attributes might come in handy:
クラス属性が役に立つ例がいくつかあります。
to keep a count of the objects you've created, or how many are still extant.
生成したオブジェクトの数を保っておくために。また、まだいくつあるのかを数えておくために。
to extract the name or file descriptor for a logfile used by a debugging method.
デバッグメソッドで使われるログファイルの名前か、ファイル識別子を引き出すために。
to access collective data, like the total amount of cash dispensed by all ATMs in a network in a given day.
全体的なデータにアクセスするために。たとえば、与えられた日の、 ネットワークの全ATMで支払われた現金の総金額のようなものにアクセスするために。
to access the last object created by a class, or the most accessed object, or to retrieve a list of all objects.
クラスによって、最後に作られたオブジェクトにアクセスするために、 また、もっともアクセスされたオブジェクトにアクセスするために、 また、すべてのオブジェクトのリストを検索するために。
Unlike a true global, class attributes should not be accessed directly. Instead, their state should be inspected, and perhaps altered, only through the mediated access of class methods. These class attributes accessor methods are similar in spirit and function to accessors used to manipulate the state of instance attributes on an object. They provide a clear firewall between interface and implementation.
本当のグローバル変数とは違って、クラス属性は直接にアクセスされるべきではありません。 直接にアクセスされるべきではありませんが、クラス属性の状態は調べられるべきですし、たぶん、変更もされるべきです。 ただし、クラスメソッドを仲介して、アクセスすることを通してのみです。 クラス属性のアクセサメソッドは、気持ちと、機能の上では、 オブジェクトのインスタンス属性の状態を操作するのに使われるアクセサに似ています。 これらは、インターフェースと実装の間に透明なファイアウォールを提供します。
You should allow access to class attributes through either the class name or any object of that class. If we assume that $an_object is of type Some_Class, and the &Some_Class::population_count method accesses class attributes, then these two invocations should both be possible, and almost certainly equivalent.
クラス属性へのアクセスは、クラスの名前か、クラスのオブジェクトのどちらを通してもできるようにすべきです。 $an_object が、Some_Class の型のもので、&Some_Class::population_count メソッドが クラス属性にアクセスするとします。そうすると、2つの呼び出しが両方とも可能であるべきです。 そして、もちろん、ほとんど同じであるべきです。
Some_Class->population_count()
$an_object->population_count()
The question is, where do you store the state which that method accesses? Unlike more restrictive languages like C++, where these are called static data members, Perl provides no syntactic mechanism to declare class attributes, any more than it provides a syntactic mechanism to declare instance attributes. Perl provides the developer with a broad set of powerful but flexible features that can be uniquely crafted to the particular demands of the situation.
疑問があります。このメソッドがアクセスする、その状態をどこに置いておくのでしょうか? C++のような、より厳格な言語とは違って、Perlには静的なデータメンバと呼ばれる場所はありません。 Perlは、クラス属性を宣言するのに、総合のメカニズムはまったく提供しません。 インスタンス属性を宣言する総合のメカニズムしかありません。 Perlは開発者に、特殊な状況の要求にも、うまく作ることのできる、強力ですが、柔軟な、 大まかなセットを提供します。
A class in Perl is typically implemented in a module. A module consists of two complementary feature sets: a package for interfacing with the outside world, and a lexical file scope for privacy. Either of these two mechanisms can be used to implement class attributes. That means you get to decide whether to put your class attributes in package variables or to put them in lexical variables.
Perlのクラスは、典型的にモジュールに実装されます。モジュールは、2つの相補的な 特徴の組合わせで、できています:外の世界とのインターフェースのためのパッケージと、 プライバシーのためのレキシカルなファイルスコープのセットです。 これらの2つのメカニズムの両方を、クラス属性の実装に使うことができます。 このことは、すなわち、クラス属性をパッケージ変数に置くか、 レキシカル変数に置くかを決めることができるということです。
And those aren't the only decisions to make. If you choose to use package variables, you can make your class attribute accessor methods either ignorant of inheritance or sensitive to it. If you choose lexical variables, you can elect to permit access to them from anywhere in the entire file scope, or you can limit direct data access exclusively to the methods implementing those attributes.
また、これらは、作るのにあたっての唯一の判断ではありません。 パッケージ変数を選んだとしても、クラス属性アクセサメソッドに、 継承を無視させるか、継承に反応させることもできます。 レキシカル変数を選んだとしても、まったく、ファイルスコープの中のどこからでも、 クラス属性にアクセスすることを許すこともできますし、また、直接データにアクセスするのを、 排他的に、それらの属性を実装するメソッドだけに制限することも出来ます。
カンの中のクラスデータ(Class Data in a Can)¶
One of the easiest ways to solve a hard problem is to let someone else do it for you! In this case, Class::Data::Inheritable (available on a CPAN near you) offers a canned solution to the class data problem using closures. So before you wade into this document, consider having a look at that module.
難しい問題を解決するための、もっとも簡単な方法の一つは、 誰か他の人に問題を解決させることです! この場合、Class::Data::Inheritable(お近くのCPANで利用できます)が、 クロージャを使うクラスデータの問題の決まりきった解決法を提供します。 ですので、このドキュメントを苦労して読み進める前に、 Class::Data::Inheritable モジュールを見ることを検討してください。
パッケージ変数としてクラスデータを¶
Because a class in Perl is really just a package, using package variables to hold class attributes is the most natural choice. This makes it simple for each class to have its own class attributes. Let's say you have a class called Some_Class that needs a couple of different attributes that you'd like to be global to the entire class. The simplest thing to do is to use package variables like $Some_Class::CData1 and $Some_Class::CData2 to hold these attributes. But we certainly don't want to encourage outsiders to touch those data directly, so we provide methods to mediate access.
Perlのクラスは、本当に、ただのパッケージなので、クラス属性を持つのに、パッケージ変数を使うのは もっとも自然な選択です。つまり、パッケージに、単純にそれ自身のクラス属性をもたせるのです。 例えば、クラス全体にグローバルであるような、異なった属性がいくつか必要な Some_Class というクラスがあるとします。 もっとも簡単なのは、$Some_Class::CData1 や$Some_Class::CData2 のようなパッケージ変数を使い、 これらの属性を持たせることです。ですが、もちろん部外者がこれらのデータディレクトリに触ることを 勧めたくはありません。ですので、アクセスを仲介するメソッドを提供します。
In the accessor methods below, we'll for now just ignore the first argument--that part to the left of the arrow on method invocation, which is either a class name or an object reference.
下の、アクセサメソッドで、今のところ、第一引数 -- メソッド呼び出しの矢印の左の部分で、 クラスの名前か、オブジェクトリファレンス -- を、無視しています。
package Some_Class;
sub CData1 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$Some_Class::CData1 = shift if @_;
return $Some_Class::CData1;
}
sub CData2 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$Some_Class::CData2 = shift if @_;
return $Some_Class::CData2;
}
This technique is highly legible and should be completely straightforward to even the novice Perl programmer. By fully qualifying the package variables, they stand out clearly when reading the code. Unfortunately, if you misspell one of these, you've introduced an error that's hard to catch. It's also somewhat disconcerting to see the class name itself hard-coded in so many places.
このテクニックは、非常に読みやすく、普通のPerlプログラマにとってさえ、完全に直接です。 パッケージ変数を完全に修飾することで、コードを読むときに、はっきりと、目立ちます。 不運なことに、もし、これらの一つでも、スペルを間違えたら、捕まえがたいエラーを持ち込むことになります。 また、クラスの名前自身を多くの場所にハードコードしているのを見るのは、いくぶんイライラもさせます。
Both these problems can be easily fixed. Just add the use strict
pragma, then pre-declare your package variables. (The our
operator will be new in 5.6, and will work for package globals just like my
works for scoped lexicals.)
この2つの問題は、両方とも簡単に修正できます。 use strict
プラグマを加え、それから、 パッケージ変数を先に宣言するだけです。(our
演算子は、5.6の、新しいものです。 my
がスコープされたレキシカル変数に働くのと同じように、パッケージのグローバル変数に働きます。)
package Some_Class;
use strict;
our($CData1, $CData2); # our() is new to perl5.6
sub CData1 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$CData1 = shift if @_;
return $CData1;
}
sub CData2 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$CData2 = shift if @_;
return $CData2;
}
As with any other global variable, some programmers prefer to start their package variables with capital letters. This helps clarity somewhat, but by no longer fully qualifying the package variables, their significance can be lost when reading the code. You can fix this easily enough by choosing better names than were used here.
他のどのグローバル変数とも同じように、 これらパッケージ変数を大文字で始めるのを好むプログラマがいます。 このことは、いくぶん明瞭さを助けますが、パッケージ変数がまったく修飾されていないので、 コードを読むときにその重要性が失われるかもしれません。 これを修正するのは簡単で、ここで使われているよりも、もっと良い名前を選べば事足ります。
卵をみんな、一つのバスケットに置く(Putting All Your Eggs in One Basket)¶
Just as the mindless enumeration of accessor methods for instance attributes grows tedious after the first few (see perltoot), so too does the repetition begin to grate when listing out accessor methods for class data. Repetition runs counter to the primary virtue of a programmer: Laziness, here manifesting as that innate urge every programmer feels to factor out duplicate code whenever possible.
ちょうど、インスタンス属性のアクセサメソッドを思慮なく列挙するのが、 最初の数回の後には、飽き飽きするものになっていくように(perltootを参照)、 クラスデータのアクセサメソッドをリストするときも、 同じことのくり返しに、非常にイライラしてくるでしょう。 くり返しは、プログラマの主要な美徳、不精に反します。 ここでは、あらゆるプログラマが感じる生まれつきの衝動が、 出来るかぎりコードの二重化を睨むものとして現れます。
Here's what to do. First, make just one hash to hold all class attributes.
次ようにすべきです。最初に、全てのクラス属性を持つハッシュをただ一つ、作ります。
package Some_Class;
use strict;
our %ClassData = ( # our() is new to perl5.6
CData1 => "",
CData2 => "",
);
Using closures (see perlref) and direct access to the package symbol table (see perlmod), now clone an accessor method for each key in the %ClassData hash. Each of these methods is used to fetch or store values to the specific, named class attribute.
クロージャ(perlref参照)を使って、パッケージのシンボルテーブル(perlmod参照)に直接にアクセスします。 さて、%ClassDataハッシュの中のそれぞれのキーごとに、アクセサメソッドをクローニングします。 これらのメソッドは、値を持ってくるか、特定の指定されたクラス属性に値を格納するために使われます。
for my $datum (keys %ClassData) {
no strict "refs"; # パッケージに新しいメソッドを登録するために
*$datum = sub {
shift; # XXX: クラス/オブジェクトの呼出を無視する
$ClassData{$datum} = shift if @_;
return $ClassData{$datum};
}
}
It's true that you could work out a solution employing an &AUTOLOAD method, but this approach is unlikely to prove satisfactory. Your function would have to distinguish between class attributes and object attributes; it could interfere with inheritance; and it would have to careful about DESTROY. Such complexity is uncalled for in most cases, and certainly in this one.
&AUTOLOADメソッドを用いる解決法も、うまくはいきますが、 このアプローチを満足の行くものであると立証するのは、見込がありません。 関数はクラス属性とオブジェクト属性の間を区別しなければなりません。 継承に干渉します。また、DESTORYに注意する必要があります。 このような複雑性は、ほとんどの場合でお呼びではありません。 もちろん、この場合も。
You may wonder why we're rescinding strict refs for the loop. We're manipulating the package's symbol table to introduce new function names using symbolic references (indirect naming), which the strict pragma would otherwise forbid. Normally, symbolic references are a dodgy notion at best. This isn't just because they can be used accidentally when you aren't meaning to. It's also because for most uses to which beginning Perl programmers attempt to put symbolic references, we have much better approaches, like nested hashes or hashes of arrays. But there's nothing wrong with using symbolic references to manipulate something that is meaningful only from the perspective of the package symbol table, like method names or package variables. In other words, when you want to refer to the symbol table, use symbol references.
なぜ、strict refs をループで無効にしているのだろうと思うかも知れません。 パッケージのシンボルテーブルを操作して、新しい関数名をシンボリックリファレンスを使って(間接的な命名で) 導入しています。シンボリックリファレンスは、strict プラグマが禁じているものです。 ふつうは、シンボリックリファレンスは、せいぜい危なっかしい考えです。 ただ、意図しないときに、あやまって使われる可能性があるので、危なっかしいというだけです。 初心者のPerlプログラマがシンボリックリファレンスを置こうとする、使いかたのほとんどには、 ネストされたハッシュや配列のハッシュのような、より良いアプローチがあるからです。 ですが、メソッド名やパッケージ変数のようなパッケージのシンボルテーブルの観点からのみ、 意味があることを操作するのに、シンボリックリファレンスを使うのは、何も間違っていません。 言い替えれば、シンボルテーブルを参照したい場合は、シンボリックリファレンスを使ってください。
Clustering all the class attributes in one place has several advantages. They're easy to spot, initialize, and change. The aggregation also makes them convenient to access externally, such as from a debugger or a persistence package. The only possible problem is that we don't automatically know the name of each class's class object, should it have one. This issue is addressed below in "The Eponymous Meta-Object".
一つの場所に、全てのクラス属性を集積するのは、さまざまな利点があります。 クラス属性は、目に付きやすく、初期化しやすく、変更しやすい。 集約することで、クラス属性を外部から、例えば、 デバッガや永続的なパッケージから、アクセスするのが便利になります。 唯一ありそうな問題は、それぞれのクラスが持っているはずの クラスオブジェクトの名前を自動的には知らないことです。 この問題は、下の方の"The Eponymous Meta-Object"に、扱われています。
継承の懸念(Inheritance Concerns)¶
Suppose you have an instance of a derived class, and you access class data using an inherited method call. Should that end up referring to the base class's attributes, or to those in the derived class? How would it work in the earlier examples? The derived class inherits all the base class's methods, including those that access class attributes. But what package are the class attributes stored in?
派生クラスのインスタンスがあったとして、継承されたメソッド呼び出しを使い、 クラスデータにアクセスします。 このとき、結局、基底クラスの属性を参照することになるべきなのか、 派生クラスの属性を参照することになるべきでしょうか? さっきの例ではどのように動くでしょうか? 派生クラスは基底クラスの全てのメソッドを継承します。 クラス属性にアクセスするメソッドも含みます。 ですが、どのパッケージにクラス属性が格納されるのでしょうか?
The answer is that, as written, class attributes are stored in the package into which those methods were compiled. When you invoke the &CData1 method on the name of the derived class or on one of that class's objects, the version shown above is still run, so you'll access $Some_Class::CData1--or in the method cloning version, $Some_Class::ClassData{CData1}
.
答えは、書かれたように、クラス属性はそれらのメソッドがコンパイルされたパッケージに、 格納されます。&CData1 メソッドを、派生クラスの名前か、派生クラスのオブジェクトで呼び出すと、 上で示されたバージョンのものが動きます。つまり、$Some_Class::Cdata1 -- または、クローニングしているバージョンのメソッドで、 $Some_Class::ClassData{CData1}
に、アクセスできます。
Think of these class methods as executing in the context of their base class, not in that of their derived class. Sometimes this is exactly what you want. If Feline subclasses Carnivore, then the population of Carnivores in the world should go up when a new Feline is born. But what if you wanted to figure out how many Felines you have apart from Carnivores? The current approach doesn't support that.
派生クラスのコンテキストではなく、 基底クラスのコンテキストで実行するようなクラスメソッドを考えましょう。 お望みのものにびったりなことがあります。猫は肉食動物の下位に分類されれば、 新たに猫が生まれると、世界の肉食動物の固体数が増えます。 ですが、肉食動物とは切り離して、何匹の猫がいるかを数えたいならどうしましょう? 現在のアプローチではそれには対応できません。
You'll have to decide on a case-by-case basis whether it makes any sense for class attributes to be package-relative. If you want it to be so, then stop ignoring the first argument to the function. Either it will be a package name if the method was invoked directly on a class name, or else it will be an object reference if the method was invoked on an object reference. In the latter case, the ref() function provides the class of that object.
クラス属性が package-relative であることに筋が通っているかどうかを、 ケースバイケースの根拠で決める必要があるでしょう。 package-relative でありたいなら、関数の第一引数を無視するのをやめます。 第一引数は、メソッドがクラス名で直接に呼ばれたなら、パッケージ名、 オブジェクトリファレンスで呼ばれたなら、オブジェクトリファレンスの、どちらかです。 後者では、ref()関数がオブジェクトのクラス名を与えます。
package Some_Class;
sub CData1 {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $varname = $class . "::CData1";
no strict "refs"; # to access package data symbolically
$$varname = shift if @_;
return $$varname;
}
And then do likewise for all other class attributes (such as CData2, etc.) that you wish to access as package variables in the invoking package instead of the compiling package as we had previously.
以前にしたようなコンパイルしているパッケージではなく、 呼び出しているパッケージのパッケージ変数としてアクセスしたいと思っている、 他の全てのクラス属性(CData2などのように)と同じようにしたいでしょう。
Once again we temporarily disable the strict references ban, because otherwise we couldn't use the fully-qualified symbolic name for the package global. This is perfectly reasonable: since all package variables by definition live in a package, there's nothing wrong with accessing them via that package's symbol table. That's what it's there for (well, somewhat).
ここで、再び、一時的に strict references を禁止するのをやめましょう。 そうしないと、パッケージのグローバル変数のために、完全に適格なシンボル名を使うことが出来ないからです。 このことは、完全に合理的です:全てのパッケージ変数は、定義により、 パッケージに生きています。パッケージのシンボル名経由で、パッケージ変数に アクセスするのは何も間違っていません。そのためにそれがあるのです(たぶん)。
What about just using a single hash for everything and then cloning methods? What would that look like? The only difference would be the closure used to produce new method entries for the class's symbol table.
全てのもののために、ただ一つのハッシュを使い、メソッドを複製してはどうでしょうか? どのように見えるでしょう? 唯一の違いは、クラスのシンボルテーブルに新しいメソッドを生み出すのに、 クロージャが使われていることです。
no strict "refs";
*$datum = sub {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $varname = $class . "::ClassData";
$varname->{$datum} = shift if @_;
return $varname->{$datum};
}
クラスの名前のメタオブジェクト(The Eponymous Meta-Object)¶
It could be argued that the %ClassData hash in the previous example is neither the most imaginative nor the most intuitive of names. Is there something else that might make more sense, be more useful, or both?
前の例の、%ClassDataハッシュは、変数名が創意に富んでも、直観的でもないと、 主張されるかもしれない。もっと意味があるか、有益なものか、 その両方を備えたものはないでしょうか?
As it happens, yes, there is. For the "class meta-object", we'll use a package variable of the same name as the package itself. Within the scope of a package Some_Class declaration, we'll use the eponymously named hash %Some_Class as that class's meta-object. (Using an eponymously named hash is somewhat reminiscent of classes that name their constructors eponymously in the Python or C++ fashion. That is, class Some_Class would use &Some_Class::Some_Class as a constructor, probably even exporting that name as well. The StrNum class in Recipe 13.14 in The Perl Cookbook does this, if you're looking for an example.)
はい、ちょうどよくあります。"クラスのメタオブジェクト"に、 パッケージと同じ名前のパッケージ変数を使いましょう。 Some_Classのパッケージ宣言のスコープ内で、クラスのメタオブジェクトとして、 %Some_Classという、クラスの名前のハッシュを使いましょう。 (クラスの名前のハッシュは、コンストラクタをクラスの名前にする、 Python や、C++ 流儀のクラスをちょっと思い出させます。 つまり、Some_Classクラスが&Some_Class::Some_Class をコンストラクタとするということです。 おそらく、その名前を同様にエクスポートさえしているでしょう。 例をお探しなら、The Perl CookbookのStrNum クラスは、そのようにしています)。
This predictable approach has many benefits, including having a well-known identifier to aid in debugging, transparent persistence, or checkpointing. It's also the obvious name for monadic classes and translucent attributes, discussed later.
このありきたりなアプローチには、多くの利益があります。 よくわかる一意の名前を含むことで、デバッグや透過的な永続生やチェックポイントの助けになります。 後で述べる、一価のクラスと半透明な属性にとっても、明らかな名前です。
Here's an example of such a class. Notice how the name of the hash storing the meta-object is the same as the name of the package used to implement the class.
下のものは、そのようなクラスの例です。メタオブジェクトを格納しているハッシュの名前が、 どのようにして、クラスを実装するのに使われているパッケージの名前と同じにしているかに 気を付けてください。
package Some_Class;
use strict;
# 完全な名前を使ってクラスのメタオブジェクトを作る
our %Some_Class = ( # our() is new to perl5.6
CData1 => "",
CData2 => "",
);
# このアクセサはcalling-package-relative
sub CData1 {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
no strict "refs"; # to access eponymous meta-object
$class->{CData1} = shift if @_;
return $class->{CData1};
}
# こちらのアクセサは違います
sub CData2 {
shift; # XXX: ignore calling class/object
no strict "refs"; # to access eponymous meta-object
__PACKAGE__ -> {CData2} = shift if @_;
return __PACKAGE__ -> {CData2};
}
In the second accessor method, the __PACKAGE__ notation was used for two reasons. First, to avoid hardcoding the literal package name in the code in case we later want to change that name. Second, to clarify to the reader that what matters here is the package currently being compiled into, not the package of the invoking object or class. If the long sequence of non-alphabetic characters bothers you, you can always put the __PACKAGE__ in a variable first.
2番目のアクセサで、__PACKAGE__記法が、2つの理由から使われています。 1つ目の理由は、後で、名前を変えたくなった際に、コードにパッケージの名前を リテラルでハードコーディングするのは避けたいからです。 2つ目の理由は、コードを読む人に、ここで何が問題なのかをはっきりさせるためです。 ここでの問題は、現在コンパイルされているパッケージであり、 オブジェクトかクラスを呼び出したパッケージではないということです。 アルファベットでない文字列が長く続くのが面倒なら、 常に、変数の最初に、__PACKAGE__を置いてください。
sub CData2 {
shift; # XXX: クラス/オブジェクトの呼び出しを無視し、
no strict "refs"; # クラスの名前のメタオブジェクトにアクセスします。
my $class = __PACKAGE__;
$class->{CData2} = shift if @_;
return $class->{CData2};
}
Even though we're using symbolic references for good not evil, some folks tend to become unnerved when they see so many places with strict ref checking disabled. Given a symbolic reference, you can always produce a real reference (the reverse is not true, though). So we'll create a subroutine that does this conversion for us. If invoked as a function of no arguments, it returns a reference to the compiling class's eponymous hash. Invoked as a class method, it returns a reference to the eponymous hash of its caller. And when invoked as an object method, this function returns a reference to the eponymous hash for whatever class the object belongs to.
ずっとシンボリックリファレンスを使っていますが、悪くはありません。 ずいぶん多くの場所で、strict ref のチェックを無効にしているのに、うろたえがちな人がいるでしょうけど。 シンボリックリファレンスであれば、常に、本当のリファレンスを作れます(その逆は真ではありませんが)。 このシンボリックリファレンスから本当のリファレンスへの変換をするサブルーチンを作りましょう。 引数無しで呼ばれたら、コンパイルしているクラスの名前のハッシュへのリファレンスを返します。 クラスメソッドとして呼ばれたら、その呼び出したクラスの名前のハッシュへのリファレンスを返します。 オブジェクトメソッドとして呼ばれたら、この関数は、オブジェクトが属している、どんなクラスでも、 クラスの名前のハッシュへのリファレンスを返します。
package Some_Class;
use strict;
our %Some_Class = ( # our() is new to perl5.6
CData1 => "",
CData2 => "",
);
# tri-natured: function, class method, or object method
sub _classobj {
my $obclass = shift || __PACKAGE__;
my $class = ref($obclass) || $obclass;
no strict "refs"; # シンボリックリファレンスを本当のリファレンスへと変換する
return \%$class;
}
for my $datum (keys %{ _classobj() } ) {
# strict refs をオフにするので、
# シンボルテーブルにメソッドを登録できます。
no strict "refs";
*$datum = sub {
use strict "refs";
my $self = shift->_classobj();
$self->{$datum} = shift if @_;
return $self->{$datum};
}
}
間接的なクラスデータへのリファレンス¶
A reasonably common strategy for handling class attributes is to store a reference to each package variable on the object itself. This is a strategy you've probably seen before, such as in perltoot and perlbot, but there may be variations in the example below that you haven't thought of before.
クラス属性を取り扱うの合理的で一般的な作戦とは、 オブジェクトそれ自身で、それぞれのパッケージ変数へのリファレンスを格納することです。 これは、おそらく既にみた作戦です。perltootや、perlbotにあるようなものです。 ですが、まだ考えたことのないようなバリエーションの例が下にあります。
package Some_Class;
our($CData1, $CData2); # our() is new to perl5.6
sub new {
my $obclass = shift;
return bless my $self = {
ObData1 => "",
ObData2 => "",
CData1 => \$CData1,
CData2 => \$CData2,
} => (ref $obclass || $obclass);
}
sub ObData1 {
my $self = shift;
$self->{ObData1} = shift if @_;
return $self->{ObData1};
}
sub ObData2 {
my $self = shift;
$self->{ObData2} = shift if @_;
return $self->{ObData2};
}
sub CData1 {
my $self = shift;
my $dataref = ref $self
? $self->{CData1}
: \$CData1;
$$dataref = shift if @_;
return $$dataref;
}
sub CData2 {
my $self = shift;
my $dataref = ref $self
? $self->{CData2}
: \$CData2;
$$dataref = shift if @_;
return $$dataref;
}
As written above, a derived class will inherit these methods, which will consequently access package variables in the base class's package. This is not necessarily expected behavior in all circumstances. Here's an example that uses a variable meta-object, taking care to access the proper package's data.
上に書いたように、派生クラスが、これらのメソッドを継承します。 これらのメソッドは基底クラスのパッケージのパッケージ変数に持続的にアクセスします。 このことは、全ての状況で、必要とされ、期待された振舞ではありません。 変数、メタオブジェクトを使う例があります。 適当なパッケージのデータへのアクセスを解決します。
package Some_Class;
use strict;
our %Some_Class = ( # our() is new to perl5.6
CData1 => "",
CData2 => "",
);
sub _classobj {
my $self = shift;
my $class = ref($self) || $self;
no strict "refs";
# get (hard) ref to eponymous meta-object
return \%$class;
}
sub new {
my $obclass = shift;
my $classobj = $obclass->_classobj();
bless my $self = {
ObData1 => "",
ObData2 => "",
CData1 => \$classobj->{CData1},
CData2 => \$classobj->{CData2},
} => (ref $obclass || $obclass);
return $self;
}
sub ObData1 {
my $self = shift;
$self->{ObData1} = shift if @_;
return $self->{ObData1};
}
sub ObData2 {
my $self = shift;
$self->{ObData2} = shift if @_;
return $self->{ObData2};
}
sub CData1 {
my $self = shift;
$self = $self->_classobj() unless ref $self;
my $dataref = $self->{CData1};
$$dataref = shift if @_;
return $$dataref;
}
sub CData2 {
my $self = shift;
$self = $self->_classobj() unless ref $self;
my $dataref = $self->{CData2};
$$dataref = shift if @_;
return $$dataref;
}
Not only are we now strict refs clean, using an eponymous meta-object seems to make the code cleaner. Unlike the previous version, this one does something interesting in the face of inheritance: it accesses the class meta-object in the invoking class instead of the one into which the method was initially compiled.
今、strict ref を取り除いているだけでなく、クラスの名前のメタオブジェクトを使って、 コードクリーナーを作っているようです。前のバージョンとは違い、 これは、継承に直面すると、ちょっと面白いことをします: これは、呼び出しているクラスの、クラスのメタオブジェクトにアクセスします。 メソッドが最初にコンパイルされたクラスではありません。
You can easily access data in the class meta-object, making it easy to dump the complete class state using an external mechanism such as when debugging or implementing a persistent class. This works because the class meta-object is a package variable, has a well-known name, and clusters all its data together. (Transparent persistence is not always feasible, but it's certainly an appealing idea.)
クラスのメタオブジェクトのデータに簡単にアクセスすることが出来ます。 デバッギングや、永続クラスを実装するときのような外部のメカニズムを使って、 完全なクラスの状態を簡単にダンプできます。 このように働くのは、クラスのメタオブジェクトがパッケージ変数であり、よく知られた名前を持ち、 全てのそのデータを一緒にまとめているからです。 (透過的な永続生は常に適しているわけではありませんが、魅力的なアイデアであるのは確かです。)
There's still no check that object accessor methods have not been invoked on a class name. If strict ref checking is enabled, you'd blow up. If not, then you get the eponymous meta-object. What you do with--or about--this is up to you. The next two sections demonstrate innovative uses for this powerful feature.
まだ、オブジェクトのアクセサメソッドがクラスの名前で呼び出されていないことをチェックしていません。 strict ref のチェックが有効なら、壊れます。そうでなければ、クラスの名前のメタオブジェクトを得ます。 それを我慢するか、なんとかするかは、あなた次第です。 次の2つのセクションで、この強力な特徴の画期的な利用法を、デモンーストレーションします。
一価のクラス(Monadic Classes)¶
Some of the standard modules shipped with Perl provide class interfaces without any attribute methods whatsoever. The most commonly used module not numbered amongst the pragmata, the Exporter module, is a class with neither constructors nor attributes. Its job is simply to provide a standard interface for modules wishing to export part of their namespace into that of their caller. Modules use the Exporter's &import method by setting their inheritance list in their package's @ISA array to mention "Exporter". But class Exporter provides no constructor, so you can't have several instances of the class. In fact, you can't have any--it just doesn't make any sense. All you get is its methods. Its interface contains no statefulness, so state data is wholly superfluous.
Perlに載っている標準モジュールには、何の属性メソッドも一切ない、 クラスインターフェースを提供しているものがいくつかあります。 もっとも普通に使われているモジュールは、プラグマを数にいれなければ、 Exporter モジュールです。このモジュールは、コンストラクタも、属性もないクラスです。 Exporter の仕事は、単純に、モジュールが呼び出しもとに自分の名前空間の一部をエクスポートする、 標準のインターフェースを提供することです。 モジュールは 、それぞれのモジュールの@ISA配列に継承のリストをセットすることで、 Exporterの &import メソッドを使い、"Exporter"に記載します。 ですが、クラスの Exporter には、コンストラクタがありません。 そのため、複数のクラスのインスタンスを持つことが出来ません。 実際に、一つももてません -- 全く意味をなさない。 あなたが得る全てのものは、そのメソッドです、 そのインターフェースには、なんの状態も含みません。 ですので、状態のデータはまったく不要なのです。
Another sort of class that pops up from time to time is one that supports a unique instance. Such classes are called monadic classes, or less formally, singletons or highlander classes.
別の種類のクラスで、時間から時間までを取得するモジュールは、 単一のインスタンスをサポートするものです。 このようなクラスは一価のクラスと呼ばれます。 または、あまり正式ではありませんが、singletonsとか、highlander classesと呼ばれます。
If a class is monadic, where do you store its state, that is, its attributes? How do you make sure that there's never more than one instance? While you could merely use a slew of package variables, it's a lot cleaner to use the eponymously named hash. Here's a complete example of a monadic class:
クラスが一価であれば、その状態、すなわち、属性をどこに格納するのでしょう。 一つのインスタンスより他にはないと、どのように確認するでしょうか? たくさんのパッケージ変数を単に使うこともできますが、 クラスの名前のハッシュを使うクリーナーを使うことも非常にあります。 一価のクラスの完全な例です:
package Cosmos;
%Cosmos = ();
# "name" 属性のアクセサメソッド
sub name {
my $self = shift;
$self->{name} = shift if @_;
return $self->{name};
}
# "birthday" 属性の読み込みのみのアクセサメソッド
sub birthday {
my $self = shift;
die "can't reset birthday" if @_; # XXX: croak() is better
return $self->{birthday};
}
# "stars" 属性のアクセサメソッド
sub stars {
my $self = shift;
$self->{stars} = shift if @_;
return $self->{stars};
}
# おや、まあ! - 星が一つ、死んでしまった!
sub supernova {
my $self = shift;
my $count = $self->stars();
$self->stars($count - 1) if $count > 0;
}
# コンストラクタ/イニシャライザ - リブートで直される
sub bigbang {
my $self = shift;
%$self = (
name => "the world according to tchrist",
birthday => time(),
stars => 0,
);
return $self; # はい、おそらくクラスです。びっくり!
}
# クラスがコンパイルされた後、ですが、use も require も戻る前、
# バンとともに、宇宙が動き始めます。
__PACKAGE__ -> bigbang();
Hold on, that doesn't look like anything special. Those attribute accessors look no different than they would if this were a regular class instead of a monadic one. The crux of the matter is there's nothing that says that $self must hold a reference to a blessed object. It merely has to be something you can invoke methods on. Here the package name itself, Cosmos, works as an object. Look at the &supernova method. Is that a class method or an object method? The answer is that static analysis cannot reveal the answer. Perl doesn't care, and neither should you. In the three attribute methods, %$self
is really accessing the %Cosmos package variable.
そのままでいてください。何も特別なようにはみえません。 これらの属性アクセサは一価のクラスであるか、普通のクラスであるかで、 することは何も違いません。ここでの最重要ポイントは $selfがオブジェクトにblessされたリファレンスを持たなければならないとは言わないところです。 単に、メソッドを呼ぶことができれば、何でもいいのです。 パッケージの名前自身、Cosmosでも、オブジェクトのように働きます。 &supernova メソッドをみてください。これは、クラスメソッドか、オブジェクトメソッドでしょうか? その答えは、静的な解析ではその答えを知らせることはできないということです。 Perlは、気にしませんし、あなたも気にしないべきです。 3つの属性メソッドで、%$self
は、実際は、%Cosmosパッケージ変数にアクセスしています。
If like Stephen Hawking, you posit the existence of multiple, sequential, and unrelated universes, then you can invoke the &bigbang method yourself at any time to start everything all over again. You might think of &bigbang as more of an initializer than a constructor, since the function doesn't allocate new memory; it only initializes what's already there. But like any other constructor, it does return a scalar value to use for later method invocations.
ステファン・ホーキングのように、複数の、連続の、関係のない宇宙の存在を仮定するなら、 &bigbang メソッドを自分で呼び、いつでも、そっくり再び、全てを始めることができます。 &bigbang を、コンストラクタよりも、もっとイニシャライザとして、考えているかもしれません。 &bigbang は、新たのメモリを割り当てません、すなわち、&bingbang は、既にそこにあるものを、 初期化するだけというために。ですが、他のどんなコンストラクタと同じように、 後のメソッド呼び出しに使うために、スカラ値をを返します。
Imagine that some day in the future, you decide that one universe just isn't enough. You could write a new class from scratch, but you already have an existing class that does what you want--except that it's monadic, and you want more than just one cosmos.
将来のいつかに、一つの宇宙では十分ではないと、決心したとしましょう。 スクラッチから新しいクラスを書くこともできますが、 既存のクラスがあります -- 一価であることを除いて、望むクラスがあり、 しかも、ただ、2つ以上の宇宙が欲しいだけです。
That's what code reuse via subclassing is all about. Look how short the new code is:
サブクラスでコードの再利用することが全てです。 新しいコードがどのくらい短いか、見てみましょう。
package Multiverse;
use Cosmos;
@ISA = qw(Cosmos);
sub new {
my $protoverse = shift;
my $class = ref($protoverse) || $protoverse;
my $self = {};
return bless($self, $class)->bigbang();
}
1;
Because we were careful to be good little creators when we designed our Cosmos class, we can now reuse it without touching a single line of code when it comes time to write our Multiverse class. The same code that worked when invoked as a class method continues to work perfectly well when invoked against separate instances of a derived class.
Cosmosクラスを設計している時に、慎重であり、良い小さなクリエータであったので、 Multiverse クラスを書く際に、コードの一行もいじらないで、Cosmosクラスを再利用することができます。 クラスメソッドとして呼ばれたときに動く同じコードが 派生クラスの個々のインスタンスから呼ばれても、完全に、うまく、動き続けます。
The astonishing thing about the Cosmos class above is that the value returned by the &bigbang "constructor" is not a reference to a blessed object at all. It's just the class's own name. A class name is, for virtually all intents and purposes, a perfectly acceptable object. It has state, behavior, and identify, the three crucial components of an object system. It even manifests inheritance, polymorphism, and encapsulation. And what more can you ask of an object?
上にあげた、Cosmosクラスでびっくりするのは、&bigbang "コンストラクタ"が返す値が、 まったく、bless されたオブジェクトへのリファレンスではないことです。 &bigbangコンストラクタが返す値は、クラス自身の名前です。 クラス名は、全ての目的と手段にとって、実質的に、完全に条件を満たしているオブジェクトです。 状態、振舞、同一性、3つの、オブジェクトシステムの重大な構成要素が、クラス名にはあります。 クラス名は、継承、ポリモルフィズム、カプセル化さえも明らかにします。 オブジェクトにこれ以上何を要求できますか?
To understand object orientation in Perl, it's important to recognize the unification of what other programming languages might think of as class methods and object methods into just plain methods. "Class methods" and "object methods" are distinct only in the compartmentalizing mind of the Perl programmer, not in the Perl language itself.
Perlでオブジェクト指向を理解するために、他のプログラミング言語がクラスメソッドと オブジェクトメソッドについて考えていることを、ただの単純なメソッドに統一することを認識するのが重要です。 "クラスメソッド"と、"オブジェクトメソッド"は、Perlプログラマが心の中で区切っているだけで、 Perl言語それ自身の中では、区切られていません。
Along those same lines, a constructor is nothing special either, which is one reason why Perl has no pre-ordained name for them. "Constructor" is just an informal term loosely used to describe a method that returns a scalar value that you can make further method calls against. So long as it's either a class name or an object reference, that's good enough. It doesn't even have to be a reference to a brand new object.
これらの同じ行の間で、コンストラクタは、何も特別なものではありません。 Perlには、それらの行に、神によって定められている名前はないのがその理由です。 "コンストラクタ"は公式ではない述語であり、後でメソッドを呼ぶことのできるスカラー値を 返すメソッドを説明するのを使います。 クラス名もオブジェクトリファレンスでありさえすれば、十分、上等です。 おろしたてのオブジェクトがリファレンスである必要さえないのです。
You can have as many--or as few--constructors as you want, and you can name them whatever you care to. Blindly and obediently using new() for each and every constructor you ever write is to speak Perl with such a severe C++ accent that you do a disservice to both languages. There's no reason to insist that each class have but one constructor, or that a constructor be named new(), or that a constructor be used solely as a class method and not an object method.
自分が望むのと同じくらい、多くの -- または、ほとんどない -- 、コンストラクタを持つことができますし、 自分がしたいように何でも名前を付けることができます。 やみくもに、素直に、いままで書いた、ありとあらゆるコンストラクタに、 new() を使うことは、両方の言語にひどい仕打をする、厳格なC++のアクセントのようなもので、Perlを喋ることです。 それぞれのクラスに一つのコンストラクタがあるとか、または、コンストラクタはnew()と名付けられるとか、 コンストラクタはオブジェクトメソッドではなく、ただクラスメソッドとして使われるとか、 そういうことを強く主張する理由はまったくありません。
The next section shows how useful it can be to further distance ourselves from any formal distinction between class method calls and object method calls, both in constructors and in accessor methods.
次のセクションでは、コンストラクタとアクセサメソッドの両方で、 クラスメソッドとオブジェクトメソッドの呼び出しとの間で、 正式な区別から、我々自身がさらに遠くまでありうるということが、 どれくらい有益であるかを示します。
半透明の属性¶
A package's eponymous hash can be used for more than just containing per-class, global state data. It can also serve as a sort of template containing default settings for object attributes. These default settings can then be used in constructors for initialization of a particular object. The class's eponymous hash can also be used to implement translucent attributes. A translucent attribute is one that has a class-wide default. Each object can set its own value for the attribute, in which case $object->attribute()
returns that value. But if no value has been set, then $object->attribute()
returns the class-wide default.
パッケージの名前のハッシュはクラスごとに、 グローバルな状態のデータをただ持つ以上に、使うことができます。 オブジェクト属性のデフォルトの設定を持つ、一種のテンプレートとしての役目も出来ます。 これらのデフォルトの設定は、コンストラクタで、特定のオブジェクトの初期化のために使うことができます。 クラスの名前のハッシュは、半透明の属性を実装するのにも使うことも出来ます。 半透明の属性とは、クラスワイドのデフォルトを持つ属性です。 それぞれのオブジェクトは属性にそれ自身の値をセットできます、 そこでは、$object->attribute()
で、値を返します。 ですが、値がまだセットされていなければ、$object->attribute()
は、 クラスワイドのデフォルトを返します。
We'll apply something of a copy-on-write approach to these translucent attributes. If you're just fetching values from them, you get translucency. But if you store a new value to them, that new value is set on the current object. On the other hand, if you use the class as an object and store the attribute value directly on the class, then the meta-object's value changes, and later fetch operations on objects with uninitialized values for those attributes will retrieve the meta-object's new values. Objects with their own initialized values, however, won't see any change.
これらの半透明の属性に コピー・オン・ライトのアプローチの何かを適用するでしょう。 これらの属性からただ、値を取りだしているならば、半透明性を得ます。 ですが、これらの属性に新しい値を蓄えるなら、新しい値は現在のオブジェクトに セットされます。他方で、クラスをオブジェクトとして使い、 直接にクラスで属性値を蓄えるなら、メタオブジェクトの値が変更し、 その後で、それらの属性が初期化されていない値でオブジェクトで作業を取ると、 メタオブジェクトの新しい値を取り出すでしょう(訳註:訳怪しい)。
Let's look at some concrete examples of using these properties before we show how to implement them. Suppose that a class named Some_Class had a translucent data attribute called "color". First you set the color in the meta-object, then you create three objects using a constructor that happens to be named &spawn.
どのように実装するかを見せる前に、これらのプロパティを使う、具体的な例を見てみましょう。 Some_Classという名前のクラスに、"color"という半透明のクラス属性があるとします。 まず、meta-object のカラーをセットします。それから、コンストラクタを使って、 3つのオブジェクトをつくります。コンストラクタは、たまたま &spawn という名前です。
use Vermin;
Vermin->color("vermilion");
$ob1 = Vermin->spawn(); # so that's where Jedi come from
$ob2 = Vermin->spawn();
$ob3 = Vermin->spawn();
print $obj3->color(); # prints "vermilion"
Each of these objects' colors is now "vermilion", because that's the meta-object's value that attribute, and these objects do not have individual color values set.
それぞれのオブジェクトの色は、現在、"vermilion(朱色)"です。 メタオブジェクトの値が、その属性であり、 それぞれのオブジェクトに個別の色の値がセットされていないからです。
Changing the attribute on one object has no effect on other objects previously created.
あるオブジェクトの属性を変更しても、先に作られた他のオブジェクトの値には 影響しません。
$ob3->color("chartreuse");
print $ob3->color(); # prints "chartreuse"
print $ob1->color(); # prints "vermilion", translucently
If you now use $ob3 to spawn off another object, the new object will take the color its parent held, which now happens to be "chartreuse". That's because the constructor uses the invoking object as its template for initializing attributes. When that invoking object is the class name, the object used as a template is the eponymous meta-object. When the invoking object is a reference to an instantiated object, the &spawn constructor uses that existing object as a template.
$ob3を使い、他のオブジェクトを産んでみると、新しいオブジェクトは、 その親が持っている色を取るでしょう。今は、たまたま、"chartreuse(淡黄緑色)"になるでしょう。 コンストラクタが、初期化の属性のテンプレートとして、呼び出したオブジェクトを使うからです。 呼び出すオブジェクトがクラス名であれば、オブジェクトは、クラスのメタオブジェクトを参照します。 呼び出すオブジェクトが説明されたオブジェクトへのリファレンスであれば、 &spawn コンストラクタは、テンプレートとして、存在するオブジェクトを使います。
$ob4 = $ob3->spawn(); # $ob3 now template, not %Vermin
print $ob4->color(); # prints "chartreuse"
Any actual values set on the template object will be copied to the new object. But attributes undefined in the template object, being translucent, will remain undefined and consequently translucent in the new one as well.
テンプレートオブジェクトにセットされる実際の値は、どんなものも、 新しいオブジェクトにコピーされます。ですが、テンプレートオブジェクトに未定義の 属性は、半透明になり、未定義のままとなり、その結果、新しいものも同様に半透明です。
Now let's change the color attribute on the entire class:
今度は、クラス全体のcolor属性を変更しましょう:
Vermin->color("azure");
print $ob1->color(); # prints "azure"
print $ob2->color(); # prints "azure"
print $ob3->color(); # prints "chartreuse"
print $ob4->color(); # prints "chartreuse"
That color change took effect only in the first pair of objects, which were still translucently accessing the meta-object's values. The second pair had per-object initialized colors, and so didn't change.
この色の変更はオブジェクトの最初の2つにのみ、影響します。 この2つは、まだ、半透明で、メタオブジェクトの値にアクセスします。 後の2つは、オブジェクトごとに、初期化された色がありました。 そのため、変更されません。
One important question remains. Changes to the meta-object are reflected in translucent attributes in the entire class, but what about changes to discrete objects? If you change the color of $ob3, does the value of $ob4 see that change? Or vice-versa. If you change the color of $ob4, does then the value of $ob3 shift?
一つ、重要な疑問があります。メタオブジェクトの値を変えることは、 クラス全体の半透明の属性に反映されます。 ですが、オブジェクトを絶やす変更はどうでしょうか? $ob3の色を変更したら、$ob4にはその変更がわかるでしょうか?また、逆の場合も同じく。 もし、$ob4の色を変更したら、$ob4の値は変わるでしょうか?
$ob3->color("amethyst");
print $ob3->color(); # prints "amethyst"
print $ob4->color(); # hmm: "chartreuse" or "amethyst"?
While one could argue that in certain rare cases it should, let's not do that. Good taste aside, we want the answer to the question posed in the comment above to be "chartreuse", not "amethyst". So we'll treat these attributes similar to the way process attributes like environment variables, user and group IDs, or the current working directory are treated across a fork(). You can change only yourself, but you will see those changes reflected in your unspawned children. Changes to one object will propagate neither up to the parent nor down to any existing child objects. Those objects made later, however, will see the changes.
While one could argue that .. 確かにまれなケースでは、そうすべきですが、そうしないでおきましょう。 Good taste aside, "chartreuse" "amethyst" 環境変数のように属性を処理するのと似たような方法で、それらの属性を扱いましょう。 ユーザやグループや現在のワーキングディレクトリは、fork()を超えて、扱われます。 自分自身のみ変更できても、それらの変更が、産まれない子に反映するのがわかるでしょう。 一つのオブジェクトの変更は、親にまで、さかのぼり、存在する子にまで下って、 オブジェクトに伝播します。 これらのオブジェクトは後で作られても、変更がわかるでしょう。
If you have an object with an actual attribute value, and you want to make that object's attribute value translucent again, what do you do? Let's design the class so that when you invoke an accessor method with undef
as its argument, that attribute returns to translucency.
実際の属性値でオブジェクトを持ち、オブジェクトの属性値を再び半透明にしたければ、 どうしましょうか? クラスを、その引数に、undef
でアクセサメソッドを呼ぶとき、 その属性は半透明性に
$ob4->color(undef); # back to "azure"
Here's a complete implementation of Vermin as described above.
上で説明した Vermin の完全な実装です。
package Vermin;
# here's the class meta-object, eponymously named.
# it holds all class attributes, and also all instance attributes
# so the latter can be used for both initialization
# and translucency.
our %Vermin = ( # our() is new to perl5.6
PopCount => 0, # capital for class attributes
color => "beige", # small for instance attributes
);
# constructor method
# invoked as class method or object method
sub spawn {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $self = {};
bless($self, $class);
$class->{PopCount}++;
# init fields from invoking object, or omit if
# invoking object is the class to provide translucency
%$self = %$obclass if ref $obclass;
return $self;
}
# translucent accessor for "color" attribute
# invoked as class method or object method
sub color {
my $self = shift;
my $class = ref($self) || $self;
# handle class invocation
unless (ref $self) {
$class->{color} = shift if @_;
return $class->{color}
}
# handle object invocation
$self->{color} = shift if @_;
if (defined $self->{color}) { # not exists!
return $self->{color};
} else {
return $class->{color};
}
}
# accessor for "PopCount" class attribute
# invoked as class method or object method
# but uses object solely to locate meta-object
sub population {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
return $class->{PopCount};
}
# instance destructor
# invoked only as object method
sub DESTROY {
my $self = shift;
my $class = ref $self;
$class->{PopCount}--;
}
Here are a couple of helper methods that might be convenient. They aren't accessor methods at all. They're used to detect accessibility of data attributes. The &is_translucent method determines whether a particular object attribute is coming from the meta-object. The &has_attribute method detects whether a class implements a particular property at all. It could also be used to distinguish undefined properties from non-existent ones.
2、3の便利になるヘルパーメソッドがあります。 これらは、まったく、アクセサメソッドではありません。 データ属性のアクセシビリティを見つけるのに使われています。 この &is_translucent メソッドは特別なオブジェクト属性が、 メタオブジェクトからくるのかどうかを決めています。 &has_attribute メソッドは、クラスが特別なプロパティを 少しでも実装しているかどうかを見つけます。 存在しないプロパティから、未定義のプロパティを区別するのにも、 使われ得ます。
# detect whether an object attribute is translucent
# (typically?) invoked only as object method
sub is_translucent {
my($self, $attr) = @_;
return !defined $self->{$attr};
}
# test for presence of attribute in class
# invoked as class method or object method
sub has_attribute {
my($self, $attr) = @_;
my $class = ref $self if $self;
return exists $class->{$attr};
}
If you prefer to install your accessors more generically, you can make use of the upper-case versus lower-case convention to register into the package appropriate methods cloned from generic closures.
より一般的に、アクセサをインストールしたいなら、 パッケージに、一般のクロージャからクローニングされた適切なメソッドを 登録するのに、大文字対小文字の慣習を利用することができます。
for my $datum (keys %{ +__PACKAGE__ }) {
*$datum = ($datum =~ /^[A-Z]/)
? sub { # install class accessor
my $obclass = shift;
my $class = ref($obclass) || $obclass;
return $class->{$datum};
}
: sub { # install translucent accessor
my $self = shift;
my $class = ref($self) || $self;
unless (ref $self) {
$class->{$datum} = shift if @_;
return $class->{$datum}
}
$self->{$datum} = shift if @_;
return defined $self->{$datum}
? $self -> {$datum}
: $class -> {$datum}
}
}
Translations of this closure-based approach into C++, Java, and Python have been left as exercises for the reader. Be sure to send us mail as soon as you're done.
このクロージャベースのアプローチを、C++、Java、Pythonへ、 転換するのは、読者の課題として残しておきます。 できあがったらすぐに、きっと送ってくださいね。
レキシカル変数のクラスデータ¶
プライバシーと責任¶
Unlike conventions used by some Perl programmers, in the previous examples, we didn't prefix the package variables used for class attributes with an underscore, nor did we do so for the names of the hash keys used for instance attributes. You don't need little markers on data names to suggest nominal privacy on attribute variables or hash keys, because these are already notionally private! Outsiders have no business whatsoever playing with anything within a class save through the mediated access of its documented interface; in other words, through method invocations. And not even through just any method, either. Methods that begin with an underscore are traditionally considered off-limits outside the class. If outsiders skip the documented method interface to poke around the internals of your class and end up breaking something, that's not your fault--it's theirs.
Perlプログラマの幾人かに使われている慣習とは違い、先の例では、 クラス属性に使われるパッケージ変数の前に、アンダースコアを付けていませんし、 インスタンス属性に使われるハッシュキーの名前にも、そのようにしていません。 データの名前に小さなマークをつけて、属性変数や、ハッシュのキーに、名目上のプライバシーを暗示する 必要はありません。というのは、いつも概念的にプライベートだからです! 部外者が、クラス内で何かをいじるかは、少しも気にしなく良いからです。 そのドキュメントのインターフェースを通すのを除いて;言い換えると、メソッドの実行を通すのを除いて。 And not even through just any method, either. メソッドアンダースコアで始まるメソッドは伝統的に、クラスの外側から立入禁止と 考えられています。もし、部外者が、ドキュメントに書かれたメソッドインターフェースを読み飛ばして、 クラスの内部をつつきまわして、何かを壊すことになっても、クラスの作者の落度ではありません。 --それは、そういうことをした部外者の落度です。
Perl believes in individual responsibility rather than mandated control. Perl respects you enough to let you choose your own preferred level of pain, or of pleasure. Perl believes that you are creative, intelligent, and capable of making your own decisions--and fully expects you to take complete responsibility for your own actions. In a perfect world, these admonitions alone would suffice, and everyone would be intelligent, responsible, happy, and creative. And careful. One probably shouldn't forget careful, and that's a good bit harder to expect. Even Einstein would take wrong turns by accident and end up lost in the wrong part of town.
Perl は、委任統治に指定されたコントロールよりもむしろ、個人の責務を信じています。 Perlは十分にあなたを尊敬しているので、あなたに、自身にあったレベルの痛みや喜びを選択させます。 Perlは、あなたが、創造的で、知性的で、自分自身で決定できると信じ、自身の行動に完全に責任を取れると期待しています。 完全な世界では、このような勧告だけで十分でしょう。そして、全ての人が、知性的で、 信頼でき、幸福で、創造的であるでしょう。そして、慎重であるでしょう。 たぶんある人が慎重であることを忘れても、それは、かなり予測しがたいことでしょう。 アインシュタインでさけ、うっかり曲がり間違えて、町の間違った地域で迷うことになった、
Some folks get the heebie-jeebies when they see package variables hanging out there for anyone to reach over and alter them. Some folks live in constant fear that someone somewhere might do something wicked. The solution to that problem is simply to fire the wicked, of course. But unfortunately, it's not as simple as all that. These cautious types are also afraid that they or others will do something not so much wicked as careless, whether by accident or out of desperation. If we fire everyone who ever gets careless, pretty soon there won't be anybody left to get any work done.
誰でも、パッケージ変数に、手が届き、変更できる、出入りできる、 パッケージ変数に、heebie-jeebiesになる人もいます。 誰かが、どこかで、 悪戯をすることに、不断に帯得ている人達です。 この問題の解決は単純に、悪戯を押え込むことです、もちろん。 ですが、不幸なことに、そなんに簡単ではありません。 これらの警告タイプは、彼らや他の人々が、たまたまか、絶望の中からかどうか、 注意なしにそんなにひどくない悪戯をするのにも恐れさせます。 注意不足な人々みんなを解雇するするなら、 そこには、すぐに、何かの仕事をする人は誰も居なくなるでしょう。
Whether it's needless paranoia or sensible caution, this uneasiness can be a problem for some people. We can take the edge off their discomfort by providing the option of storing class attributes as lexical variables instead of as package variables. The my() operator is the source of all privacy in Perl, and it is a powerful form of privacy indeed.
それが、必要のないパラノイアか、賢明な警告かどうか、 この不安は人によっては、問題になりえます。 パッケージ変数の代わりにレキシカル変数に、クラス属性を蓄えるオプションを 与えることで、そういう人たちの不安を和らげることができます。 my() 演算子はPerlのすべてのプライバシーの源です。そして、my()は、 プライバシーの強力な形です。実際に。
It is widely perceived, and indeed has often been written, that Perl provides no data hiding, that it affords the class designer no privacy nor isolation, merely a rag-tag assortment of weak and unenforcible social conventions instead. This perception is demonstrably false and easily disproven. In the next section, we show how to implement forms of privacy that are far stronger than those provided in nearly any other object-oriented language.
次のことは、広く認められていますし、実際、よく書かれています。 Perlは、データの隠蔽を提供しないし、 Perlは、クラスの設計者にプライバシーも孤立も提供しません。 単に、ぼろぎれの、弱点の詰め合わせであり、 押しつけることの出来ない社会的慣習が代わりにあるだけです。 この認識は、確実に間違いであり、ちっとも精査されていません。 次のセクションでは、ほとんどのどんな他のオブジェクト指向言語で提供されているよりも、 はるかに強力なプライバシーの形を実装するのを見せます。
ファイルスコープのレキシカル変数¶
A lexical variable is visible only through the end of its static scope. That means that the only code able to access that variable is code residing textually below the my() operator through the end of its block if it has one, or through the end of the current file if it doesn't.
レキシカル変数はその静的なスコープの終りまででしか、見えません。 このことは、ブロックがあればそのブロックの終りまでの、ブロックがなければ現在のファイルの終りまでの、 my()演算子の下にあるテキストに存在するコードだけが、この変数にアクセス出来る唯一のコードです。
Starting again with our simplest example given at the start of this document, we replace our() variables with my() versions.
このドキュメントの最初にあった、最も単純な例で始めましょう。 our() 変数を my() バージョンで置き換えます。
package Some_Class;
my($CData1, $CData2); # ファイルスコープ。何のパッケージでもありません。
sub CData1 {
shift; # XXX: クラス/オブジェクトの呼び出しを無視します。
$CData1 = shift if @_;
return $CData1;
}
sub CData2 {
shift; # XXX: クラス/オブジェクトの呼び出しを無視します。
$CData2 = shift if @_;
return $CData2;
}
So much for that old $Some_Class::CData1 package variable and its brethren! Those are gone now, replaced with lexicals. No one outside the scope can reach in and alter the class state without resorting to the documented interface. Not even subclasses or superclasses of this one have unmediated access to $CData1. They have to invoke the &CData1 method against Some_Class or an instance thereof, just like anybody else.
あの古い、$Some_Class::CData1 パッケージ変数とその仲間については、これだけにしておきます! それらは、今や過ぎさって、レキシカル変数に置き換えられました。 スコープの外側にいる誰も、ドキュメントに書かれたインターフェースの手段以外に、 クラスの状態に影響を及ぼせませんし、変更も出来ません。 このサブクラスやスーパークラスでさえ、$CData1へアクセスを仲介しません。 Some_Classか、そのインスタンスか、他の似たようなもので、 &CData1メソッドを呼ぶ必要があります。
To be scrupulously honest, that last statement assumes you haven't packed several classes together into the same file scope, nor strewn your class implementation across several different files. Accessibility of those variables is based uniquely on the static file scope. It has nothing to do with the package. That means that code in a different file but the same package (class) could not access those variables, yet code in the same file but a different package (class) could. There are sound reasons why we usually suggest a one-to-one mapping between files and packages and modules and classes. You don't have to stick to this suggestion if you really know what you're doing, but you're apt to confuse yourself otherwise, especially at first.
ものすごく正直になれば、最後のステートメントは、 同じファイルスコープに、複数のクラスを一緒にパックしないし、 複数の違うファイルにまたっがて、クラスの実装ばらまきもしないと想定するでしょう。 これらの変数のアクセシビリティは、静的なファイルスコープでユニークにベースにされています。
If you'd like to aggregate your class attributes into one lexically scoped, composite structure, you're perfectly free to do so.
クラス属性を1つのレキシカルスコープの変数に集約し、構造体を作りたければ、 そうするのは、完全に自由です。
package Some_Class;
my %ClassData = (
CData1 => "",
CData2 => "",
);
sub CData1 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$ClassData{CData1} = shift if @_;
return $ClassData{CData1};
}
sub CData2 {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$ClassData{CData2} = shift if @_;
return $ClassData{CData2};
}
To make this more scalable as other class attributes are added, we can again register closures into the package symbol table to create accessor methods for them.
他のクラス属性を加えるように、より拡張性をもたせたければ、 再び、パッケージのシンボルテーブルにクロージャを登録し、 それらのためのアクセスメソッドを作れます。
package Some_Class;
my %ClassData = (
CData1 => "",
CData2 => "",
);
for my $datum (keys %ClassData) {
no strict "refs";
*$datum = sub {
shift; # XXX: クラス/オブジェクト呼び出しを無視します
$ClassData{$datum} = shift if @_;
return $ClassData{$datum};
};
}
Requiring even your own class to use accessor methods like anybody else is probably a good thing. But demanding and expecting that everyone else, be they subclass or superclass, friend or foe, will all come to your object through mediation is more than just a good idea. It's absolutely critical to the model. Let there be in your mind no such thing as "public" data, nor even "protected" data, which is a seductive but ultimately destructive notion. Both will come back to bite at you. That's because as soon as you take that first step out of the solid position in which all state is considered completely private, save from the perspective of its own accessor methods, you have violated the envelope. And, having pierced that encapsulating envelope, you shall doubtless someday pay the price when future changes in the implementation break unrelated code. Considering that avoiding this infelicitous outcome was precisely why you consented to suffer the slings and arrows of obsequious abstraction by turning to object orientation in the first place, such breakage seems unfortunate in the extreme.
何か他のようなアクセサメソッドを使う自分自身のクラスを必要とすることは、 おそらく良い事です。ですが、他のもの全て、サブクラス、スーパークラス、 敵味方、全てが、仲介を通してあなたのオブジェクトにくるのを、 要求したり、予期したりすることは、良い考えを超えるものです。 これは、そのモデルにとって全く重要です。 "public"データも、"protected"データも、どちらも、 そのようなものは心の中からなくしましょう。 それらは、魅惑的ですが、詰まるところ、有害な考えです。 両方とも、戻って来て、あなたに噛みつくでしょう。 全ての状態が完全にプライベートだと考えられ、それ自身のアクセサメソッドの観点から保存される、 安定した位置から外れるとすぐに、そのエンベロープを破ることになるからです。 カプセル化されているエンベロープに穴を開けていると、 未来の実装の変更が関係ないコードを壊す時、誰かが代価を払うことは、間違いない。 この不完全な結果を避ける考えは、まさに、 最初の場所でオブジェクトのオリエンテーションを始めることで、 卑屈な抽象化のスリングと弓の被害を被るのに同意する理由です。 そのような破損は極端に不運のようです(訳註:よくわからない)。
より多くの継承の懸念(More Inheritance Concerns)¶
Suppose that Some_Class were used as a base class from which to derive Another_Class. If you invoke a &CData method on the derived class or on an object of that class, what do you get? Would the derived class have its own state, or would it piggyback on its base class's versions of the class attributes?
Some_Classが、派生の Another_Class から基底クラスとして使われているとしましょう。 &CDataメソッドを、派生クラスか、派生クラスのオブジェクトで呼んだら、 何を得るでしょうか? 派生クラスは、その自身の状態があるのか、それとも、 基底クラスのクラス属性の変数におんぶするのでしょうか?
The answer is that under the scheme outlined above, the derived class would not have its own state data. As before, whether you consider this a good thing or a bad one depends on the semantics of the classes involved.
その答えは上で要点をまとめた枠組のもとにあります。 派生クラスはそれ自身の状態データがありません。 前のように、これを良いことと思うか、悪いことと思うか、 関係するクラスの意味によります。
The cleanest, sanest, simplest way to address per-class state in a lexical is for the derived class to override its base class's version of the method that accesses the class attributes. Since the actual method called is the one in the object's derived class if this exists, you automatically get per-class state this way. Any urge to provide an unadvertised method to sneak out a reference to the %ClassData hash should be strenuously resisted.
レキシカルで、クラスごとの状態を扱う、 最も綺麗な、最も健全で、最も単純な方法は、 クラス属性にアクセスするメソッドの基底クラスのバージョンを 派生クラス用に、オーバーライドすることです。 呼び出される実際のメソッドが、オブジェクトの派生クラスのものなので、 もしあれば、この方法で、クラス後との状態を時動的に得ます。 %ClassData ハッシュへのリファレンスをこっそり外に持ち出すめの、 非公式のメソッドを提供する衝動に、はげしく耐えるべきです。
As with any other overridden method, the implementation in the derived class always has the option of invoking its base class's version of the method in addition to its own. Here's an example:
他のオーバーライドされたメソッドと同様に、 派生クラスの実装にはいつも、自分自身のバージョンとは別に、 その基底クラスバージョンのメソッドを呼び出すオプションがあります。 その例です:
package Another_Class;
@ISA = qw(Some_Class);
my %ClassData = (
CData1 => "",
);
sub CData1 {
my($self, $newvalue) = @_;
if (@_ > 1) {
# set locally first
$ClassData{CData1} = $newvalue;
# then pass the buck up to the first
# overridden version, if there is one
if ($self->can("SUPER::CData1")) {
$self->SUPER::CData1($newvalue);
}
}
return $ClassData{CData1};
}
Those dabbling in multiple inheritance might be concerned about there being more than one override.
オーバーライドよりも、多重継承に首をつっこむとが、考えられます。
for my $parent (@ISA) {
my $methname = $parent . "::CData1";
if ($self->can($methname)) {
$self->$methname($newvalue);
}
}
Because the &UNIVERSAL::can method returns a reference to the function directly, you can use this directly for a significant performance improvement:
&UNIVERSAL::can メソッドは、関数へのリファレンスを、直接に返します。 ですので、重要なパフォーマンスの改良のために、これを直接に使うことができます。
for my $parent (@ISA) {
if (my $coderef = $self->can($parent . "::CData1")) {
$self->$coderef($newvalue);
}
}
ドアに施錠して、鍵を放り投げる¶
As currently implemented, any code within the same scope as the file-scoped lexical %ClassData can alter that hash directly. Is that ok? Is it acceptable or even desirable to allow other parts of the implementation of this class to access class attributes directly?
現在インプリメントされるように、ファイルスコープのレキシカル変数の %ClassData と 同じ範囲内のどのコードでも、そのハッシュを直接に、変更することができます。 これは良いでしょうか? このクラスの実装の他の部分が、直接にクラス属性にアクセスすることを許すことは、 受け入れられか、望ましいものでしょうか?
That depends on how careful you want to be. Think back to the Cosmos class. If the &supernova method had directly altered $Cosmos::Stars or $Cosmos::Cosmos{stars}
, then we wouldn't have been able to reuse the class when it came to inventing a Multiverse. So letting even the class itself access its own class attributes without the mediating intervention of properly designed accessor methods is probably not a good idea after all.
このことは、あなたがどれくらい注意深くありたいかに依存します。 Cosmoクラスに戻って、考えてください。&supernova メソッドが直接に、$Cosmos::Startsか、 $colsmos::Cosmos{stars}
を変更していたら、Multiverseを考案するときに、 クラスを再利用することができなかったでしょう。クラスそれ自身でさえも、 そのクラス属性に、適切に設計されたアクセサメソッドが仲介している介入なしに、 アクセスするのは、結局、良い考えではないでしょう。
Restricting access to class attributes from the class itself is usually not enforcible even in strongly object-oriented languages. But in Perl, you can.
クラス自身からクラス属性へのアクセスを制限するのは、ふつう、 強力なオブジェクト指向言語でさえ、強制されていません。ですが、Perlでは、できます。
Here's one way:
一つの方法です:
package Some_Class;
{ # scope for hiding $CData1
my $CData1;
sub CData1 {
shift; # XXX: unused
$CData1 = shift if @_;
return $CData1;
}
}
{ # scope for hiding $CData2
my $CData2;
sub CData2 {
shift; # XXX: unused
$CData2 = shift if @_;
return $CData2;
}
}
No one--absolutely no one--is allowed to read or write the class attributes without the mediation of the managing accessor method, since only that method has access to the lexical variable it's managing. This use of mediated access to class attributes is a form of privacy far stronger than most OO languages provide.
誰も-- 絶対に誰も -- クラス属性に、支配しているアクセサメソッドの仲介なしには、 クラス属性を読み書きすることを許されていません。 メソッドだけが、支配ているレキシカル変数にアクセスするからです。 この、クラス属性への仲介するアクセスの使用は、ほとんどの、 オブジェクト指向言語が提供するものよりも、はるかに強力な、プライバシーの形です。
The repetition of code used to create per-datum accessor methods chafes at our Laziness, so we'll again use closures to create similar methods.
データごとの アクセサメソッドを作成するために使用される、 コードを反復するのは、不精を苛立だせます。 ですので、似たようなメソッドを作成するのに、再びクロージャを使用しましょう。
package Some_Class;
{ # scope for ultra-private meta-object for class attributes
my %ClassData = (
CData1 => "",
CData2 => "",
);
for my $datum (keys %ClassData ) {
no strict "refs";
*$datum = sub {
use strict "refs";
my ($self, $newvalue) = @_;
$ClassData{$datum} = $newvalue if @_ > 1;
return $ClassData{$datum};
}
}
}
The closure above can be modified to take inheritance into account using the &UNIVERSAL::can method and SUPER as shown previously.
上のクロージャは、以前に示した、&UNIVERSAL::can メソッドとSUPERを使用して、 継承を考慮に入れるために修正できます。
半透明、再び¶
The Vermin class demonstrates translucency using a package variable, eponymously named %Vermin, as its meta-object. If you prefer to use absolutely no package variables beyond those necessary to appease inheritance or possibly the Exporter, this strategy is closed to you. That's too bad, because translucent attributes are an appealing technique, so it would be valuable to devise an implementation using only lexicals.
Veriman クラスは、そのメタオブジェクトとして、パッケージ変数、 クラスの名前の %Vermin を使って、半透明性をデモンストレーションしました。 継承か、もしかすると、Exporter を和らげるそれらの必需品以上に、 絶対に、パッケージでない変数を使いたいなら、この作戦は、身近なものです。 残念なことに、半透明の属性はアピールするテクニックですので、 レキシカル変数のみを使う実装を考え出す価値のあるものです。
There's a second reason why you might wish to avoid the eponymous package hash. If you use class names with double-colons in them, you would end up poking around somewhere you might not have meant to poke.
2つ目のクラスの名前のハッシュを避けたいと思う理由があります。 それらに、ダブルコロンのクラス名を使うと、 つつくつもりのないどこかを、結局、つつくことになるでしょう。
package Vermin;
$class = "Vermin";
$class->{PopCount}++;
# accesses $Vermin::Vermin{PopCount}
package Vermin::Noxious;
$class = "Vermin::Noxious";
$class->{PopCount}++;
# accesses $Vermin::Noxious{PopCount}
In the first case, because the class name had no double-colons, we got the hash in the current package. But in the second case, instead of getting some hash in the current package, we got the hash %Noxious in the Vermin package. (The noxious vermin just invaded another package and sprayed their data around it. :-) Perl doesn't support relative packages in its naming conventions, so any double-colons trigger a fully-qualified lookup instead of just looking in the current package.
最初のケースでは、クラスの名前にはダブルコロンがなかったので、 現在のパッケージのハッシュを得ました。 ですが、次のケースでは、現在のパッケージのハッシュを得る代わりに、 Vermin パッケージの、ハッシュ %Noxious を得ました。 (有害な社会の害虫は、他のパッケージに侵入し、そのまわりに、 自分のデータを広げます :-) Perlは、その名前の慣例に、関連のあるパッケージを サポートしません。ですので、ダブルコロンは、現在のパッケージの中を見るだけではなく、 完全修飾されたルックアップを引き起こします。
In practice, it is unlikely that the Vermin class had an existing package variable named %Noxious that you just blew away. If you're still mistrustful, you could always stake out your own territory where you know the rules, such as using Eponymous::Vermin::Noxious or Hieronymus::Vermin::Boschious or Leave_Me_Alone::Vermin::Noxious as class names instead. Sure, it's in theory possible that someone else has a class named Eponymous::Vermin with its own %Noxious hash, but this kind of thing is always true. There's no arbiter of package names. It's always the case that globals like @Cwd::ISA would collide if more than one class uses the same Cwd package.
実際問題、Vermin クラスには、吹き飛ばした、%Noxiousという名前の、 存在するパッケージ変数は、ありそうもないです。まだ、信用できないなら、 ルールを知っている自分の領域を、つねに見張ることができます。 Eponymous::Vermin::Noxious か、Hieronymus::Vermin::Boschious か、 Leave_Me_Alone::Vermin::Noxious を、クラス名として、代わりに、使うように。 確かに、 他の誰かが、Eponymous::Vermin という名のクラスを、 それ自身の %Noxious ハッシュと一緒に持つことは、理論上は可能ですが、 この種類のことは、つねに真実です。パッケージの名前の得決定者はいません。 2つ以上のクラスが同じCwdパッケージを使うなら、 @Cwd::ISAのようなグローバル変数が衝突するするのは、ありがちなケースです。
If this still leaves you with an uncomfortable twinge of paranoia, we have another solution for you. There's nothing that says that you have to have a package variable to hold a class meta-object, either for monadic classes or for translucent attributes. Just code up the methods so that they access a lexical instead.
パラノイアの不快な苦痛がまだ残っているなら、別の解決法があります。 一価のクラスのために、あるいは半透明の属性のために、 クラスのメタオブジェクトにを持つのに、 パッケージ変数を持たなくてはいけないとするものは何もありません。 代わりに、レキシカルにアクセスするようなメソッドをコードにしてください。
Here's another implementation of the Vermin class with semantics identical to those given previously, but this time using no package variables.
Verminクラスの別の実装です。以前に与えられたものと同じ意味ですが、 今回は、パッケージ変数を使っていません。
package Vermin;
# Here's the class meta-object, eponymously named.
# It holds all class data, and also all instance data
# so the latter can be used for both initialization
# and translucency. it's a template.
my %ClassData = (
PopCount => 0, # capital for class attributes
color => "beige", # small for instance attributes
);
# constructor method
# invoked as class method or object method
sub spawn {
my $obclass = shift;
my $class = ref($obclass) || $obclass;
my $self = {};
bless($self, $class);
$ClassData{PopCount}++;
# init fields from invoking object, or omit if
# invoking object is the class to provide translucency
%$self = %$obclass if ref $obclass;
return $self;
}
# translucent accessor for "color" attribute
# invoked as class method or object method
sub color {
my $self = shift;
# handle class invocation
unless (ref $self) {
$ClassData{color} = shift if @_;
return $ClassData{color}
}
# handle object invocation
$self->{color} = shift if @_;
if (defined $self->{color}) { # not exists!
return $self->{color};
} else {
return $ClassData{color};
}
}
# class attribute accessor for "PopCount" attribute
# invoked as class method or object method
sub population {
return $ClassData{PopCount};
}
# instance destructor; invoked only as object method
sub DESTROY {
$ClassData{PopCount}--;
}
# detect whether an object attribute is translucent
# (typically?) invoked only as object method
sub is_translucent {
my($self, $attr) = @_;
$self = \%ClassData if !ref $self;
return !defined $self->{$attr};
}
# test for presence of attribute in class
# invoked as class method or object method
sub has_attribute {
my($self, $attr) = @_;
return exists $ClassData{$attr};
}
注意¶
Inheritance is a powerful but subtle device, best used only after careful forethought and design. Aggregation instead of inheritance is often a better approach.
継承は強力ですが、繊細な仕組みです。注意深い見通しと設計の後で使うのが一番です。 継承の代わりに、集約が、よりよいアプローチであることがよくあります。
You can't use file-scoped lexicals in conjunction with the SelfLoader or the AutoLoader, because they alter the lexical scope in which the module's methods wind up getting compiled.
SelfLoader か、AutoLoader との接続では、ファイルスコープのレキシカル変数を使えません。 モジュールのメソッドが、結局コンパイルされるレキシカルスコープを変更するからです。
The usual mealy-mouthed package-mungeing doubtless applies to setting up names of object attributes. For example, $self->{ObData1}
should probably be $self->{ __PACKAGE__ . "_ObData1" }
, but that would just confuse the examples.
ふつう当り障りのない、パッケージのマンジングは、確かに、オブジェクト属性の名前を せっとするのに、適合します。たとえば、$self->{ObData1}
は、おそらく、 $self->{ __PACKAGE__ . "_ObData1" }
であるべきです。 ですが、それは、例を混乱させるだけだったでしょう、
SEE ALSO¶
perltoot, perlobj, perlmod, and perlbot.
The Tie::SecureHash and Class::Data::Inheritable modules from CPAN are worth checking out.
Tie::SecureHash と、Class::Data::Inheritable モジュールは、CPANから 良く調べる価値があります。
原文まま¶
Copyright (c) 1999 Tom Christiansen. All rights reserved.
This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.
Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit would be courteous but is not required.
謝辞¶
Russ Allbery, Jon Orwant, Randy Ray, Larry Rosler, Nat Torkington, and Stephen Warren all contributed suggestions and corrections to this piece. Thanks especially to Damian Conway for his ideas and feedback, and without whose indirect prodding I might never have taken the time to show others how much Perl has to offer in the way of objects once you start thinking outside the tiny little box that today's "popular" object-oriented languages enforce.
履歴¶
Last edit: Sun Feb 4 20:50:28 EST 2001