オブジェクト編その4-例外処理

※この記事を書いているのは未熟なプログラマです。もし「こんな内容の記事書くんじゃない。未熟者めが!」というご意見があったらありがたく頂戴します。よろしくお願いします。

ところで書店に出回っている本にはある共通点があります。

  • 書籍名が記載されている。
  • 著者名が記載されている。
  • 価格が記載されている。

他にもISBNや版型などいろいろな決まりごとがあります。これは我々が準備するクラスもそうであるべきです。
もし書籍名が記述されていない場合は「例外処理を行ったので処理を中断する」ようにしましょう。

C:\work\lib\Book.pmを以下のように修正して下さい。

#!C:\Perl\bin\perl
package Book;
use strict;
use warnings;
use Carp;
use Data::Dumper;

sub new {
    my ( $class, $args ) = @_;
    
    my $name        = $args->{name};
    my $authors_ref = $args->{authors};
    my $price       = $args->{price};
    
    croak("書籍名が指定されていません。") unless $name;
    croak("著者が指定されていません。  ") unless $authors_ref;
    croak("価格が指定されていません。  ") unless $price;
    
    my $self = bless {
    	_name    => $name,
    	_authors => _to_arrayref( $authors_ref ),
    	_price   => $price,
    }, $class;

    return $self;
}

sub name   {
    my $self = shift;
    $self->{_name} = shift if @_;
    $self->{_name};
}

sub authors { 
    my $self = shift;
    my $authors_ref = shift;
    $self->{_authors} = _to_arrayref( $authors_ref ) if $authors_ref;
    return @{ $self->{_authors} };
}

sub price  { $_[0]->{_price}  }
sub formatted_price { _addcomma( $_[0]->price ) }

sub speak {
    print 'hi!';
}

sub _addcomma {
    my $number = shift;

    if($number =~ /^[-+]?\d\d\d\d+/g) {
	my ($i, $j);
	for($i = pos($number) - 3,
	    $j = $number =~ /^[-+]/;
	    $i > $j; $i -= 3) {
	    	substr($number, $i, 0) = ',';
		}
    }
    return $number;
}

sub _to_arrayref {
    my ($value) = @_;

    return [] if !defined $value;
    return $value if ref $value eq 'ARRAY';
    return [$value];
}

1;

C:\work\example\oop_004.pl を以下のように記述して下さい。

#!C:\Perl\bin\perl
use strict;
use warnings;
use lib 'lib';
use Book;

my $args = {
#    name    => '初めてのPerl',
    authors => [ 'ランダル・L. シュワルツ', 'トム クリスチャンセン' ],
    price   => 3780,
};


my $book  = Book->new( $args );

print $book->name, "\n";
print $book->authors, "\n";
print $book->formatted_price, "\n";

実行結果を確認します。

例外が送出されています。どうやら14行目はクラスの設計者が意図した使い方をしていないようです。
つまりこのBookクラスは書籍名が必須項目のようです。

書店に出回っている本に例外なく書籍名やISBNが指定されている本が出回っているのはおそらくそのシステムでも同様に書籍名が欠けている本は例外だからでしょう。

こうしてBookクラスを使って間違って書籍名や著者名のない本を創る人がいなくなりました。
めでたし。

※簡略化してあるスクリプトなので著者名に無名配列とか入れられると困りますが・・

ちなみにここでcroakを使ってdieを使ってない理由ですが、ここでの例外の内容は「このBookクラスの使い方はそうじゃない!!ここ間違えてるよ!!」という認識をしてほしいので、使い方を間違えているoop_004.plの14行目がエラーになっているわけです。

dieだとこんな感じです。

とりあずえクラスの場合はcroakにしておけば大丈夫と覚えておけば大丈夫です。

テレビのリモコンが反応しなくなった原因を「赤外線を送出するための電力がありません」と説明されるより「電池が入っていません」って言ってもらったほうがいいでしょ?



お疲れさまでした。
設計したクラスを意図したとおりに使ってもらうために例外処理を組み込みました。

次回は継承を解説したいと思います。