Perlで時刻計算：オブジェクト指向で書いてみる

# Perlってオブジェクトで書けたのかっ

　2022年4月、Perlがオブジェクトで書ける事を初めて知った。`class`ではなく`package`というらしい。`Perl`の`Time::Piece`について調べていたところ、偶然知ってしまった。練習と備忘録も兼ねてさっそく書いてみる。

# 素人です

　別記事でも書いてますが本職ではありません。なので基本間違ってると思います。素人が独学でどのようにオブジェクト指向を理解しているのかという無駄なテーマであなたの数分を無駄にします。

# 設計

　従業員が出勤したときの時刻、退勤したときの時刻を記録し、その差から何時間会社にいたのか、を`HH:MM:SS`の形式で記録も計算も表示も行います。なので、`従業員`というクラスに、`従業員の名前`、`出勤時刻`、`退勤時刻`、使ってないけど`休憩時間`というデータを持たせます。またそのデータから`何時間勤務していたか`をメソッドとします。

# コードを書く

```Perl
#!/usr/bin/perl

use strict;
use warnings;

use Time::Piece;
use Time::Seconds;    # 計算するためにコレが必要らしい

###########################################################
# hh:mm:ss で表現された時刻同士の差分を計算します。
# 日付の概念がないので、0時を超える残業をするとエラーになるよ～
# あくまでも練習
###########################################################

## memo
# @_ は引数を受け取るという意味
# フォーマット識別子で整形したものを変数に格納するときは sprintf, 画面に表示するときは printf

package mycalctime;    # 本当は「zyuugyouin」とか「MyEmployee」の方が良いよ！

my $count = 0;     # 従業員が何人いるか、人数をカウントできます

# 従業員に共通する部分の設計。newじゃなくても良いらしいけど、newという名称にするのが一般的らしい。
# ソレっぽく言うと、コンストラクタ（newしたときに勝手に実行されるヤツ）というらしい。
sub new {
    my($class, $name, $start_time, $end_time, $break_time) = @_;        # 第1引数：クラス名
    my($self) = {};                                                     # カラの参照オブジェクトを生成
    bless($self, $class);                                               # オブジェクトとクラス名をbless
    $self->{NAME} = $name;                                              # 各データ（属性）の初期化
    $self->{START_TIME} = $start_time;
    $self->{END_TIME} = $end_time;
    $self->{BREAK_TIME} = $break_time;
    $count++;                                                           # クラス変数
    return $self;
}

# デストラクタ、何も考えずに書く。DESTROYは決まっている語句。
# インスタンスが減るので$countはデクリメントする。
sub DESTROY {
    my($self) = @_;
    $count--;
}

# メソッド：オブジェクトの総数を返す。何となく書いておく。
sub getCount {
    return $count;
}

## 以下、お決まりのセッターゲッター。使う使わないに関係なく何も考えずに書く。意味とか深く考えてはいけない。
## 名前とか出勤時刻、退勤時刻という「データ」に具体的な「値」を入れたり出したりします。
# メソッド：従業員の名前を設定する
sub setName {
    my($self, $name) = @_;
    $self->{NAME} = $name;
}

# メソッド：従業員の名前を取得する
sub getName {
    my($self) = @_;
    return $self->{NAME};
}

# メソッド：開始時刻の設定と取得
sub setStartTime {
    my($self, $start_time) = @_;
    $self->{START_TIME} = $start_time;
}

sub getStartTime {
    my($self) = @_;
    return $self->{START_TIME};
}

# メソッド：終了時刻の設定と取得
sub setEndTime {
    my($self, $end_time) = @_;
    $self->{END_TIME} = $end_time;
}

sub getEndTime {
    my($self) = @_;
    return $self->{END_TIME};
}

# メソッド：休憩時間の設定と取得
sub setBreakTime {
    my($self, $break_time) = @_;
    $self->{BREAK_TIME} = $break_time;
}

sub getBreakTime {
    my($self) = @_;
    return $self->{BREAK_TIME};
}

### 何も考えずにセッターゲッターを記載するのはここまで ###

# メソッド：開始時刻と終了時刻の差を計算
sub calcStayTime {
    my($self) = @_;
    my $START_TIME = $self->getStartTime;
    my $END_TIME = $self->getEndTime;
    my $T1 = Time::Piece->strptime($START_TIME, "%H:%M:%S");
    my $T2 = Time::Piece->strptime($END_TIME, "%H:%M:%S");
    my $RESULT_TIME = $T2 - $T1;
    my ($sec, $min, $hour, $day, $mon, $year, $mday, $yday, $isdst) = gmtime($RESULT_TIME);
    my $RESULT = sprintf("%02d:%02d:%02d\n", $hour, $min, $sec);

    return $RESULT;
}

###############################################################################
package main;

# 従業員"DAREKASAN"をnewします
my $darekasan = mycalctime->new("DAREKASAN");

# "DAREKASAN"の出勤、退勤時間を設定して計算結果を見る
$darekasan->setStartTime("09:10:15");
$darekasan->setEndTime("18:05:20");

print ($darekasan->getName, " : ", $darekasan->calcStayTime);

```

# 実行結果

```
DAREKASAN : 08:55:05
```

　ヤバい、Perl始まった・・・（感動）

# 蛇足：オブジェクトで書くと何が良いの？

　上記では従業員`DAREKASAN`だけの出退勤時刻の記録と差分計算を行ったので、1人だけなら手続き処理で書いた方が短くわかりやすくなると思います。でも従業員が100人くらいに増えたとき、追加の99人分の出退勤時刻変数の準備、その準備された変数で差分計算関数に入れて結果を得るための変数の準備など、変数地獄になります。

　オブジェクトで書いておくと設計部分が共通化できるので、後々コードの量が増えてきたときに全体のコードの量が減るわ、変数の量が減るわ、メンテナンスも楽だわで良いことが多い。

## 何をクラスにすれば良いのか？

　これは悩みます。とりあえず手続き型で書いていって、「あぁっ、この部分同じものだけど違うものとして振舞ってくれる何か便利な方法ないの！？」って思いをたくさんすれば、センスが磨かれるような気がします。とりあえずPerlは変数地獄から解放されるだけでもかなーり有難い。

　僕のオブジェクトプログラミングの理解は「同じものだけど違うものとして振舞ってくれる」という感覚です。上記の例でいえば、`名前`、`出勤時刻`、`退勤時刻`は従業員が皆共通して持っている同じ`データ`だけど、その`データの中身`はそれぞれで違う、という感じです。

## たぶんつまづくところ

　`->`コレ。アロー演算子。これの意味を本気で知ろうとすると、コンピュータになぜメモリという部品が積まれていてどう使われているかという深～い話になるので、僕のような野良素人はここを深堀りするのはやめておくことにします。c言語の`ポインタ`や、vbaの`byVal`と`byRef`の違い等がなんとなくの理解の参考になるような・・・。動けば良いんだよ！動けば！

## 人を増やしてみる

　と、上記の流れを踏まえて新人が2人入ってきたとします。コードを追記します。

```Perl
# 新入社員が2人増えました
my $sinzinA = mycalctime->new("sinzinA");
my $sinzinB = mycalctime->new("sinzinB");

# 開始、終了時間の設定
$sinzinA->setStartTime("08:55:20");
$sinzinA->setEndTime("18:03:35");
$sinzinB->setStartTime("8:49:50");
$sinzinB->setEndTime("18:03:30");

print ($sinzinA->getName, " : ", $sinzinA->calcStayTime);
print ($sinzinB->getName, " : ", $sinzinB->calcStayTime);
```

　実行結果。

```
sinzinA : 09:08:15
sinzinB : 09:13:40
```

　もし手続き型で書いていたら、もっと変数宣言が必要になり、処理もそれぞれで書く必要があります。例はセットするデータも2つ、処理も1つと少ないですが、本格的にプログラミングするとそれはそれは大変な量に・・・。オブジェクト指向で書くとそこらへんが省略できます。

# Perlのオブジェクトまとめ
・`package`でクラス名を宣言。
・クラスのデータ（属性、プロパティ）が入るものを`$self->{DATA}`で準備。
・よくわからんが`bless($self, $class);`を書く。
・セッターゲッターを準備。
・`$instance->{DATA}`でも値にはアクセスできるが、値のセットや参照はセッターゲッターで行う。

　`Time::Piece`の計算例とオブジェクト指向プログラミングの練習でした。おしまい。

# 参考にしたサイト

https://www.tohoho-web.com/perl/class.htm