My First Perl #4

クラス編

ということで、クラス。
Classで重要なのはやっぱり継承パターンと多態性でしょう。
アクセサはどうなってるのか、パッケージとかも含め、それくらいできると夢が広がりングな気がする

参考にはいつもの オブジェクト指向 / Perl逆引き事典 - サンプルコードによるPerl入門 を 見てます。

クラス作成

package が使えるそうな。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  my $hoge = shift;
  $self->{hoge} = $hoge;
  return $self;
}

my $hoge = Hoge->new('hello world');
print $hoge->{hoge} . "\n";

コンストラクタの定義から、プロパティの設定と取得はこうやるのね。
元のページ → 汎用的なコンストラクタの雛形 - サンプルコードによるPerl入門 には init メソッドが必要とのことだったけど、今回は無視
どうやら、new のコンストラクタには [Class(prototype), arg1, arg2..] といった形式で渡されているようですね
bless がよくわからないけど、js でいうところの Function.prototype.bind 的なものだろうか、第一引数に prototype を割り当てる的な(それだとapplyの方が近そうだけど)

プロパティ作成

もう少し、プロパティ
参考元を参考に、引数を色々受け取ってみる

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub init {
  my ($self, ($hoge, $foo, $bar)) = @_;

  $self->{hoge} = $hoge;
  $self->{foo} = $foo;
  $self->{bar} = $bar;
}

my $hoge = Hoge->new('hello', 'world', 'perl #4');
# print $hoge->hoge . ':' . $hoge->foo . ':' . $hoge->bar . "\n";
## => Can't locate object method "hoge" via package "Hoge"
print $hoge->{hoge} . ':' . $hoge->{foo} . ':' . $hoge->{bar} . "\n";

なるほど、$self->{verialble} で設定したものは、$self->variableで取り出せないのね(Hashだから?)。当たり前といえば当たり前だった。
それ以外は意外とフツーな感じ

メソッド作成

プロパティまでは作成できたので、次はメソッド。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub init {
  my ($self, ($hoge, $foo, $bar)) = @_;

  $self->{hoge} = $hoge;
  $self->{foo} = $foo;
  $self->{bar} = $bar;
}

sub greeting {
  my $self = shift;

  print $self->{hoge} . ':' . $self->{foo} . ':' . $self->{bar} . "\n";
}

my $hoge = Hoge->new('hello', 'world', 'perl #4');
$hoge->greeting();

先ほどの property で出力してたのを メソッドで。
第一引数に self(自分自身) が入ってるとか、python ぽい

メソッド引数

メソッドのプロパティはどうなってるのか。
たぶん、#init で試したから同じだと思うけど。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub init {
  my ($self, ($hoge, $foo, $bar)) = @_;

  $self->{hoge} = $hoge;
  $self->{foo} = $foo;
  $self->{bar} = $bar;
}

sub greeting {
  my $self = shift;
  my ($arg1, $arg2, $arg3) = @_;

  print $self->{hoge} . ':' . $self->{foo} . ':' . $self->{bar} . "\n";
  print $arg1 . ',' . $arg2 . ',' . $arg3 . "\n";
}

my $hoge = Hoge->new('hello', 'world', 'perl #4');
$hoge->greeting('a', 'b', 'c');

ふむ。
shift は @_ から一つ shift して、 @_ 自体は (a, b) = @_ とかのリスト構文的なので、値をまとめて代入できる。と

継承

クラスの継承。なんかちょっと特殊だった

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub init {
  my ($self, ($hoge, $foo, $bar)) = @_;

  $self->{hoge} = $hoge;
  $self->{foo} = $foo;
  $self->{bar} = $bar;
}

sub greeting {
  my $self = shift;
  my ($arg1, $arg2, $arg3) = @_;

  print $self->{hoge} . ':' . $self->{foo} . ':' . $self->{bar} . "\n";
  print $arg1 . ',' . $arg2 . ',' . $arg3 . "\n";
}

package Foo;
use base('Hoge');

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub greeting {
  my $self = shift;

  print 'Im Foo#' . $self->{hoge} . ',' . $self->{foo} . "\n";
}

my $foo = Foo->new('hello', 'world', 'perl #4');
$foo->greeting('a', 'b', 'c');

まず、

  • @ISA はよくわからなかったので、use baseによる継承を使った。
  • super を指定しないでも上位メソッドはコンストラクタから呼べた
  • new のコンストラクタには、上位クラスの参照はなかった
  • override 記述をしなくても、自動的に override だった。
  • 上位プロパティは特に指定なしに呼び出せた。

これ、use で複数とか指定するとどうなっちゃうんだろ。差分継承だったらいいな。(それはいつかやる)

overrideしたメソッドから上位メソッドの呼び出し

疑問に思ったのでこれをやってみる
この方法は、ここから 参照。
ちなみに、やらんとしてることは、javascript だとこんな感じ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Hoge = function(a, b) {
  this.a = a;
  this.b = b;
};
Hoge.prototype.greeting = function (){
  console.log('Im Hoge: ' + this.a + ',' + this.b);
};

var Foo = function(){
  Hoge.apply(this, arguments);
};
Foo.prototype.greeting = function(bar){
  Hoge.prototype.greeting.apply(this, arguments);
  console.log('bar value => ' + bar);
};

var foo = new Foo('hello', 'world');
foo.greeting('**bar value**');

ということで、perl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use strict;
use warnings;

package Hoge;

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub init {
  my ($self, ($hoge, $foo, $bar)) = @_;

  $self->{hoge} = $hoge;
  $self->{foo} = $foo;
  $self->{bar} = $bar;
}

sub greeting {
  my $self = shift;
  my ($arg1, $arg2, $arg3) = @_;

  print $self->{hoge} . ':' . $self->{foo} . ':' . $self->{bar} . "\n";
  print $arg1 . ',' . $arg2 . ',' . $arg3 . "\n";
}

package Foo;
use base('Hoge');

sub new {
  my $proto = shift;
  my $self = {};

  bless $self, $proto;

  $self->init(@_);

  return $self;
}

sub greeting {
  my $self = shift;

  $self->SUPER::greeting('Im', 'Foo', '#greeting');
}

my $foo = Foo->new('hello', 'world', 'perl #4');
$foo->greeting('a', 'b', 'c');

なるほど。$self->SUPER::#method で上位メソッド(というか親インスタンスのメソッドの呼び出し)ができるようになるのね。
これは使い道が広がるわ。

次回

アクセサを見てみたいところだけど、やっぱここで触れなかった package あたり。 どうやって分割してんのかと。