Rubyと一緒に学ぶ、オブジェクト指向

はじめまして、新執行として入った澤井です。
今回は現在のプログラミングにおける考え方の基礎になっているオブジェクト指向について、プログラミング言語Rubyを使いながら書きたいと思います。

今回は簡単にオブジェクトとメソッドのお話のみをするので、クラスやインスタンスのお話はしません。
オブジェクト指向について調べたけどよくわからなかったという人でも、Ruby知らないって人でも理解できるように書いたつもりなので読んでいただけると嬉しいです。
想定している読者は「最近プログラム書けるようになった!」くらいの人です。


オブジェクトとは

オブジェクト指向とは、プログラム設計の考え方の一つです。
プログラムで扱う変数や値を「オブジェクト(物体)」として捉え、オブジェクトに紐付いている「メソッド」を操作することで処理が行われます。

言葉で説明しても分かりづらいと思うので図で表してみましょう。

オブジェクトとメソッドの関係

ここに整数の21が入った「21」オブジェクトがあったとします。
整数の「21」オブジェクトにはいくつかのメソッドが生えています。
例えば「21」に生えている「plus(13)」のメソッドを呼んでみましょう。
すると「21」は引数に与えられた13を足して34を返します。

オブジェクト指向ではこのように、オブジェクトとそこに生えたメソッドの呼び出しを使ってプログラムを書きます。

下のコードはこれをRubyのプログラムに置き換えたものです。

21.to_string()
# => "21" #文字列の21に変換

21.plus(13)
# => 34 #13を足した合計の34を返す

21.to_float()
# => 21.0 #小数の21.0に変換

ここで挙げたメソッドは全て架空のものなのですが、なんとなくオブジェクトの雰囲気は伝わるでしょうか?

余談ですがRubyは「純粋なオブジェクト指向言語」*1なので、Rubyのコードは基本的に全てこの形になっています。
一応ifとかraiseとかこの形にならない制御構文の予約語はあるのですが、他言語に比べると圧倒的に少ないです。
他言語では予約語になる単語もRubyではメソッドとして定義しているために

オブジェクト.メソッド(引数)

として、Rubyはほとんど全てのコードをこの形で説明できるのです。

オブジェクト指向的に解釈する


次は簡単なRubyプログラムの例をオブジェクト指向的に解釈してみましょう。

print(2 + 3)

一見してメソッドは無いように見えますね。
しかし実際には内部でこのようになっています。

print(2.+(3))

Rubyの+演算子は+メソッドの糖衣構文(文法のエイリアス)になっているので、+演算子は実は「2」オブジェクトの+メソッドなのです。

ちなみに、print()の方はあらかじめ用意された画面出力のメソッドです。
「"純粋な"オブジェクト指向ならグローバルな関数が存在するのはおかしくない?」と思った方は鋭いですね、本当の形はこうです。

Kernel.print(2.+(3))

内部で呼び出されているのはKernelモジュールのprintメソッドです。
Rubyにはグローバルな関数はないのに、なぜ暗黙にKernelのprintメソッドが呼ばれるのかは・・・面白い話なのでいつか記事にしたいと思います。

関数とメソッドの違い


さて、話を戻します。こうして構造をはっきりとさせていくとメソッドの引数にオブジェクトがあり、さらにオブジェクトとメソッドと引数があり・・・となっているのが分かりますね。
メソッドを実行したりして値を取ることを「評価する」と言います。
評価した値をメソッドの引数にとり、また評価してメソッドの引数に取る、この構造だけでオブジェクト指向は出来ています。

評価の流れ

オブジェクト指向ではない構造には、例えば「関数」があります。
関数は特定のオブジェクトから生えているわけではなく、それ単体で呼び出されます。
メソッドも関数に似ていますが、何らかのオブジェクトから生えている点が異なります。

+演算子を関数的・メソッド的に置き換えた場合を考えてみましょう。

メソッドと関数の違い

メソッドが生えてる元のオブジェクトのことをレシーバと言います。
レシーバが持つ値(value_1)と引数(value_2)を足すのでメソッドの引数は1つ、関数は引数が2つになりますね。

他にも、引数が数字・文字列・小数のごちゃまぜだった場合、上の+関数は2つの引数の種類に応じて複雑な条件分岐を実装しなければいけません。
しかし+メソッドは引数の種類がレシーバと同じかそれ以外かを考えるだけなので実装が簡単になります。

メソッドには関数と比べて、多様な引数を受け入れられる使い方があると分かったでしょうか?

おわりに


さて、ここまででオブジェクトとメソッドについて簡単に説明してみました
オブジェクト指向だから何ができるのかよりも、ひたすら構造の説明をしてしまったので面白くなかったかもしれないですね(すみません)。

オブジェクト指向と呼ばれているものは40年くらい前に考案され、現在の多くの言語の土台にあるとても重要な考え方です。
授業で習って何気なく使っているメソッドは、なぜドット繋ぎで記述するのか、オブジェクト指向をきちんと理解することはプログラム設計の基礎に繋がります。
そしてオブジェクト指向を言語仕様から体現しているRubyに僕は惚れ込んでいるので、Rubyの良さも同時に伝われば尚良しだと思っております。
今回は本当に形の説明だけでしたが、次はクラスとインスタンスについても触れる記事を書きたいですね。
ここまで読んでくださりありがとうございました。

細かい注釈

そもそもオブジェクト指向にも種類があり、記事ではクラスベースの解釈をしています(クラス指向)が、本来Rubyはオブジェクトに送るメッセージに注目して設計されています(メッセージ指向)。メソッドと引数がレシーバに対するメッセージとしてオブジェクトに渡されます。
この文法はオブジェクト指向を説明するのに分かりやすいです。他言語では制御構造として特有の記法を持つことが多いのですが、Rubyはほぼ全てをレシーバとメソッドの関係で表せます(そう見えない場合は糖衣構文)。なので今回はクラス指向の設計意味論的な解説より、メッセージ指向の視覚的な文構造で説明してみようと思いました。

*1 この「純粋な」オブジェクト指向とは何を意味するのでしょう。
上に述べた通りオブジェクト指向自体に複数の解釈がありますが、クラス指向的に解釈すると純粋には見えない部分があります。
例えばRubyはオープンクラスを採用しているのでStringクラスでもIntegerクラスでも自由に書き換えることが出来ますが、このような本来のスコープを超えた影響を与えるモンキーパッチはクラス指向のカプセル化や継承に反しているように思えます。
しかし実際これを実現しているのは純粋なオブジェクトとメソッドの呼び出しのみです。Rubyではクラス自体もオブジェクトで、クラスはClassクラスのインスタンスで表現されています。クラス内でメソッドを定義する機能はClassクラスのメソッドが提供しているだけなので、この挙動は言語の特別な機能ではないわけです。
Rubyが「純粋な」オブジェクト指向と呼ばれるのは、このように自身の言語仕様の深い部分もオブジェクト指向で設計されているからなのです。

参考

Rubyのclassクラスとかのモヤっと感をズバっと解決したい
https://qiita.com/gogotanaka/items/81cbaaf1c4deeb99b396
オブジェクト指向には大きくメッセージ指向、クラス指向、プロトタイプ指向の3つの流儀があると知りましたが、その上でこれらを包摂する説明や定義が可能ですか?
https://jp.quora.com/オブジェクト指向には大きくメッセージ指向-クラス
メッセージ指向、クラス指向、インターフェイス指向
https://qiita.com/wingsys/items/d42e7c09c0eed5197859


この記事が気に入ったらサポートをしてみませんか?