sironekotoroの日記

Perl で楽をしたい

「良いコード/悪いコードで学ぶ設計入門」第6章その3 型チェックで分岐しない #ミノ駆動本

リスト6.51〜6.54まで

一気にまとめたコードがこれ。

途中でしれっと出てくる Money クラスの add メソッドも書いております。

・・・Perl で書き換えながらめっちゃ書きにくさを感じることがあります。

このコードだと、interface の HotelRates の中で分岐コードを書くところ。

まぁ、ここで書いているのは「悪い(例示の)コード」なので、書きにくくて正解なんですけどね。

そうそう。

途中、コメントアウトしたところがあります。

# return $self->fee()->add( Money->new( amount => 3000 ) );

my $money = $self->fee();
return $money->add( Money->new( amount => 3000 ) );

1行でメソッドチェーンでかけるんですが、途中途中で返り値はなんだっけ・・・?

値?値オブジェクト?

みたいになったので、統一した方がいいんだろうなぁ、と思いました。

ここクリックして展開

use strict;
use warnings;
use Function::Parameters;
use feature qw/say/;

package Money {
    use Carp qw/croak/;
    use Mouse;
    use namespace::autoclean;
    use Readonly;
    use constant { MIN => 0 };

    has amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 1,
    );

    method add($other) {

        my $added = $self->amount() + $other->amount();
        return Money->new( amount => $added );
    }

    __PACKAGE__->meta->make_immutable();
}

# 宿泊料金を表すロール
package HotelRate {
    use Mouse::Role;
    requires 'fee';

    fun busy_season_fee($self) {
        if ( ref $self eq 'RegularRates' ) {

            # return $self->fee()->add( Money->new( amount => 3000 ) );
            my $money = $self->fee();
            return $money->add( Money->new( amount => 3000 ) );

        }
        elsif ( ref $self eq 'PremiumRates' ) {

            # return $self->fee()->add( Money->new( amount => 5000 ) );
            my $money = $self->fee();
            return $money->add( Money->new( amount => 5000 ) );
        }
    }

}

#通常宿泊料金
package RegularRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 7000 );
    }
    __PACKAGE__->meta->make_immutable();

}

# プレミアム宿泊料金
package PremiumRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 12000 );
    }
    __PACKAGE__->meta->make_immutable();
}

package main;

# 追加した繁忙期ロジック

# 通常宿泊料金&繁忙期
my $regular_rate = RegularRates->new();
say $regular_rate->fee->amount();                  # 7000
say $regular_rate->busy_season_fee()->amount();    # 10000

# プレミアム宿泊料金&繁忙期
my $premium_rate = PremiumRates->new();
say $premium_rate->fee->amount();                  # 12000
say $premium_rate->busy_season_fee()->amount();    # 17000

リスト6.55 繁忙期料金を切り替えられるよう interface に定義

先の「悪いコード」に比べて、「いいコード」の方が直感的ですね。

あと、「図6.8」みたいなオブジェクト図のがいまいちわからなかったんですが、コードを見てからだと理解できるという。

というか、オブジェクト指向も書物を読んだだけではわからなくて、書いてみて、書き続けてやっとわかるみたいな。

割と脳筋使うところだなぁという感じです。

ここクリックして展開

use strict;
use warnings;
use Function::Parameters;
use feature qw/say/;

package Money {
    use Carp qw/croak/;
    use Mouse;
    use namespace::autoclean;

    has amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 1,
    );

    method add($other) {

        my $added = $self->amount() + $other->amount();
        return Money->new( amount => $added );
    }

    __PACKAGE__->meta->make_immutable();
}

# 宿泊料金を表すロール
package HotelRate {
    use Mouse::Role;

    requires qw/fee busy_season_fee/;
}

#通常宿泊料金
package RegularRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 7000 );
    }

    method busy_season_fee() {
        my $money = $self->fee();
        return $money->add( Money->new( amount => 3000 ) );
    }

    __PACKAGE__->meta->make_immutable();

}

# プレミアム宿泊料金
package PremiumRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 12000 );
    }

    method busy_season_fee() {
        my $money = $self->fee();
        return $money->add( Money->new( amount => 5000 ) );
    }
    __PACKAGE__->meta->make_immutable();
}

package main;

# 追加した繁忙期ロジック

# 通常宿泊料金&繁忙期
my $regular_rate = RegularRates->new();
say $regular_rate->fee->amount();                  # 7000
say $regular_rate->busy_season_fee()->amount();    # 10000

# プレミアム宿泊料金&繁忙期
my $premium_rate = PremiumRates->new();
say $premium_rate->fee->amount();                  # 12000
say $premium_rate->busy_season_fee()->amount();    # 17000

よし、6章も残りは「フラグ引数」のみ。がんばろー

おまけ

MouseX::AttributeHelpers; を使ってみたパターン。

さっぱり!

ここクリックして展開

use strict;
use warnings;
use Function::Parameters;
use feature qw/say/;

package Money {
    use Carp qw/croak/;
    use Mouse;
    use MouseX::AttributeHelpers;
    use namespace::autoclean;

    has amount => (
        metaclass => 'Number',
        is        => 'rw',
        isa       => 'Int',
        required  => 1,
        default   => 0,
        provides  => {
            add => 'add',
        }
    );

    __PACKAGE__->meta->make_immutable();
}

# 宿泊料金を表すロール
package HotelRate {
    use Mouse::Role;

    requires qw/fee busy_season_fee/;
}

#通常宿泊料金
package RegularRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 7000 );
    }

    method busy_season_fee() {
        my $money = $self->fee();
        return $money->add(3000);
    }

    __PACKAGE__->meta->make_immutable();

}

# プレミアム宿泊料金
package PremiumRates {
    use Mouse;
    use namespace::autoclean;

    with 'HotelRate';

    method fee() {
        return Money->new( amount => 12000 );
    }

    method busy_season_fee() {
        my $money = $self->fee();
        return $money->add(5000);
    }
    __PACKAGE__->meta->make_immutable();
}

package main;

# 追加した繁忙期ロジック

# 通常宿泊料金&繁忙期
my $regular_rate = RegularRates->new();
say $regular_rate->fee->amount();        # 7000
say $regular_rate->busy_season_fee();    # 10000

# プレミアム宿泊料金&繁忙期
my $premium_rate = PremiumRates->new();
say $premium_rate->fee->amount();        # 12000
say $premium_rate->busy_season_fee();    # 17000