プログラミング初心者の勉強ブログ #86
「オブジェクト指向設計」について理解を深めていくシリーズ。「オブジェクト指向設計実践ガイド」を読んでの備忘録になります。今回は「モジュール」についてです。継承が「縦のつながり」とすれば、モジュールは「横のつながり」を形成します。ダックタイプが「シグネチャ」のみの横のつながりであれば、モジュールは「メソッド」を含んだ横のつながりです。
目次
[toc]
前回までの内容
[blogcard url=”https://whatsupguys.net/programming-school-dive-into-code-learning-85/”]
[blogcard url=”https://whatsupguys.net/programming-school-dive-into-code-learng-81/”]
[blogcard url=”https://whatsupguys.net/programming-school-dive-into-code-learning-80/”]
[blogcard url=”https://whatsupguys.net/programming-school-dive-into-code-learning-78/”]
[blogcard url=”https://whatsupguys.net/programming-school-dive-into-code-learning-77/”]
ロールを共有するモジュールとは
僕が最初にクラスとモジュールの違いをGoogleで調べ確認した際、「クラスはインスタンス生成が可能で、モジュールはインスタンス生成ができない」という違いで判断し、理解をしておりました。
Rubyにおけるモジュールができることは大きく分け2つ存在し、
- クラス内でincludeまたはextendでモジュールを指定し、メソッドを拡張する「Mix-in」としての機能
- 関連のあるクラスやモジュールをまとめる、「名前空間」としての機能
そのため、モジュール内にはメソッドやクラスを定義することになります。Railsで言えば、「独自ヘルパーメソッド」がモジュールに該当し、helpersディレクトリ内の特定のrbファイルは
module BlogsHelper def method01 #省略 end def method02 #省略 end end
このように 「module BlogsHelper」というモジュールにメソッドを作成すればビューで使用でき、コントローラーで使用する際は「include」を書き込むことで、そのコントローラー内のインスタンスメソッドとして活用できます。継承による親子関係でないクラス同士に共通するメソッドをモジュールに定義し、それぞれのクラスでそのモジュールを反映させメソッドを持たせることを「ロールの振る舞いを共有する」と言います。ロールの振る舞いを共有するために、モジュールを利用します。
今回はMix-inの機能を含め、本を読んで学んだ「ロールの振る舞いを共有するモジュール」についての理解をまとめていきます。
※名前空間としての機能は「ロールの振る舞いを共有するモジュール」としての機能から少し外れるので割愛します。参考にしたQiitaのリンク(RubyのModuleの使い方とはいったい – Qiita)より確認ください。
モジュールの「Mix-in」
Mix-inは「include」と「extend」を使い、ロールの振る舞いを共有することを指します。前提としてMix-inを行うモジュールの中には「メソッド」が定義されます。
「継承」は、サブクラスとスーパークラスによる「親子関係」で「縦の階層構造」を構成して、抽象的でより一般的な振る舞いを上に持っていき、具体的で特殊な振る舞いを下に位置付けることでメソッドの共有が行われていましたが、一方で「モジュール」は「横のつながり」をクラス同士に持たせるイメージです。コードのDRYを減らせる一方で、複雑さが増す可能性を孕むことになります。
include
クラス内にincludeでモジュールを指定した場合、指定したモジュール内のメソッドはそのクラスで生成されたインスタンスから呼び出すことのできる「インスタンスメソッド」として働きます。クラスから直接呼び出せないので、一旦インスタンス化してからモジュール内のメソッドが利用できるようになります。
module BlogsHelper def hoge p 'hoge' end end class BlogsController include BlogsHelper def index end # 省略 end // クラスから直接呼び出せない BlogsController.hoge # => NoMethodError: undefined method `hoge' for BlogsController:Class // 生成したインスタンスから呼び出せる blog = BlogsController.new blog.hoge # => "hoge"
extend
クラス内にextendでモジュールを指定した場合、指定したモジュール内のメソッドはそのクラスから呼び出すことのできる「クラスメソッド」として働きます。クラス内で「def self.xxxxxx」と定義した時と同様に、クラスに直接モジュール内のメソッドを当てて呼び出すことが可能です。
module BlogsHelper def hoge p 'hoge' end end class BlogsController extend BlogsHelper def index end # 省略 end // クラスから直接呼び出せる BlogsController.hoge # => "hoge" // 生成したインスタンスからは呼び出せない blog = BlogsController.new blog.hoge # => NoMethodError: undefined method `hoge' for #<BlogsController:xxxxxxxxxxxxxx>
ダックタイピングとモジュール
以前、【Ruby】オブジェクト指向設計を噛み砕いていく(#4 ダックタイピング)にて「ダックタイピング」について学習しました。ダックタイプは、オブジェクト間のインターフェースに着目し、不必要な依存を減らすことを目的として、多岐にわたるオブジェクトが同じメッセージを利用できるようにする技法です。ダックタイプもモジュールのMix-inと同様、親子関係が特にないオブジェクト同士につながりを持たせております。
何が違うかということになりますが、ダックタイプによって連携されるメッセージは「シグネチャ」のみです。あくまでインターフェース上のみの共有です。
シグネチャとは日本語にすると「署名」などの意味ですが、一般的なプログラミング言語(特にオブジェクト指向言語)におけるシグネチャも正にそういう意味で、唯一無二であることを示すためのもの(=メソッドの署名)です。1つのメソッドは基本的に1つのシグネチャを持っています。このようにシグネチャを持つことで、それぞれのメソッドがコンパイラなどで明確に区別できるわけです。
より複雑な振る舞い(メソッド)を共有する場合、モジュールを使うことになります。
まとめ
オブジェクトは「継承」による縦のつながりと、「モジュール」による横のつながりで複雑な階層構造を構成しつつメッセージのやりとりを行います。継承とモジュールをうまく活用することでコードの再利用性が高まる一方、階層構造は深く広くなればなるほど複雑さが増します。要は「プログラマーの手腕が問われるよ」ということであり、本読んでいるとオブジェクトのマネジメント的な考え方であったり、コーディング自体のUXの考え方であったり、とにかく考えてコード組めよ感を要所要所に感じます。あんまり難しく考えるべきではないのかもしれない。色々考えても今の技量じゃ大した設計はできないですし。これからのコーディングに対する意識の持ち方次第なんだなと。
以上ありがとうございました。