Featured image of post 第4回-勝手に書き換えられないようにする - Mooで覚えるオブジェクト指向プログラミング

第4回-勝手に書き換えられないようにする - Mooで覚えるオブジェクト指向プログラミング

どこからでも変更できてしまう属性がバグの原因に?Mooのis => 'ro'で読み取り専用の属性を作り、必要なものだけis => 'rw'にすることで、安全で堅牢なクラス設計を学びます。

@nqounetです。

「Mooで覚えるオブジェクト指向プログラミング」シリーズの第4回です。

前回の振り返り

前回は、new(コンストラクタ)を使って複数のオブジェクトを作成する方法を学びました。

Messageクラスから複数のオブジェクトを作り、配列に格納してループ処理しましたね。

今回は、 属性を「勝手に書き換えられない」ようにする方法 を学びます。

どこからでも変更できてしまう問題

これまでのコードでは、has content => (is => 'rw')と書いてきました。rwは「read-write」、つまり読み書き可能という意味です。

1
2
3
4
my $msg = Message->new(content => 'おはよう');
print $msg->content;      # 読み取り: おはよう
$msg->content('こんにちは'); # 書き込み: 値を変更
print $msg->content;      # 読み取り: こんにちは

便利に見えますが、実はこれがバグの温床になることがあります。

例えば、掲示板の投稿内容は、一度投稿したら変更されないはずです。しかし、is => 'rw'だと、プログラムのどこからでも書き換えられてしまいます。

大きなプログラムになると、「誰がこの値を変えたんだ?」と追跡するのが大変です。意図せず値が変わってしまい、バグの原因になることも珍しくありません。

解決策:is => 'ro'で読み取り専用にする

そこで登場するのが、is => 'ro'です。roは「read-only」、つまり読み取り専用という意味です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package Message {
    use Moo;
    has content => (is => 'ro');  # 読み取り専用

    sub show {
        my $self = shift;
        print "投稿: " . $self->content . "\n";
    }
};

my $msg = Message->new(content => 'おはよう');
print $msg->content;  # 読み取り: おはよう(OK)

# $msg->content('こんにちは');  # エラー!書き込みは禁止

is => 'ro'にすると、newでオブジェクトを作るときには値を設定できますが、その後は変更できなくなります。

変更しようとすると、Perlがエラーを出して教えてくれます。これにより、「意図しない変更」を防ぐことができます。

必要なものだけis => 'rw'にする

とはいえ、すべての属性を読み取り専用にすればいいわけではありません。

例えば、投稿に「既読フラグ」を追加したいとしましょう。既読フラグは、後から変更する必要がありますよね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package Message {
    use Moo;
    has content => (is => 'ro');  # 投稿内容は変更不可
    has is_read => (is => 'rw');  # 既読フラグは変更可能

    sub show {
        my $self = shift;
        my $status = $self->is_read ? '[既読]' : '[未読]';
        print "$status 投稿: " . $self->content . "\n";
    }

    sub mark_as_read {
        my $self = shift;
        $self->is_read(1);
    }
};

my $msg = Message->new(content => 'おはよう', is_read => 0);
$msg->show;          # [未読] 投稿: おはよう
$msg->mark_as_read;  # 既読にする
$msg->show;          # [既読] 投稿: おはよう

このように、変更されるべきでない属性はis => 'ro'に、変更が必要な属性だけis => 'rw'にします。

これがオブジェクト指向の大切な考え方の1つ、 カプセル化 の入り口です。データを外部から守り、必要な操作だけを許可することで、バグの少ないプログラムを作れます。

まとめ

  • is => 'ro'は読み取り専用(read-only)の属性を定義する
  • is => 'rw'は読み書き可能(read-write)の属性を定義する
  • 変更されるべきでない属性はroにして、意図しない変更を防ぐ
  • 変更が必要な属性だけrwにすることで、安全なクラス設計ができる

次回予告

次回は、オブジェクト作成時に必ず値を設定させるrequiredオプションを学びます。「うっかり値を渡し忘れた」というバグを防ぐ方法です。お楽しみに。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。