005.Perl Programming[Class]
1.オブジェクト指向の基本
元来、Perlのプログラミング手法は、一つ一つの処理に注目し、機能ごとに分割してプログラムをまとめていく構造化プログラミングが主流であった。構造化プログラミングの利点としては、同じ処理はひとつの冠するを呼び出して何度も実行することができるため、簡潔な記述ができるという点にある。しかし大規模なシステム構築に採用しようとするとデータ構造が複雑化し、プログラムのメンテナンスや修正が難しくなるという欠点がある。そこで新しく考案されたのがオブジェクト指向プログラミングである。
オブジェクト指向プログラミングは構造化プログラミングとは異なり、オブジェクト指向プログラミングは、変数などのデータとメソッド、データを処理する手続きをオブジェクトというひとつのまとまりとして部品化し、それらを組み合わせていくプログラミング手法である。オブジェクト指向プログラミングではオブジェクト内で行っている内部情報をい隠蔽するという目的もある。このことによりプログラマーがオブジェクトを利用する際に、内部の構造やどのような処理が行われているのかといったことを意識する必要が少なくなる。このような利点から、オブジェクト指向プログラミングは大規模なシステム構築においてとても有効的なプログラミング手法であるとされている。
Perlはオブジェクト指向性を備えて開発されたプログラム言語ではないため、C++ javaなどのようなデータ型としてのクラスは存在しない。Perlでクラスを作成するには変数やサブルーチンに名前空間を管理するためのペッケージに特殊な処理を行う必要がある。Perlでは一般的に関数のことをサブルーチンと呼ぶが、オブジェクト指向では関数のことを「メソッド」と呼ぶ。サブルーチンとメソッドの違いはメソッドの場合、暗黙のうちにオブジェクトそのものが第1引数として呼ばれる点である。つまり、メソッドの第1引数は必ずオブジェクトのリファレンスとなる。この第1引数のことをインボカントと呼ぶ。
2.Class
オブジェクト指向というのは実際に存在するものと同じである。クラスというのは共通の性質を持ったオブジェクトの集合、オブジェクトというのは実際に値を設定した実体のことでありインスタンスとも呼ぶ。perlではクラスはパッケージの別名のように扱われる。実際、Perlでクラスを作成するにはパッケージを作成することから始める。
package car;
このように宣言する。クラスとパッケージの違いはクラスには「コンストラクタ」という特殊なメソッドを定義する必要があるという点である。コンストラクタとは、新しいオブジェクトが生成されるときに呼び出される特殊なメソッドである。Perlではコンストラクタには任意の名前をつけることができるが、一般的には[new]という名前が使われている。newを使ってコンストラクタを定義する。コンストラクタもクラスのメソッドのひとつなので、第1引数にクラスのリファレンス(インボカント)が渡される。
sub new {
my $class=shift;
}
次にオブジェクトを用意する。perlではオブジェクトにスカラー、配列、ハッシュといったあらゆる型の変数を使用する。
my $self ={
name=>"soralis",
body=>"metal",
};
最後に定義したクラスに先ほど作成した変数を関連付ける作業を行う。クラスに作成した変数を関連付けるにはbless関数を使用する。
bless関数は二つの引数をとる。第1引数に[関連付けたい変数のリファレンス]、[第2引数に関連づけるクラス名]を指定する。
bless $self,$class;
$selfをblessすることによって、catクラスのオブジェクトとして関連付けされる。オブジェクトの作成から、blessされたオブジェクトのリファレンスを返すまでの作業を一般的にコンストラクタで行う。
先ほど定義したcatクラスのコンストラクタは以下のようになる。
sub new{
my $class=shift;
#オブジェクトを用意
my $self ={
name=>"soralis",
body=>"metal",
};
#blessする
bless $self,$class;
return $self;
}
これがクラスを定義する一連の流れになる。定義したクラスを使用するには[use パッケージ名」で使用するクラスを宣言しなければならない。
use car;
次にクラスのオブジェクトを作成する。オブジェクトを作成するにはクラスで定義したコンストラクタを使う。
my $car =new car;
これにより$catにはcatクラスのオブジェクトのリファレンスが入る。catクラスのメソッドやプロパティにアクセスするには「->(アロー演算子)」を用意する。
$car->{name} #carクラスのnameプロパティ
$car->{body} #carクラスのbodyメソッド
package car;
#コンストラクタ
sub new{
my $class =shift;
##オブジェクト
my $self={
name=>"soralis",
body=>"metal",
speed=>"60km",
};
return bless $self,$class;
}
#eatメソッド
sub run{
#インボカント
my $self =shift;
print "$self->{name}が $self->{speed}で走る。\n";
}
1;
3.Class[@ISA] クラス継承
オブジェクト指向プログラミングにおいて「継承」とは、ある既存のクラスをベースにして、新しく別のクラスを定義することである。ベースとなるクラスは「スーパクラス」、新しく定義したクラスを「サブクラス」と呼ぶ。サブクラスにはスーパークラスのメソッドなどの性質が引き継がれるため、スーパークラスとは異なる部分を定義するだけですむ。
Perlでクラスを継承するには、@ISAという配列に継承したいクラス名を宣言する。@ISA配列を使ってクラスを継承する場合には、useであらかじめスーパークラスを宣言しておく必要がある。
次の例文ではあらかじめsuperclass,Subclassというパッケージを定義して、ubclass.pm内で@ISA配列を使いSuperClassを継承させている。subclassのみを呼び出し、SubClass内のメソッドを使用すると共に、SubClass.pmで継承したSuperClass内のメソッドSuper_methodを使用している。
package SuperClass;
use strict;
#コンストラクタ
sub new{
my($class,%args)=@_;
#オブジェクト
my $self=bless{},$class;
$self->init(%args);
return $self;
}
#メソッド
sub super_method{
my $self=shift;
print "SuperClassで定義されているメソッド\n";
}
1;
#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#
package SubClass;
use SuperClass;
use strict;
our(@ISA);
@ISA=qw(SuperClass);
sub init{
my($self,%args)=@_;
$self->{name}=$args{name};
$self->{job}=$args{job};
}
sub get_name{
my $self=shift;
return $self->{name};
}
sub get_job{
my $self=shift;
return $self->{job};
}
1;
############################################################
#!/usr/bin/perl
use lib qw(./);
use SubClass;
use strict;
my $subclass=new SubClass(name=>"太郎",job=>"プログラマー");
#サブクラスのメソッド
print "サブクラスのメソッドを使用\n";
print "名前:".$subclass->get_name. "\n";
print "職業:".$subclass->get_job. "\n";
#スーパークラスのメソッド
print "スーパークラスのメソッドを使用\n";
#メソッドが継承されている
$subclass->super_method;
#############################################################
4.Class[base]クラス継承
Perlでクラスの継承を行うには、@ISA配列を使う以外に、[baseプラグマモジュール]を使用して継承を実行することもできる。@isa配列を使って継承を行うと、あらかじめスーパークラスをuseしておかなければならない。baseプラグマモジュールを使用すると、スーパークラスをuseする必要がないので、ソースが簡潔になる。@ISA配列とbaseプラグマのソース比較は次のとおり。
[@ISA]
use SuperClass;
our(@ISA);
@ISA=qw(SuperClass);
[base]
use base qw(SomeClass);
package SubClass;
use strict;
use base qw(SuperClass);
sub init{
my($self,%args)=@_;
$self->{name}=$args{name};
$self->{job}=$args{job};
}
sub get_name{
my $self=shift;
return $self->{name};
}
sub get_job{
my $self=shift;
return $self->{job};
}
1;
#############################################################
Package SuperClass;
use strict;
sub new{
my($class,%args)=@_;
my $self=bless {},$class;
$self->init(%args);
return $self;
}
sub super_method{
my $self=shift;
print "SuperClassで定義されているメソッドです。\n";
}
1;
############################################################
#!/usr/bin/perl
use lib qw(./);
use SubClass;
use strict;
my $subclass=new SubClass(name=>"太郎",job=>"プログラマー");
#サブクラスのメソッド
print "サブクラスのメソッドを使用\n";
print "名前:". $subclass->get_name ."\n";
print "職業:".$subclass->get_job ."\n";
#スーパークラスのメソッド
print "スーパークラスのメソッドを使用\n";
#スーパークラスのメソッドが継承されている
$subclass->super_method;
5.Class[can]メソッド宣言確認
使用するクラスに指定したメソッドが宣言されているかどうか調べるために、Perlではcanメソッドが用意されている。canメソッドはクラスを定義すると自動的に使用できるようになるので、プログラマーがクラス内にメソッドとして定義する必要はない。can メソッドは以下のように使用する。
$class->can('method_name');
定義したクラスのオブジェクトのcanメソッドを呼び出す。canメソッドはメソッド名を引数に持つ
#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#
#!/usr/bin/perl
use lib qw(./);
use Man;
use strict;
my $man =new Man(name=>"一郎");
#get_nameメソッドが定義されているかどうか
if($man->can("get_name")){
print "get_nameは定義されている\n";
print "名前:". $man->get_name ."\n\n";
}else{
print "get_nameは定義されていません\n";
}
#get_jobメソッドが定義されているかどうか
if($man->can("get_job")){
print "get_jobは定義されています。\n";
print "名前:". $man->get_job ."\n\n";
}else{
print "get_jobは定義されていません\n";
}
#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#
package Man;
use strict;
sub new{
my($class,%args)=@_;
my $self=bless{},$class;
$self->{name}=$args{name};
$self->{job}=$args{job};
return $self;
}
#get_nameメソッド
sub get_name{
my $self=shift;
return $self->{name};
}
1;
###################################################################
6.Class[isa]クラス継承確認
定義しているクラスがあるクラスを継承しているかどうか調べるために、Perlではisaメソッドが用意されている。isaメソッドもcanメソッドと同じくクラスを定義すると自動的に使用することができる
メソッドなので、ユーザーがクラス内に定義する必要はない。
##################################################################
package SuperClass;
use strict;
sub new{
my($class,%args)=@_;
my $self=bless {},$class;
$self->init(%args);
return $self;
}
sub super_method{
my $self=shift;
print "SuperClassで定義されているメソッドです。\n";
}
1;
################################################################
package SubClass;
use base qw(SuperClass);
use strict;
sub init{
my($self,%args)=@_;
$self->{name}=$args{name};
$self->{job}=$args{job};
}
#get_nameを定義
sub get_name{
my $self=shift;
return $self->{name};
}
#get_jobを定義
sub get_job{
my $self=shift;
return $self->{job};
}
1;
#############################################################
package OtherClass;
use strict;
sub new{
my($class,%args)=@_;
return bless{},$class;
}
#other_methodを定義
sub other_method{
my $self=shift;
print "OtherClassで定義されているメソッドである\n";
}
1;
###########################################################
#!/usr/bin/perl
use lib qw(./);
use SubClass;
use OtherClass;
use strict;
#SubClassのオブジェクト;
my $subclass=new SubClass(name=>"太郎",job=>"サッカー選手");
#OtherClassのオブジェクト
my $otherclass=new OtherClass;
#SubClassはSuperClassを継承しているか。
#isaメソッドでクラス継承を確認する
if($subclass->isa("SuperClass")){
print "SubClassはSuperClassを継承しています。\n";
}else{
print "SubClassはSuperClassを継承していません。\n";
}
#OtherClassはSuperClassを継承しているか
if($otherclass->isa("SuperClass")){
print "OtherClassはSuperClassを継承しています。\n";
}else{
print "OtherClassはSuperClassを継承していません。\n";
}
7.Class[Exporter]制御
Exporterモジュールはパッケージの名前空間に対してエクスポートするものを制御するためのモジュールである。Exporterモジュールにはクラス内のメソッドを呼び出すための以下のような特殊変数が用意されている。
@EXPORT 指定したメソッドや変数を自動的にインポートする。
@EXPORT_OK 指定したメソッドや変数を呼び出し側で明示的に指定する必要がある。
@EXPORT_FAIL 指定したメソッドや変数をインポート不可にする。
@%EXPORT_TAGS 指定したメソッドや変数は呼び出し側で指定したハッシュのキーに応じてインポートする。
@EXPORTでインポート可能にしたメソッドや変数はインポート先でパッケージ修飾子を省略して呼び出すことができる。
@EXPORT_OKで明示的に指定された場合、インポート可能にしたメソッドや変数は、useする際にメソッドや変数を指定する必要がある。
##################################################################################
package MyPackage;
use Exporter;
our(@EXPORT,@EXPORT_OK,%EXPORT_TAGS,@ISA);
@ISA=qw(Exporter);
#自動的に呼び出し可能なサブルーチン
@EXPORT=qw(function1);
#明示的に指定して呼び出し可能なサブルーチン
@EXPORT_OK=qw(function2 function3);
#キーを指定で呼び出し可能なサブルーチン
%EXPORT_TAGS=(TEST=>[qw(function3)]);
sub function1{
print "function1です。\n";
}
sub function2{
print "function2です。\n";
}
sub function3{
print "function3です。\n";
}
sub function4{
print "function4です。\n";
}
#################################################################################
#!/usr/bin/perl
use lib qw(./);
use MyPackage;
#自動的に呼び出し
function1();
#################################################################################
#!/usr/bin/perl
use lib qw(./);
use MyPackage qw(function2);
#明示的に呼び出し
function2();
#################################################################################
#!/usr/bin/perl
use lib qw(./);
use MyPackage qw(:TEST);
#キーを指定して呼び出し
function3();
#################################################################################
#!/usr/bin/perl
use lib qw(./);
use MyPackage;
#@EXPORT変数に指定していない場合はパッケージ修飾子から記述しないとエラー
MyPackage::function4();
#################################################################################
8.Class:Struct 構造化
クラスを定義する際にコンストラクタや変数の設定などを記述していると、複雑なコードになってしまう。
たとえば定義するクラスが値を保持しておくためだけのような簡単なもので、今後スーパークラスとして使用されることがないのであれば、Class:Structモジュールを使用することで、クラスの初期化などの作業を簡素化することができる。
Class::Structモジュールはstructサブルーチンのみをエクスポートする。structサブルーチンは定義するクラスに必要なプロパティを定義することができる。
しかしClass::Structはクラス設計やプロパティ操作に特化したモジュールではない。必要なデータの集まりをstructサブルーチンで定義しているだけである。
#################################################################################
package MyPackage;
use strict;
sub new{
my($class,%args)=@_;
my $self=bless{},$class;
$self->{name}=$args{name};
$self->{jobs}=$args{jobs};
$self->{hobby}=$args{hobby};
$self->{favorite}=$args{favorite};
return $self;
}
sub name{
my($self,$name)=@_;
if($name){
$self->{name}=$name;
}
return $self->{name};
}
sub hobby{
my($self,$hobby)=@_;
if($hobby){
$self->{hobby}=$hobby;
}
return $self->{job};
}
sub favorite{
my($self,$favorite)=@_;
if($favorite){
$self->{favorite}=$favorite;
}
return $self->{favorite};
}
1;
##################################################################################
package StructTest;
use Class::Struct;
use strict;
struct(
name=>'$',
job=>'$',
hobby=>'$',
favorite=>'$'
);
1;
###################################################################################
#!/use/bin/perl
use lib qw(./);
use StructTest;
use MyPackage;
use strict;
my $structtest=new StructTest(
name=>"太郎",
job=>"営業",
hobby=>"音楽鑑賞",favorite=>"コーンスープ"
);
print "StructTest モジュールの結果:\n";
print "名前:".$structtest->name ."\n";
print "職業:".$structtest->job . "\n";
print "趣味:".$structtest->hobby ."\n";
print "好きなもの" .$structtest->favorite ."\n\n";
#値を変更
$structtest->job("事務職");
$structtest->hobby("読書");
$structtest->favorite("味噌汁");
print "値を変更しました。\n";
print "名前:".$structtest->name ."\n";
print "職業:".$structtest->job ."\n";
print "趣味:".$structtest->hobby ."\n";
print "好きなもの:" .$structtest->favorite. "\n\n";
print "―" x 20 . "\n\n";
my $mypackage =new MyPackage(
name=>"太郎",
job=>"営業",
hobby=>"音楽鑑賞",
favorite=>"ラーメン"
);
print "MyPackageモジュールの結果:\n";
print "名前:".$mypackage->name ."\n";
print "職業:".$mypackage->job ."\n";
print "趣味:".$mypackage->hobby."\n";
print "好きなもの:".$mypackage->favorite."\n";
####################################################################################
9.Class[Class::Accessor]
Class::Accessorモジュールを使用してクラスを作成すると、プロパティと同じ名前のアクセサメソッドを簡単に作成できる。Class::Accessorのmk_accessorsサブルーチンでアクセサメソッドを作成したいプロパティを指定する。
指定されたプロパティはアロー演算子を使用してアクセスすることができる。
10.Class[Class::Declare]パブリック/プライベート実装
PerlにはJavaやPHP(Version5から)のようなメソッド、プロパティのアクセス制御がある。Perでpublic(クラス内、またはクラス外からのアクセスが可能)、private(そのクラス内のみアクセス可能)を実装するにはClass::Declareを使用する。Class::Declareを敬称し、Class::Declareのdeclareメソッドにpublic,privateにするプロパティを指定する。privateで宣言したプロパティ、メソッドをそのクラス以外で使用するとエラーになる。
##############################################################################################################
package DeclareTest;
use strict;
use base qw(Class::Declare);
#Class::Declare
_PACKAGE_->declare(
public=>{public_var=>"publicです。"},
private=>{priuvate_var=>"privateです。"}
);
sub public_method{
my $self=_PACKAGE_->public(shift);
print "publicメソッドである\n";
}
sub use_private_method{
my $self=_PACKAGE_->public(shift);
print "publicメソッドを介してのprivateメソッド使用はok\n";
$self->private_method;
}
sub private_method{
my $self=_PACKAGE_->private(shift);
print "privateメソッドである\n";
}
1;
#################################################################################################
#!/usr/bin/perl
use lib qw(./);
use DeclareTest;
use strict;
my $declaretest=new DeclareTest;
#public
print "Public:".$declaretest->public_var."\n";
#値を変更
$declaretest->public_var("値を変更した");
print "public_varの値を変更:".$declaretest->public_var ."\n";
##メソッドを使用
$declaretest->public_method;
$declaretest->use_private_method;
#priovateで宣言しているものは直接使用できない。
print "private:" .$declaretest->private_var."\n";
$declaretest->private_method;
##################################################################################################