Perl Language
オブジェクト指向のPerl
サーチ…
オブジェクトの作成
他の多くの言語とは異なり、Perlにはオブジェクト用のメモリを割り当てるコンストラクタがありません。代わりに、データ構造を作成し、それにデータを取り込むクラスメソッドを書く必要があります(ファクトリメソッドの設計パターンとして理解できるかもしれません)。
package Point;
use strict;
sub new {
my ($class, $x, $y) = @_;
my $self = { x => $x, y => $y }; # store object data in a hash
bless $self, $class; # bind the hash to the class
return $self;
}
この方法は次のように使用できます。
my $point = Point->new(1, 2.5);
メソッドに矢印演算子->
が使用されると、その左のオペランドが与えられた引数リストの前に追加されます。だから、 new
@_
は値('Point', 1, 2.5)
、 @_
)を含んでいます。
new
名前に特別なものは何もありません。必要に応じてファクトリメソッドを呼び出すことができます。
ハッシュには何も特別なものはありません。あなたは次のように同じことをすることができます:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
一般に、参照はオブジェクトでもスカラー参照であってもかまいません。しかし、多くの場合、ハッシュはオブジェクトデータを表現する最も便利な方法です。
クラスの定義
一般的に、Perlのクラスは単なるパッケージです。通常のパッケージと同様に、データとメソッドを含めることができます。
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
パッケージで宣言された変数は、オブジェクト変数ではなくクラス変数であることに注意することが重要です。パッケージレベルの変数を変更すると、クラスのすべてのオブジェクトに影響します。オブジェクト固有のデータの格納方法は、「オブジェクトの作成」を参照してください。
クラスパッケージを具体的にするのは、矢印演算子->
です。それは裸の言葉の後に使用されるかもしれません:
Point->new(...);
またはスカラ変数の後(通常は参照を保持):
my @polar = $point->polar_coordinates;
矢印の左にあるものが、メソッドの指定された引数リストの前に追加されます。たとえば、コール後
Point->new(1, 2);
new
配列@_
には、 ('Point', 1, 2)
3つの引数が含まれます。
クラスを表すパッケージは、この規約を考慮に入れ、すべてのメソッドに1つの余分な引数があることを期待してください。
継承とメソッド解決
クラスを別のクラスのサブクラスにするには、 parent
pragmaを使用します。
package Point;
use strict;
...
1;
package Point2D;
use strict;
use parent qw(Point);
...
1;
package Point3D;
use strict;
use parent qw(Point);
...
1;
Perlでは複数の継承が可能です:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
継承はすべて、特定の状況で呼び出されるメソッドの解決に関するものです。純粋なPerlはオブジェクトデータの格納に使用されるデータ構造に関する規則を規定していないので、継承はそれとは関係ありません。
次のクラス階層を考えてみましょう。
package GeometryObject;
use strict;
sub transpose { ...}
1;
package Point;
use strict;
use parent qw(GeometryObject);
sub new { ... };
1;
package PlanarObject;
use strict;
use parent qw(GeometryObject);
sub transpose { ... }
1;
package Point2D;
use strict;
use parent qw(Point PlanarObject);
sub new { ... }
sub polar_coordinates { ... }
1;
メソッドの解決は次のように機能します。
開始点は、矢印演算子の左オペランドによって定義されます。
裸の単語の場合:
Point2D->new(...);
...または文字列を保持するスカラー変数:
my $class = 'Point2D'; $class->new(...);
...出発点は対応する名前のパッケージ(両方の例では
Point2D
)です。左のオペランドが、 祝福された参照を保持するスカラ変数である場合:
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
開始点は参照のクラス(ここでも
Point2D
)です。矢印演算子はblessされていない参照のためのメソッドを呼び出すために使用することはできません。
開始点に必要なメソッドが含まれている場合は、単に呼び出されます。
したがって、
Point2D::new
が存在するため、Point2D->new(...);
単にそれを呼び出します。
開始点に必要なメソッドが含まれていない場合は、
parent
クラスの深さ優先探索が実行されます。上記の例では、検索順序は次のようになります。-
Point2D
-
Point
(Point2D
最初の親) -
GeometryObject
(Point
親) -
PlanarObject
(Point2D
2番目の親)
たとえば、次のコードでは:
my $point = Point2D->new(...); $point->transpose(...);
PlanarObject::transpose
オーバーライドされていても、呼び出されるメソッドはGeometryObject::transpose
PlanarObject::transpose
です。-
開始点を明示的に設定することができます。
前の例では、次のように、
PlanarObject::transpose
明示的に呼び出すことができます:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
同様に、
SUPER::
は現在のクラスの親クラスでメソッド検索を実行します。例えば、
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
Point2D::new
実行中にPoint::new
を呼び出します。
クラスメソッドとオブジェクトメソッド
Perlでは、クラス(静的)メソッドとオブジェクト(インスタンス)メソッドの違いは他の言語ほど強くありませんが、依然として存在します。
矢印演算子の左のオペランド->
は、呼び出されるメソッドの最初の引数になります。それは文字列でもよいし、
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
オブジェクト参照:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
クラスメソッドは、最初の引数が文字列であることを期待するものであり、オブジェクトメソッドは、最初の引数がオブジェクト参照であると期待するメソッドです。
クラスメソッドは通常、クラスの名前だけである最初の引数で何もしません。一般的に、Perl自身がメソッドの解決に使用します。したがって、一般的なクラスメソッドをオブジェクトに対しても呼び出すことができます。
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
この構文は許可されていますが、しばしば誤解を招くことがありますので避けてください。
オブジェクトメソッドは、オブジェクト参照を最初の引数として受け取るので、(クラスメソッドとは異なり)オブジェクトデータを扱うことができます。
package Point;
use strict;
sub polar_coordinates {
my ($point) = @_;
my $x = $point->{x};
my $y = $point->{y};
return (sqrt($x * $x + $y * $y), atan2($y, $x));
}
1;
同じメソッドは両方のケースを追跡できます:クラスまたはオブジェクトメソッドとして呼び出されるとき:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
最新のPerlでクラスを定義する
利用可能ですが、クラスを一から定義することは現代のPerlではお勧めできません。より多くの機能と利便性を提供するヘルパーOOシステムの1つを使用します。これらのシステムには、
Moose
- Perl 6 OOデザインに触発されたClass::Accessor
- 軽量のMooseへの代替Class::Tiny
- 真に最小クラスビルダー
ムース
package Foo;
use Moose;
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write boolean property
sub qux {
my $self = shift;
my $barIsBaz = $self->bar eq 'baz'; # property getter
$self->baz($barIsBaz); # property setter
}
クラス::アクセサ(構文のムース)
package Foo;
use Class::Accessor 'antlers';
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write property (only 'is' supported, the type is ignored)
クラス::アクセサ(ネイティブ構文)
package Foo;
use base qw(Class::Accessor);
Foo->mk_accessors(qw(bar baz)); # some read-write properties
Foo->mk_accessors(qw(qux)); # a read-only property
クラス:: Tiny
package Foo;
use Class::Tiny qw(bar baz); # just props
ロール
Perlでの役割は基本的に
- メソッドと属性のセット
- クラスに直接注入されます。
役割は、(役割を消費すると言われている)は、任意のクラスになる (またはに適用 )することができる機能の一部を提供します。役割は継承することはできませんが、別の役割によって消費される可能性があります。
ロールは、メソッド自体を実装する代わりに、消費するクラスにいくつかのメソッドを実装する必要があります(JavaまたはC#のインタフェースと同様)。
Perlにはロールの組み込みサポートがありませんが、そのようなサポートを提供するCPANクラスがあります。
package Chatty;
use Moose::Role;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Moose;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}
あなたのOOシステムがロール(例えば、 Class::Accessor
またはClass::Tiny
)のサポートを提供しない場合に使用します。属性をサポートしていません。
package Chatty;
use Role::Tiny;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Class::Tiny;
use Role::Tiny::With;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}