update.はじめにRailsのポリモーフィック(polymorphic、多態性)関連について、実装方法を説明している記事は見かけるのですが、実際これがどんなものでどんな時に便利なのかを説明している記事があまりなく、よくわからないで使っている人もいるようなので本記事を書きました。実装方法そのものについては詳細には説明しませんので、Railsのポリモーフィック関連をどう書くのかを知らない方はまずは公式ドキュメントを読んでいただけると理解が早いかと思います。ボリュームは少ないので、2分もかからないかと思います。'implementation'.)」を実現する方法の一つになります。単一テーブル継承)などでも同じです。......と言われて何のことかわかる人はこの記事を読む必要はないかもしれません。わからない人は安心してください。これから説明していきます!ダックタイピング/インターフェースとはポリモーフィックの話をする前に、まずはダックタイピング/インターフェースについて解説していきます。以下のサンプルコードをご覧ください。"myaa"ダックタイピングの語源は以下の言葉です。duck"「もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである」これを上記サンプルコードに則して表現すると以下のように言えるでしょう。「もしもそれが動物(Animal)のように鳴く(#soundメソッドを持っている)ならば、それは動物である。」このように、「違うものがある決まった振る舞い/入出力を持つことで、同じように扱えるようにすること」をダックタイピングと呼びます。その「ある決まった振る舞い」「入出力の定義」のことをインターフェースと呼びます。コード例に沿って表現すると、DuckとCatは違うクラスですが両方とも#soundというメソッド(インターフェース)を持つので、Animalの中ではどちらのオブジェクトであるかを意識せず#soundを呼ぶことができる、というのがダックタイピングです。ダックタイピングのメリットさて、では上記のコードは何が便利なのでしょうか?ここで注目して欲しいのは、上記コードは以下のようになっていることです。Animalクラスの@animalに入るオブジェクトは、#soundを呼べるオブジェクトである「#soundが存在すること」、「#soundの返り値の形式が同じこと」が守られていれば、引数となるクラスは#barkメソッドの内部実装を知る必要がないし、手を加える必要はないこれらを守らないNG例を書いてみました。@animalの実態ごとにメソッドの呼び方を変えなければならない。そのため、条件分岐が@animalに入りうるクラスの数だけ増える。endendコード内にコメントで書いたように、色々問題が出てきます。先ほどのルールが守られていれば、例えば新しいクラスを追加しなければならない場合にも「既存コードはいじらず」「新しく追加するコード側のインターフェースのみ気を付ければ良い」ようになるので、複雑度を上げないで済みます。インターフェースを踏襲(ダックタイピング)することで、既存コードはいじらずに機能拡張を行える。インターフェースが違う場合はAnimal側も修正が必要になる。"myaa"このように、インタフェースを統一することで冗長なコードがなくなり、スケーラビリティを担保できることがダックタイピングのメリットです。ポリモーフィックの説明、その前にさて、ようやくポリモーフィックの説明です。これもサンプルコードがあった方が解説しやすいので、以下のようなアプリがあったとして説明をさせていただきます。Employeeの2つがある本来ならばこれらをまとめるUserモデルがいるべきかもしれませんが、今回のアプリでなかったとしますメッセージモデル(テーブル)の定義さて、上記のようなメッセージアプリを作る時、メッセージのモデル(テーブル)はどのように作ればいいでしょうか?もしポリモーフィックを使わずにやるならば、以下のようにEmployerMessageモデル(employer_messagesテーブル)とEmployeeMessageモデル(employee_messagesテーブル)を作り、thread_idのような両テーブルに共通するカラムを用意してJOINする、などという方法が考えられます。:employeeend要件によってはこれもありなのかもしれませんただ、EmployerMessageとEmployeeMessageが同じインターフェースを持つのならば、この方法では同じコードを繰り返し書くことになるのであまり好ましくありません。メッセージ送信者モデルが増えれば増えるほどコードの重複も増え、複雑度もそれに応じて高くなることが予想されます。メッセージモデルは一つにまとめ、それを各メッセージ送信者モデルで共有したくなります。その場合でポリモーフィックを使わないならば、コードは以下のようになるかと思います。:messagesendmessagesテーブルにはemployer_idカラムとemployee_idカラムがあり、それを使って各モデルとJOINすることになります。実際のコード内での呼び出し方は以下のようになるでしょう。?ただ、この書き方は上記ダックタイピングNG例と同じような問題をはらんでおり、message.employerとmessage.employeeのどちらが呼べるのかは都度チェックする処理を挟まないとメッセージ送信者モデルを取得できません。TABLEを行う必要がある」といった問題もプラスされています。いった時にダックタイピングを使って簡潔に書けるようにするもの、それがポリモーフィックですポリモーフィックの使いどころ/便利なところ上記のまとめになりますが、Railsのポリモーフィックが便利なのは「一つのモデルを同じインターフェースを持ったものが扱う(ダックタイピングする)」場合になります。実際にポリモーフィックを利用したコードは以下のようになります。first_nameは各テーブルに定義されたカラムだと思ってください。endendこのコードは以下のように使うことができます。messageableを通すことで、EmployerとEmployeeのどちらであるかを意識する必要がない。messageableなモデルのインターフェースとして必ず`#sender_name`が定義されているので、メソッドが定義されているかどうかのチェックも必要ない。メッセージ送信者モデル側からは通常のhas_many関連として扱えるEmployer.find(params[:id]).messagesEmployee.find(params[:id]).messagesダックタイピングを使うことによって、メッセージ送信者モデル(messageable)の実際のクラスを意識せずに#sender_nameにアクセスすることができました。messageableなモデルを追加する時にも以下のように追加モデルを書くだけでよく、既存コードをいじる必要はありません。以上でポリモーフィックの概念レベルの説明は終わりとなりますが、ついでによくあるポリモーフィックの書き方も書いておきます。endend間違ったポリモーフィックダックタイピングNG例とかぶりますが、これまでを踏まえて「こういうポリモーフィックは間違っている」ということを説明します。以下のようなコードがあるとします。インターフェースが統一されていないと、クラスで条件分岐する必要がある。messageable.employee_emailendこのようにmessageableの実態・インターフェースを逐一調べるようになってしまうとポリモーフィックの意味がないどころか、抽象化レイヤが入る分複雑度が増し、非常に読みづらくテストが書きづらくバグりやすいコードになってしまいます。ポリモーフィックを使う場合は必ずインターフェースを統一し、すべてのクラスを同じように扱えるようにしなければなりません。以下、とりとめのない話です個人的にはポリモーフィック自体は非常に便利だけどもRubyのシンタックス的に厳しいものがあるなー、と思っています。endend個人的にはmoduleのメソッドのオーバーライドを明示的にするため、includeする際にブロックを使ってその中でオーバーライドさせたいのですが、残念ながらできません。endendなので、以下のようにincludeとオーバーライドは分ける必要があるのですが、コード量が多くなってくるとどれがオーバーライド用のメソッドでどれがそれ以外のメソッドなのかわかりにくくなるので、何か一工夫が欲しくなります。}endこのようなRubyのシンタックス上の限界からRailsのポリモーフィック関連は実装者の実力に依存しがちな構文に思えるので、利用する際には細心の注意を払う必要があるでしょう。upLoginユーザーは見つかりませんでした。