sironekotoroの日記

Perl で楽をしたい

「良いコード/悪いコードで学ぶ設計入門」第6章その4 フラグ引数 #ミノ駆動本

リスト6.62

フラグ引数を使っている例

damege(true, damegeAmount);

確かに、これだけでは何が true なのかはコードを追いかけて関数の中身を見ないとわからんよなぁ。

ということで、それを「メソッドを分離する」で改良したところから。

ここクリックして展開

#!/usr/bin/env perl
use strict;
use warnings;
use Function::Parameters;
use feature qw/say/;

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

    has hit_point => (
        metaclass => 'Number',
        is        => 'rw',
        isa       => 'Int',
        default   => 0,
        provides  => {
            sub => 'hit_sub',
        }
    );
    has magic_point => (
        metaclass => 'Number',
        is        => 'rw',
        isa       => 'Int',
        default   => 0,
        provides  => {
            sub => 'magic_sub',
        }
    );
    has state => (
        is       => 'rw',
        isa      => 'Str',
        required => 1,
    );

    __PACKAGE__->meta->make_immutable();
}

package Damege {
    use Mouse::Role;
    requires 'execute';
}

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

    with 'Damege';

    has member => (
        is       => 'ro',
        isa      => 'Member',
        required => 1,
    );

    has damege_amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 0,
    );

    method execute($damege_amount) {

        my $member    = $self->member;
        my $hit_point = $member->hit_sub($damege_amount);

        return $member if ( 0 < $hit_point );

        $member->hit_point(0);
        $member->state('dead');
        return $member;

    }

    __PACKAGE__->meta->make_immutable();
}

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

    with 'Damege';

    has member => (
        is       => 'ro',
        isa      => 'Member',
        required => 1,
    );

    has damege_amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 0,
    );

    method execute($damege_amount) {

        my $member      = $self->member;
        my $magic_point = $member->magic_sub($damege_amount);

        return $member if ( 0 < $magic_point );

        $member->magic_point(0);
        return $member;

    }

    __PACKAGE__->meta->make_immutable();
}

package main;

# メンバークラスのインスタンス
my $member = Member->new( hit_point => 100, state => '' );

# ヒットポイントクラスのインスタンス
my $hit_point_damege = HitPointDamege->new( member => $member );

# ヒットポイントのダメージメソッド
my $dameged_member = $hit_point_damege->execute(10);

# メンバーの現時点のヒットポイント
say $dameged_member->hit_point();    # 90

# メンバーの現時点の状態
say $dameged_member->state();        # 空欄

my $dead_member = $hit_point_damege->execute(100);
say $dead_member->hit_point();       # 0
say $dead_member->state();           # dead

$member = Member->new( hit_point => 100, magic_point => 50, state => '' );
my $magic_point_damege = MagicPointDamege->new( member => $member );
my $use_magic_member   = $magic_point_damege->execute(10);
say $use_magic_member->magic_point;    # 40

my $more_use_magic_member = $magic_point_damege->execute(1000);
say $more_use_magic_member->magic_point;    # 0

リスト6.63 〜 リスト6.66

ここでやっと、ストラテジパターンとはこういうものかな?という引っ掛かりが得られた気がします。

(理解したとは言えない)

関数に渡す引数にオブジェクトを渡すことで、オブジェクトに含まれたメソッドを活用できるんだなぁ、と。

今まではそれを禁忌とでも思って避けていました。

つまり、プリミティブ型執着(第5章)ですね。

Enum 使わずにやってますが、ここのポイントはこれですね。

package Damege {
    use Mouse::Role;
    requires 'execute';

    method apply_damege( $damege_type, $damege_amount ) {

        return $damege_type->execute($damege_amount);
    }
}

引数にオブジェクトを渡す。

渡ってくるオブジュエクトはインターフェイスで必要なメソッドを備えていることは保証されているので、ダックタイピングで処理できる。

if 文で分岐せずとも、引数にオブジェクトを渡した時点で処理のルートが確定する。

頭いいなぁ!すごいなぁ。

そして、昔、これに似たコードをわからないまま触った記憶が蘇ってきました。こういう意味だったのか・・・

あの時に理解したかったなぁ。

ここクリックして展開

#!/usr/bin/env perl
use strict;
use warnings;
use Function::Parameters;
use feature qw/say/;

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

    has hit_point => (
        metaclass => 'Number',
        is        => 'rw',
        isa       => 'Int',
        default   => 0,
        provides  => {
            sub => 'hit_sub',
        }
    );
    has magic_point => (
        metaclass => 'Number',
        is        => 'rw',
        isa       => 'Int',
        default   => 0,
        provides  => {
            sub => 'magic_sub',
        }
    );
    has state => (
        is       => 'rw',
        isa      => 'Str',
        required => 1,
    );

    __PACKAGE__->meta->make_immutable();
}

package Damege {
    use Mouse::Role;
    requires 'execute';

    method apply_damege( $damege_type, $damege_amount ) {

        return $damege_type->execute($damege_amount);

    }

}

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

    with 'Damege';

    has member => (
        is       => 'ro',
        isa      => 'Member',
        required => 1,
    );

    has damege_amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 0,
    );

    method execute($damege_amount) {

        my $member    = $self->member;
        my $hit_point = $member->hit_sub($damege_amount);

        return $member if ( 0 < $hit_point );

        $member->hit_point(0);
        $member->state('dead');
        return $member;

    }

    __PACKAGE__->meta->make_immutable();
}

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

    with 'Damege';

    has member => (
        is       => 'ro',
        isa      => 'Member',
        required => 1,
    );

    has damege_amount => (
        is       => 'ro',
        isa      => 'Int',
        required => 0,
    );

    method execute($damege_amount) {

        my $member      = $self->member;
        my $magic_point = $member->magic_sub($damege_amount);

        return $member if ( 0 < $magic_point );

        $member->magic_point(0);
        return $member;

    }

    __PACKAGE__->meta->make_immutable();
}

package main;

my $member           = Member->new( hit_point => 100, state => '' );
my $hit_point_damege = HitPointDamege->new( member => $member );
my $dameged_member   = Damege->apply_damege( $hit_point_damege, 10 );
say $dameged_member->hit_point();    # 90
say $dameged_member->state();        # 空欄

# クリティカルヒット!
my $dead_member = Damege->apply_damege( $hit_point_damege, 100 );
say $dead_member->hit_point();       # 0
say $dead_member->state();           # dead

# 復活
$member = Member->new( hit_point => 100, magic_point => 50, state => '' );

# マジックポイントにダメージ
my $magic_point_damege = MagicPointDamege->new( member => $member );
my $use_magic_member   = Damege->apply_damege( $magic_point_damege, 10 );
say $use_magic_member->magic_point;    # 40

# マジックポイント枯渇
my $more_use_magic_member = Damege->apply_damege( $magic_point_damege, 1000 );
say $more_use_magic_member->magic_point;    # 0

やっと6章終わった

理解が難しかったのと、Perl で同じような動作をするための環境づくりで結構時間食った気がします。

ただ、得られたものはあったなぁ、と充足感ありあり。

この先の章も楽しみ〜