@nqounetです。
「Mooで覚えるオブジェクト指向プログラミング」シリーズの第10回です。
前回の振り返り
前回は、子クラスで同じ名前のメソッドを定義して親クラスのメソッドを上書きする オーバーライド を学びました。
if文でクラスの種類を判定して分岐するのではなく、各クラスが自分のformatメソッドを持つ設計にすることで、コードがスッキリしましたね。
今回は、継承とは別の方法で振る舞いを共有する ロール を学びます。
問題:継承関係にないクラスで同じ機能が必要
掲示板アプリに「作成日時を記録する」機能を追加したくなりました。MessageとUserの両方にcreated_at属性を持たせたいとします。
しかし、ここで困った問題があります。
- MessageとUserは継承関係にない
- Userは投稿ではないので、Messageの子クラスにするのはおかしい
- 逆に、MessageがUserを継承するのもおかしい
- かといって、両方のクラスに同じコードを書くのはコードの重複になる
継承は「AはBの一種である」(is-a関係)のときに使うべきです。UserはMessageの一種ではありません。無理に継承関係を作ると、設計が歪んでしまいます。
このような「継承関係にないが、同じ振る舞いを持たせたい」ケースで活躍するのが ロール です。
解決策:Moo::Roleでロールを定義
ロールとは、クラスに「合成(compose)」できる振る舞いの集まりです。継承ではなく、機能の断片を複数のクラスで共有する仕組みです。
PerlではMoo::Roleを使ってロールを定義します。
| |
use Mooではなくuse Moo::Roleと書くのがポイントです。
このTimestampableロールは、created_at属性とcreated_dateメソッドを提供します。しかし、ロール単体ではオブジェクトを作ることはできません。クラスに合成して初めて使えるようになります。
解決策:withでロールを適用
ロールをクラスに適用するには、withを使います。
| |
with 'Timestampable'と書くだけで、UserクラスにもMessageクラスにもcreated_at属性とcreated_dateメソッドが追加されます。
- UserはMessageを継承していない
- MessageもUserを継承していない
- しかし、両方とも「作成日時を持つ」という振る舞いを共有している
これがロールの力です。継承が「AはBの一種である」(is-a)なら、ロールは「Aは〜できる」「Aは〜を持つ」(has-a、can-do)という関係を表現します。
まとめ
Moo::Roleでロールを定義できるwith 'RoleName'でロールをクラスに適用できる- ロールは継承関係にないクラスで振る舞いを共有するのに使う
- 継承は「AはBの一種である」、ロールは「Aは〜できる」という関係を表す
次回予告
次回は、属性として持っているオブジェクトに仕事を任せる 委譲(delegation) を学びます。クラスが肥大化してきたとき、handlesオプションで責務を分離する方法を紹介します。お楽しみに。
