この記事はモバイルファクトリー Advent Calendar 2017の2日目の記事です。
1日目に引き続き、@carimaticsが担当します。
1日目の記事は『Docker上でPerlのWebアプリケーションを動かしてみる』でした。
本記事では、Perlにおけるオブジェクト指向プログラミングの入門です。
オブジェクト指向プログラミング自体の入門ではありません。
開発環境
以下の環境で動作確認をしています。
- OS: macOS Sierra version 10.12.6
- Perl: perl v5.26.0
- Carton: carton v1.0.28
- Docker: Version 17.09.0-ce-mac35
- Docker image: perl:5.26
本記事のコードは以下のコマンドで実行されるDockerコンテナで動かしました。
$ docker run -it perl:5.26 bash
準備
Cartonのインストール
Cartonがなければインストールします。
$ cpanm Carton
(中略)
7 distributions installed
$ carton --version
carton v1.0.28
作業ディレクトリの作成
適当なディレクトリを作成してそこに移動します。
/path/to/Work
となっていますが、適宜読み替えてください。
$ mkdir /path/to/Work
$ cd /path/to/Work
以下、本記事における「作業ディレクトリ」は、今作ったWorkディレクトリを指すとします。
依存モジュールのインストール
作業ディレクトリに以下の内容のcpanfileを作成します。
requires 'Mouse';
依存モジュールをインストールします。
$ carton install
(中略)
Complete! Modules were installed into Work
Perlでオブジェクト指向プログラミングを実現する
もともとPerlはオブジェクト指向言語ではありません。
しかし bless
を駆使したり、CPANモジュールを利用したりすることでオブジェクト指向プログラミングが可能です。
本記事では、 Mouse
というモジュールを用いてPerlにオブジェクト指向システムを導入します。
実行するスクリプトを Work/script
配下に、モジュールを Work/lib
配下に作成していきます。
Work/script
配下のスクリプトには、ファイルの先頭に以下の記述があるものとします。(冗長になるため、本文中では省略します)
use strict;
use warnings;
use utf8;
use feature qw/say/;
use lib 'lib';
クラス
Mouseを用いると、クラスの定義をすることができます。
xy平面上の点を表す Point
クラスを定義してみましょう。
package Point {
use Mouse;
has x => (is => 'ro', isa => 'Int');
has y => (is => 'ro', isa => 'Int');
};
1;
Mouse
モジュールを use
すると、そのパッケージを Mouse
クラスにすることができます。
has name => parameters
の構文で、属性(インスタンス変数)を定義します。
Point
では、 x
、 y
、という属性を定義しています。
パラメータはそれぞれ以下のような指定ができます。
-
is
-
ro
(read only)、rw
(read/write)、bare
(何もしない)
-
-
isa
- 変数に代入できる値の型を指定
今回は x
、 y
は両方ともread only( ro
)かつ整数( Int
)型となります。
では、 Point
クラスを使ってみましょう。
use Point;
my $p = Point->new(x => 3, y => 4); # インスタンス生成
say "x: ", $p->x;
say "y: ", $p->y;
Mouseクラスのインスタンスは ClassName->new(parameters)
で作成します。
また、引数で属性の初期化をすることができます。
実行してみます。
$ carton exec perl script/point.pl
x: 3
y: 4
継承
Mouseにも継承の仕組みがあり、あるクラスのサブクラスを作成することができます。
では、例とした先に作成した Point
に色の情報を持たせた ColorPoint
クラスを作成します。
package ColorPoint {
use Mouse;
extends qw/Point/;
has color => (is => 'ro', isa => 'Str');
};
1;
extends
でスーパークラスを指定できます。
Point
クラスに color
属性を持たせたクラスが出来上がりました。
実行してみましょう。
use ColorPoint;
my $cp = ColorPoint->new(x => 2, y => 3, color => 'red');
say "x: ", $cp->x;
say "y: ", $cp->y;
say "color: ", $cp->color;
$ carton exec -- perl script/color_point.pl
x: 2
y: 3
color: red
isa
による型チェックでは、指定されたクラスのサブクラスでもパスします。
2つの Point
間の距離を測る Ruler
クラスを作成します。
package Ruler {
use Mouse;
has p1 => (is => 'rw', isa => 'Point');
has p2 => (is => 'rw', isa => 'Point');
sub distance {
my $self = shift;
my $dx = $self->p2->x - $self->p1->x;
my $dy = $self->p2->y - $self->p1->y;
return sqrt(($dx**2) + ($dy**2));
}
};
1;
これは酷いコードですね!
しかし今は許容して話を進めます。
Ruler
クラスは Point
クラスの属性 p1
、 p2
を持ち、それらのユークリッド距離を計算しています。
use Ruler;
use Point;
use ColorPoint;
my $p1 = Point->new(x => 0, y => 0);
my $p2 = ColorPoint->new(x => 3, y => 4, color => 'blue');
my $ruler = Ruler->new();
$ruler->p1($p1);
$ruler->p2($p2);
say "distance: ", $ruler->distance();
属性への値のセットはコンストラクタでも可能ですが、 $instance->attr(value)
でも可能です。(read onlyでなければ)
$ carton exec -- perl script/ruler.pl
distance: 5
Ruler
の p2
は Point
クラスのインスタンスを持つことができますが、そのサブクラス( ColorPoint
)を持つこともできることを確認しました。
まとめ
Perlでもオブジェクト指向プログラミングができることを見ました。
has
とか書くの面倒くさいですね。
この程度のコードであれば他の言語とそんなに変わらないのですが、オブジェクト指向プログラミングを言語レベルでサポートしているわけではないので、書いてて煩わしさを覚える場面は多々あります。
個人的な意見としては、オブジェクト指向プログラミングをしたいなら他の言語を選択すべきだと思ってます。
モバイルファクトリー Advent Calendar 2017は、3日目も引き続き @carimatics が担当します。
よろしくお願いします。