=encoding utf8 =pod =head1 題名 Moose::Manual::BestPractices - Mooseを最大限に活用する =head1 推薦の言葉 Mooseにはさまざまな機能がありますし、使い方も決して一通りではありません。でも、使う機能は一部にしぼって、いつもそれを使うようにした方が、みなさんのためになると思います。 もちろんどんな「ベストプラクティス」集でもそうですが、これも本当にただの意見にすぎませんので、無視していただいても結構です。 =head2 Cと不変化 Mooseのクラス定義の最後にはMooseのシュガー関数を削除してクラスを不変化することをおすすめします。 package Person; use Moose; # extends, roles, attributes, etc. # methods no Moose; __PACKAGE__->meta->make_immutable; 1; Cの部分はMooseがエクスポートするキーワードを全て取り払う、単にコードをきれいにするためのものです。クラスを定義した後にこれらのキーワードは必要ありませんので、削除しておくほうがよいでしょう。 CはMooseがオブジェクト生成をはじめとした色々な動作を高速化にする処理を適用することを可能にします。ただいこの代償としてそのクラスを以後変更することはできません。 また、クラスを不変化すると、いろいろなものが高速化します(もっとも顕著なのはオブジェクトの生成です)。 Lを含め、それ以外のキーワードのエクスポート除去も行うさらに一般的な手法としてはLやLがあります。 =head2 Cはオーバーライドしないこと Cをオーバーライドするのは非常に悪いことです。同じことをしたいのであればCメソッドやCメソッドを使ってください。Cをオーバーライドすると、クラスを不変化したときにコンストラクタをインライン展開できなくなってしまいます。 ただし、Cをオーバーライドしてもよい場合が2つあります。ひとつは、自前でLのサブクラス「と」Lを用意してコンストラクタをインライン展開するようなMooseX拡張モジュールを書いている場合。2つめはMooseを使っていない親クラスをサブクラス化する場合です。 やり方を知っている人なら、このベストプラクティスを無視してよいときもご存じですよね ;) =head2 かならずCを呼ぶこと 自分のクラスでCメソッドをオーバーライドする場合はかならず、お行儀よくCを呼んで、自分では明示的にチェックしていないケースを処理するようにしてください。 Lが提供しているデフォルトのCメソッドは名前付きパラメータのリストとハッシュリファレンスをどちらも正しく処理できます。また、「ハッシュリファレンス以外の」引数がひとつだけの場合もチェックしてくれます。 =head2 できればかならずデフォルトを用意すること、そうでなければCを使うこと クラスにデフォルト値が用意されていると新しいオブジェクトを生成するのが簡単になります。デフォルトを用意できない場合はアトリビュートをCにすることを検討してください。 どちらもしないと、アトリビュートが単にセットされていないだけという状態になることがあるため、オブジェクトがより複雑なものになってしまいます(みなさんや、みなさんのクラスのユーザが考慮しなければならない状態の可能性が増えてしまうためです)。 =head2 よほどのことがなければCのかわりにCを使うこと ビルダーは継承可能ですし、明示的な名前がついています。とにかくこちらの方が明らかにきれいなのです。 ただし、デフォルトがリファレンスでない場合、「あるいは」デフォルトが何らかの空のリファレンスでしかない場合は、「ぜひ」デフォルトを使ってください。 また、ビルダーメソッドはプライベートにしておいてください。 =head2 Cを使うこと 遅延評価は便利ですし、初期化の順番の問題を解決してくれることも多いものです。また、まったくする必要がないかもしれない作業を遅らせるときにも使えます。遅延評価をさせたい場合は、Cを使うと、タイプ数を節約して、名前も標準化できるようになります。 =head2 クリア用のメソッドや断定用のメソッドはプライベートにすることを検討すること 「本当に」だれでもアトリビュートをクリアできるようになっている必要はあるでしょうか。おそらくないはずです。この機能をデフォルトでクラスの外から見えるようにするのはやめましょう。 断定用のメソッドの方はそれほど問題ではありませんが、わざわざ必要以上に公開APIを大きくする理由はありません。 =head2 デフォルトは読み取り専用にして、書き込み用のアクセサはプライベートにすることを検討すること アトリビュートを可変化すると、単純にプログラムの中で考慮しなければならない複雑さが増してしまいます。状態を可変化するかわりに、クラスのユーザには必要があれば新しいオブジェクトを作るようすすめてください。 どうしてもアトリビュートを読み書き可能に「しなければならない」場合は、書き込み用のアクセサを別のプライベートメソッドにしてしまうことを検討してください。APIの幅を狭めた方がメンテナンスがしやすくなりますし、状態が可変になっているとトラブルのもとです。 そのようなアトリビュートを定義する場合は、プライベートと分かる関数名をCに渡します: has pizza => ( is => 'ro', isa => 'Pizza', writer => '_pizza', ); =head2 サブクラスでアトリビュートの型を変える前にもう一度考え直すこと その道の先にあるのは大混乱です。アトリビュート自身がオブジェクトの場合、少なくとも親クラスのオブジェクトの型と同じインタフェースを持っているかどうかは確認しましょう。 =head2 C機能は使わないこと 何を言っているのかわからない? 大丈夫です。 =head2 CのかわりにLトレートを使うこと Cはいささかやっかいな機能ですし、複雑なアトリビュートを直接外に見せるのは美しくありません。かわりにLを使って、そうする必要がある機能のみを外に見せるようなAPIを定義することを検討してください。そうすれば、ほしい機能のみを外に見せることができるようになります。 =head2 もっとも具体的なサブクラスであってもかならずCを呼ぶこと CとCを使う場合は、階層内でもっとも具体的なサブクラスの中でもCを呼ぶことをおすすめします。こうしておくと、親クラスを変更しなくてもさらにサブクラス化を進めて階層を拡張することができるようになります。 =head2 型には名前空間を付けること 型の名前には何らかの命名規則を適用してください。「MyApp::Type::Foo」のようなものがおすすめです。 あとから再利用できるようにLを使って型をパッケージングするつもりがあるなら、空白やピリオドのようにPerlの識別名としては使えない文字は使わないようにしてください。 =head2 Mooseの組み込み型は直接型変換しないこと CのようなMooseの組み込み型に型変換を定義すると、この型を利用しているPerlインタプリタ上で動作しているすべてのアプリケーションに影響を及ぼします。 # very naughty! coerce 'ArrayRef' => from Str => via { [ split /,/ ] }; だから、そのかわりにサブタイプを作って、そちらを型変換してください。 subtype 'My::ArrayRef' => as 'ArrayRef'; coerce 'My::ArrayRef' => from 'Str' => via { [ split /,/ ] }; =head2 クラス名を直接型変換しないこと Mooseの組み込み型の場合とまったく同じで、クラス型もインタプリタ全体に影響を及ぼすグローバルなものなので、クラス名に型変換を追加してしまうと、よそで不思議な副作用を引き起こすことがあります。 # also very naughty! coerce 'HTTP::Headers' => from 'HashRef' => via { HTTP::Headers->new( %{$_} ) }; そうするかわりに、型変換用に「空の」サブタイプを作成できます。 subtype 'My::HTTP::Headers' => as class_type('HTTP::Headers'); coerce 'My::HTTP::Headers' => from 'HashRef' => via { HTTP::Headers->new( %{$_} ) }; =head2 型結合のかわりに型変換を使うこと 型結合のかわりに型変換を使うことを検討してください。これについてはLで詳しく取り上げています。 =head2 型の定義はすべてひとつのモジュールにまとめること 型や型変換はすべてひとつのモジュールの中で定義してください。これもLで説明しました。 =head1 ベストプラクティスの効用 このようなベストプラクティスにしたがうと、さまざまな利益が得られます。 ほかのコードとの相性が確実によくなるよう手助けをしてくれるので、コードの再利用性が高まり、拡張しやすくなります。 定評のある慣用句を使うことでメンテナンスが簡単になりますし(ほかの人にメンテナンスしてもらわなければならなくなったときは特にそうです)、コードをすぐに理解してもらいやすくなるので、ほかのMooseユーザからのサポートも得やすくなります。 また、中にはMooseが適切な処理をするのを支援するためのものもあります(特に不変化についての話がそうです。不変化すると、コードが速くなります)。 メタプログラミングを最大限に活用する手助けをするものもたくさんあります。本当の型変換を定義するかわりにCをオーバーライドして手作業で型変換していては、イントロスペクション可能なメタデータは得られません。MooseXの拡張モジュールがイントロスペクションを頼りに適切な処理をしている場合は特にこのようなことが問題になります。 =head1 作者 Yuval (nothingmuch) Kogman Dave Rolsky Eautarch@urth.orgE =head1 COPYRIGHT AND LICENSE Copyright 2009 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut