Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Ruby on Rails でポリモーフィズムをマスターする:初心者にもわかる実践ガイド

オブジェクト指向プログラミング(OOP)でアプリケーションを開発したことがあるなら、ポリモーフィズムを実際に使ったことがあるか、少なくともその用語は耳にしたことがあるでしょう。

科学やコンピュータサイエンスの教科書に登場しそうな言葉です。調べてみたことはあっても、概念をきちんと理解しないまま実装してしまった、という経験がある方もいるかもしれません。

この記事では、特にRuby on Railsにおけるポリモーフィズムへの理解を深めていきます。取り上げるトピックは以下の通りです。

  • 現実世界におけるポリモーフィズム
  • OOPにおけるプログラミングのポリモーフィズム
  • Railsアプリケーションに組み込んで、高品質なコードを維持する方法

それでは始めましょう!

現実世界におけるポリモーフィズム

ポリモーフィズムは文脈によっていくつかの定義がありますが、どの場面でも使える便利な定義は「1つのオブジェクトが複数の形態を取れる能力」です。実際、単語を分解すると、polyは「多数の」、morphは「形態」を意味します。

現実世界での身近な例を挙げると、一人の女性が警察官であり、姉や妹でもあり、誰かの子どもであり、母親でもある、といったケースです。それぞれの役割が彼女の振る舞いを決定し、その人自身を形作っています。

ポリモーフィズムと遺伝学

コンピュータプログラミングの世界以外では、ポリモーフィズムという用語は生物学や遺伝学と結び付けられることが多いです。この文脈では、「同一種の中に複数の異なる形態や型の個体が存在するような遺伝的変異」と、より具体的に定義されます。

ジャガーを例に考えてみましょう。ジャガーには複数の遺伝子変異があり、それが毛皮の色に影響を与えます。多くのジャガーは黒い斑点のある黄褐色ですが、遺伝子の変化によって斑点が薄かったり濃かったりし、中には黒い毛皮を持つ個体もいます。

同じ種の鳥における色の違いもポリモーフィズムの一例です。ゴウカンチョウ(Gouldian finch)は、個体ごとの色彩に明確な違いがあることで知られています。

モノモーフィズムとポリモーフィズムの違い

モノモーフィズムに目を向けると、ポリモーフィズムをさらに深く理解できます。生物学の観点では、モノモーフィズムは「1つの形態しか持たない種」として定義でき、発育のさまざまな段階を通じて同じ形態を保ちます。

ペンギンはモノモーフィックな生物です。専門家でさえ雌雄を見分けるのが難しいほどです。ペンギンの遺伝的な差異はごくわずかなため、体型や白黒の体色といった外見上の特徴はほとんど区別がつきません。

モノモーフィックな種では、行動の違いこそが雌雄を見分ける最も手軽な手がかりになることが多いです。

それでは、プログラミングにおけるポリモーフィズムに話を進めましょう。

OOPにおけるポリモーフィズム

冒頭の定義——「オブジェクトが複数の形態を取れる能力」——を思い出すと、これがOOPと自然につながることがわかります。

OOPでは、同じメソッドに異なるオブジェクトを渡すことで、別々の結果を得られます。条件分岐を使っても同様のことは実現できますが、コードが肥大化し、DRY原則から外れてしまう恐れがあります。クリーンで論理的なOOPアプリケーションを構築するには、ポリモーフィズムが欠かせません。

RubyのようなOOP言語でポリモーフィズムを実装する2つの方法——継承とダックタイピング——を見ていきましょう。

Rubyにおける継承とポリモーフィズム

継承とは、子クラスが親クラスの性質を受け継ぐ仕組みです。

以下は、継承を使ってポリモーフィズムを実装する例です:


上記のコードには、親クラスInstrumentを継承した2つの子クラス——StringedPercussion——があります。メソッドinstrument_exampleを呼び出すだけで、SaxophoneGuitarDrumsという複数の出力が得られるため、この例はポリモーフィックだと言えます。

継承によるポリモーフィズムの実現は、本質的にはメソッドのオーバーライドですが、OOP言語におけるポリモーフィズムの概念を明確に理解する助けになります。

Rubyにおけるダックタイピングとポリモーフィズム

OOPにおけるより実践的なポリモーフィズムの例が、ダックタイピングです。


各クラスのメソッドはすべてbrandという名前ですが、(継承を使った場合と異なり)メソッドをオーバーライドしているわけではありません。親クラスを継承する代わりに、ここには4つの独立したクラスがあり、それぞれが独自のメソッドを持っています。ダックタイピングが便利なのは、各メソッドを個別に呼び出す必要がなく、クラスを反復処理するだけで出力を取得できる点です。

この場合も、メソッドbrandを呼び出すだけで、GibsonPearlFenderCasioという複数の形態の出力が得られるため、ポリモーフィックです。もちろん、この結果を得るだけならダックタイピングやポリモーフィズムは必須ではありません。しかし、クリーンで論理的なコードを書くうえで非常に有用です。

Ruby on Railsにおけるポリモーフィズム

ポリモーフィズムは、Active Recordのアソシエーション(関連付け)としてRuby on Railsと相性抜群です。複数のモデルが本質的に同じことをしているなら、それらを1つのモデルに統合し、ポリモーフィックな関係を作れます。

引き続き楽器をテーマに、ユーザーが楽器を投稿したり、コメントしたり、レビューしたりできるアプリケーションを考えてみましょう。まずは以下のER図(ERD)をご覧ください:

Ruby on Rails でポリモーフィズムをマスターする:初心者にもわかる実践ガイド

この例では、ユーザーが楽器の詳細情報とともに投稿できるアプリケーションのERDを示しています。ユーザーは投稿された楽器に対してコメントすることもできます。

さらに、他のユーザーはその楽器を評価したり、コメントの有用性や妥当性を判断するためにコメントを評価したりできます。これらのActive Recordの関連付けは問題なく機能し、アプリケーションの目的を十分に果たしています。

では、ここに新しい関連付けを追加したい場合はどうでしょうか? 重複した関連付けを何度も追加する必要が出てきます。

たとえば、ユーザーの信頼性を評価するuser_ratingモデルを追加したい場合、独自の関連付けを持つ別テーブルを作成しなければなりません。つまり、userモデルとuser_ratingモデルの間に新しいリレーションを追加することになります。ERDは次のようになります:

Ruby on Rails でポリモーフィズムをマスターする:初心者にもわかる実践ガイド

こうして、本質的には同じこと——異なる文脈で対象を評価すること——を行う3つのモデルが並ぶことになりました。このような状況こそ、ポリモーフィックな関連付けの出番です。

評価モデルをポリモーフィック化したERDを見てみましょう:

Ruby on Rails でポリモーフィズムをマスターする:初心者にもわかる実践ガイド

ratingカラムとの混同を避けるため、モデル名はratingsではなくreviewsとしました。ここで、レビューされる側のモデルは他のモデルとの関連付けを保ったまま、個別だった評価モデルが1つのレビューモデルへと統合されています。

reviewable_typereviewable_idが、レビューがどのモデルに紐付いているのかを表すことで、従来の個別評価モデルと同じ役割を担います。

reviewable_typeカラムにはモデルのクラス名(userinstrument_post、またはcomment)が保存され、reviewable_idには対応するモデルのIDが保存されます。

この2つのカラムを利用すれば、Active Recordクエリや条件分岐を通じて、評価値を特定のユーザー、投稿、コメントに紐付けられるようになります。なお、外部キーuser_idはレビューモデルに残しておきます。これにより、どのユーザーがレビューを残したのかを追跡できるからです。

現時点では、ポリモーフィックモデルの「-able」という接尾辞は少し不思議に感じるかもしれませんが、後ほどRailsのマジックを紹介する際に、その目的が明らかになります。

このように、1つのモデル(オブジェクト)が複数の形態——ユーザー、コメント、楽器投稿へのレビュー——を表現できるようになったため、レビューモデルはポリモーフィックであると言えます。

Ruby on Railsでポリモーフィズムを実装する

いよいよ、Railsアプリケーションでポリモーフィズムを実装してみましょう!userinstrument_postcommentモデルはすでに存在するものとして、ポリモーフィックなreviewsモデルの導入に取り掛かります。

まず、ターミナルでテーブルを作成し、モデルを生成します:


これにより、次のマイグレーションファイルが生成されます:


マイグレーション実行後、schema.rbファイルが更新されます。polymorphicオプションを指定すると、reviewableカラムがreviewable_typereviewable_idの2つのカラムへと変換されます:



先ほど触れた「-able」という接尾辞の意味が、ここでわかりますね。これはポリモーフィックな関連付けを指定するためのRailsの命名規則で、ユーザーや楽器投稿、コメントを「reviewable(レビュー可能)」にするためのものです。

このRailsのマジックを機能させるには、他のモデルがポリモーフィックモデルと正しく関連付けられている必要があります。




これで、userinstrument_postcommentの各モデルに対してレビューと評価を作成できるようになりました。

すでに2人以上のユーザー、コメント、楽器投稿が作成済みであれば、Active Recordクエリを使って、次のようにさまざまな方法でレビューを作成・参照できます:





表示したいデータに応じて、reviewsモデルとやり取りする方法は他にもたくさんあります。ポイントは、親モデル側からレビューを作成する際の扱いです。レビューがユーザーとreviewableモデルの両方に関連付けられている限り、Active Recordが自動的にreviewable_idreviewable_typeを該当モデルに紐付けてくれます。

もしポリモーフィズムを使わなければ、Railsアプリケーションにはより多くのテーブル、不要な重複カラム、そして大量のbelongs_tohas_manyの関連付けが必要になっていたでしょう。ポリモーフィズムのおかげでテーブル結合の必要性が減り、Active Recordクエリや関連付けがより簡単かつ高速に行えるようになったのです。

まとめ:クリーンで論理的なRubyコードのためにポリモーフィズムを活用しよう

この記事では、生物学とRubyプログラミングという2つの異なる領域におけるポリモーフィズムを探ってきました。いずれの世界でも、ポリモーフィズムとは「オブジェクトが複数の形態を取れる能力」を指します。

Rubyにおける継承とダックタイピングを使ったポリモーフィズムの実装方法を確認した後、Ruby on Rails特有の活用方法について詳しく掘り下げました。

ポリモーフィズムは、クリーンで論理的なコードを書くための強力な武器になります。この重要なOOPの概念をぜひツールボックスに加えて、現在進行中のプロジェクトはもちろん、将来のアプリケーション開発にも活かしてください。

それでは、Happy coding!

追伸:Ruby Magicの最新記事をいち早く読みたい方は、Ruby Magicニュースレターを購読すれば、記事を見逃すことはありません!

  1. AppSignalが振り返る2024年のRubyハイライト:必読記事トップ5

    今年も残りわずかとなりました。そこでAppSignalでは、2024年に最も読まれたRuby記事トップ5を、逆順でご紹介します。 Ruby on Rails 7.2の新機能:Active Recordのデータベース関連機能 Ruby on Rails 7.2で導入される注目すべき変更点、特にActive Record周りの改善について詳しく解説した記事です。 StimulusとTurbo Framesで実現するRailsのHotwireモーダル このシリーズの第1回では、Railsアプリケーションにアクセシビリティに配慮したモーダルを実装するための、2つのHotwireアプローチを取り上げてい

  2. Ruby MRIのソースコードを読んで内部構造を学ぶ方法

    Rubyを使い込んでいると、その裏側でどのように動作しているのか気になったことはありませんか? Rubyの内部に深く踏み込む方法の一つが、それを実現しているソースコードを読むことです。C言語がわからなくても、興味深い発見はたくさんあります。 ソースコードはRubyのGitHubリポジトリで公開されています。 理想を言えば、クラス名やメソッド名を簡単に検索できるCodequeryのようなツールを使うのがおすすめです。 コアクラスを探検する 調査のほとんどはルートフォルダで行うことになります。ここには、object.cにあるObjectや、array.cにあるArrayなど、すべてのコアクラスのソ