2021年の「オブジェクト指向」を考える
きしださんが先日もたのしいお題を投下されていました。
出遅れましたがこのネタについて少し掘り下げてみます。
念のため個人的なスタンスをあらかじめ表明しておくと、オブジェクト指向に対してはそれなりに好意的ですが、別に時代の最先端だとかソフトウェア開発に必須の知識というほどではない(でも知っておくと便利というか、知らないと不便なこともあるかもしれないのでわざわざ避けるのはおすすめしない)というくらい温度感です。
オブジェクト指向 is 何
そもそも「オブジェクト指向」という言葉自体、座りの悪い言葉です。
意味が明確なのは「オブジェクト指向プログラミング(OOP)」、「オブジェクト指向プログラミング言語(OOPL)」、「オブジェクト指向設計(OOD)」「オブジェクト指向分析(OOA)」といった「オブジェクト指向なんとか」の方で、それらをふわっとまとめた(ような気がする)単語が「オブジェクト指向」じゃないかと思います。
とはいえ2020年代では手法としてのOOAやOODはあまり活発ではなさそうなので(※個人の見解です、というかもうちょっと活性化してほしいと思っているんですがその話題は別の機会に)、どちらかというとOOPとその周辺をふわっと指す言葉なんではないかという認識です。
あなたの「オブジェクト指向」はどこから?
では現在の「オブジェクト指向」とは具体的にどういうものなんでしょうか。
ふわっとしたものの輪郭をとらえるために、ざっくりと「こういうやつ」を列挙してみます。なお順番はてきとうです。
継承するやつ
これは分かりやすいですね。継承があればオブジェクト指向、という考え方です。
確かに継承はオブジェクト指向の重要なポイントの1つではあるはずですし、オブジェクト指向以外ではあまり継承を取り上げていないことが多そうなので、オブジェクト指向の特徴としては分かりやすいものです。
とはいえ継承は使いすぎると弊害も大きいこともあって、実装の継承とインターフェースの継承を分けたりとか、言語の文法で部分的に委譲できるようにしたりとか、さまざまな工夫も導入されてきました。またオブジェクト指向に影響を受けた新し目の言語では、継承を導入せずにオブジェクト指向的なことをできるようにしている場合が多いようです。(オブジェクト指向にとって)継承とはなんぞや、という話も必要なのかもしれません。
クラスを書くやつ
これも分かりやすいですね。クラスを使ってコードを書くのがオブジェクト指向、という考え方です。
ちなみにこう書くと「オブジェクト指向と言っても必ずしもクラスベースとは限らず、プロトタイプベースが云々」と言い出すオブジェクト指向おじさんやオブジェクト指向おばちゃんが現れるかもしれませんが、別に必要十分条件の話をしたいわけではないので気にしないでほしいです。
現代ではクラスはオブジェクト指向と密接に関わっているため、クラスベースな言語でクラスをもりもり書くプログラミングでは自動的にオブジェクト指向プログラミングになりそうです。
データと手続き・処理・関数などを一緒にするやつ
いわゆるカプセル化ですね。ちなみに私はカプセル化と情報隠蔽とは別物という流儀で習ったので、カプセル化は「まとめるだけ」で「情報を隠す」やつではない、というスタンスです。
「手続き」とか「関数」とか「メソッド」とか、なんかいろいろ言われ方をする奴とデータを別々に考えるんではなくて、一緒に考えようというものです。
「で?」とか思うかもしれませんが、「なんか分かりやすいし、扱いやすい」くらいに思っておけばまずは十分でしょう。もう少し細かい話ではスコープの管理みたいな話も出てきます。
オブジェクト指向プログラミング言語を使うやつ
世の中には「オブジェクト指向プログラミング言語(Object-Oriented Programming Language)」と呼ばれている言語があります。どこまでがOOPLでどこからがそうではないか、というのは難しいところがあるというか、一歩間違えると戦争になるので深入りしたくないところですが、まあ誰が見てもOOPLとか、公式サイトでOOPLと自称してるやつもいるわけです。
そういう言語を使ってプログラミングすることがオブジェクト指向すること、という考え方です。
確かにOOPLは文法やイディオムやクラスライブラリなど、言語を取り巻くもろもろがオブジェクト指向的な考え方によって作られているため、ふつうに書けばふつうにオブジェクト指向的なコードになりやすいです。そういう意味ではあなたち嘘ではないのでしょう。
とはいえ、OOPLでオブジェクト指向っぽくなく書くみたいなことは普通いくらでもできますし、OOPLじゃない言語でOOPっぽいことをする例もいくらでもあるわけで、さすがに言語だけで決めていいのか? という気持ちにはなります。
メッセージをパッシングするやつ
オブジェクト指向の中でもメッセージ指向の流れがあって、そちらの方はメッセージパッシングを重視するものです(他には抽象データ型の流れがあります)。
とはいえメッセージはActorモデルとかもあって、そちらとオブジェクト指向は関係あるけど一括にはできない、という距離感だと理解しています。
データがアイデンティティーを持つやつ
そういえば最近は「オブジェクトはアイデンティティーを持っていて云々」という話は以前ほどは聞かなくなった気がしますね。
DDDをやってる人だとエンティティと値(オブジェクト)の違いと言えば分かりやすいかもしれません。すべてを値で表現することもできるかもしれないけど、アイデンティティーを持ったエンティティーを使った方が分かりやすいものもあるよね、という世界観で、それがまさにオブジェクトであり、そのような設計・モデリングに基づきプログラミングするのがオブジェクト指向だ、という見解です。
もちろんこのような性質をデータに持たせるのにOOPLは必要なわけではないのですが、OOPLであれば言語としてこのような機能をすでに持っているわけで、それを自然に活用できると便利ではあります。
遅延束縛するやつ
「遅延束縛」と日本語で言われてもピンとこないかもしれませんが、英語だとLate BindingとかDynamic Binding(動的束縛)とかいうやつですね。ここでいう「束縛」というのは、代入の親戚の方ではなくて、コードに書かれているメソッド(名)とその実装との対応付けをコンパイル時ではなく実行時に解決するというものだと理解しています。
この辺り、実用的には「あとから継承したクラスを追加する場合にも元のコードを修正したり(コンパイル言語の場合でも)再コンパイルする必要がなくて便利」みたいなのがあるんですが、動的な世界で暮らしている人には分かりにくいかもしれません。また、別にOOPLじゃなくてもあるところにはある機能ではあります。
ポリモルなんとかとかいうやつ
これはポリモーフィズムのことです。日本語では多態性、多相性という訳語が与えられています。オブジェクト指向の説明にはよく引き合いに出されるものでした。
最近だと多相がどうという話はオブジェクト指向よりも型システム方面でよく聞くような気もしますが、オブジェクト指向の基本の1つにポリモーフィズムを入れるのはめずらしくはないかと思います。
関数の最初の引数を関数名の前に書くやつ
これは構文的な話ですね。要は foo(val1, val2)
と書くやつはオブジェクト指向じゃなくて、 val1.foo(val2)
と書くやつはオブジェクト指向、というものです。後者は val1->foo(val2)
でも許されそうです。
もちろんこんなことを書くと「CLOSは?」などの突っ込みが入るのがお約束ですが、GoとかRustとかもこの前に書くスタイルが援用されていて、一定の重要性はあるように思われます。
変数の後ろに「.」と書くとメソッドを補完してくれるやつ
最近聞いたパターンなんですが、OOPLのメリットとしてIDEを使った場合にメソッド補完がいい感じにできる、ということを挙げる場合があるそうです。
なるほどねーという感想ですが、もちろんこれはオブジェクト指向の重要な概念というわけではなく、前述の構文的な特徴からくる副次的なメリットと考えるべきではないかと思われます。とはいえ確かにうまくできているなあと思います。
UMLを書くやつ
最近は衰退気味かもしれませんが、UMLというモデリング言語がありまして、これを使うとオブジェクト指向になるというやつです。
UMLはOOPやOOD・OOAとの親和性が高いので、オブジェクト指向で使われる(オブジェクト指向以外ではあまり使われない)かもしれませんが、まあユースケース図とか状態マシン図とかはオブジェクト指向関係なく使えるし使ってるよね、という人もいそうです。あるいは「クラス図を使えばオブジェクト指向」ということかもしれませんが、その場合は「クラスを書けばオブジェクト指向」と同じような意味合いになりそうです。
なんとかパターンを使うやつ
デザインパターンとかアーキテクチャパターンとかのやつです。まとめて示す場合は「ソフトウェアパターン」という言葉を使うのが好みです。DDDとかもパターンの親戚なので含めてしまってもいいのかもしれません。
さすがにこの辺までくると「それってオブジェクト指向関係ないのでは?」と疑い始めていると思いますが、パターンが盛んだった時代はいろんな意味でオブジェクト指向プログラミングが流行っていた時代と重なり、パターンを考案したり記述したりする際には暗黙の前提としてオブジェクト指向が想定されていることも少なくないのかもしれません。そういう意味では本質的に関係があるわけではないにせよ、既存のパターンに関しては一定の親和性はありそうですし、結果的にオブジェクト指向と重なりがちなのかもしれません。
ちなみにDDDについては関数型でDDDする本「Domain Modeling Made Functional」もあるので参考にされると良いかもしれません(私は未読です)。
TDDで使うやつ
テストについてもOOPとか出てくるずっと前から書いてたのに今さら何を? と思われるかもしれませんが、xUnitとかになってくるとOOPと合わせて発展してきたという経緯がありそうです。
TDDもSmalltalkやJavaなどで使われ始めたので、OOP(OOPL)と合わせて語られることが多いのかもしれません。とはいえもちろんOOPL以外でもふつうにTDDできます。
アジャイルで使うやつ
この辺りもすでに書いた項目と同じように、「オブジェクト指向と関係あるわけではないけど、OOPと合わせて使われていたので関連付けられて考える場合もある」くらいのやつです。
言語や設計とは距離があるので、より関係の浅いものですね。
オブジェクト指向の浸透と拡散
いろいろ書いてきましたが、「オブジェクト指向関係なくない?」というものをそれなりに含まれていたかと思います。
これは、一時期(おそらくある程度は現在も含む)のソフトウェア開発の中でオブジェクト指向が大きな位置を占めており、ソフトウェア開発に関するもろもろがオブジェクト指向の考え方や文化に影響されていた、というところに由来するのではないかと思います。直接関係なくても、間接的には影響を与えていた、ということですね。その辺もみんなひっくるめて「オブジェクト指向」ということだと言うと言い過ぎになるわけですが、まったく無視して議論するのも難しいかもしれません。
そんなわけで、ソフトウェア工学全体を勉強する場合でも、オブジェクト指向についてある程度知見があった方が理解しやすいところはあるでしょう(同様に、関数型についてもある程度知見があった方が理解しやすいところもあるでしょう)。
まとめ
幸か不幸か、「オブジェクト指向とはなんぞや」という定義をトップダウンに持ってきて、それを使うといいプログラミングができる、ということはなさそうです。一時期はそういう傾向も一部にはあったのかもしれませんが、現代では雲散霧消していると言ってよいでしょう。
かといって、OOPLやオブジェクト指向の影響を受けた言語でプログラミングする際には、どうしてもメソッドだの継承・委譲だのの言葉が避けられません。この辺を理解するにはOOP的な考え方をある程度知らないと、ちょっと不思議なコードができてしまうかもしれません。
その意味で、きしださんのようにそれを「ソフトウェア工学」と呼ぶことにするにせよ(少なくともソフトウェア開発におけるオブジェクト指向関連技術・技法はソフトウェア工学の一部に含まれることは確かでしょう)、オブジェクト指向の影響を受けた技術について知っておくに越したことはないでしょう。
そういった経緯で知識としてのOOPについて学びたいときでも、プログラミング言語を使いながら、コードを書きながら覚える、という実践的な習得方法をおすすめしたい気持ちはあります。
つまるところプログラミング言語も道具であり、道具は使いながら覚えるものなので。「これはオブジェクト指向的なのか否か」「何をすればオブジェクト指向になるのか」といった悩みに囚われる必要はないかと思います。
いい感じなプログラミングを目指して、引き続き頑張っていきましょう。
追記: オブジェクト指向大統一理論の夢とその喪失
オブジェクト指向の議論が発散しやすい一因として、かつてのオブジェクト指向はプログラミングよりももっと大風呂敷というかスコープを広げて、ソフトウェアに関するすべてをオブジェクト指向で統一する大統一理論的なものを想定していたものの、現代ではその文脈は失われつつある点があるかと思います。
これまでそれとなく触れていたOOA、OODがこの辺に関わるものなのですが、歴史的にはおそらく20世紀末から21世紀初めにかけて、ソフトウェアのコードから離れた要件定義や各種の設計についてもオブジェクト指向の考え方を反映させ、ソフトウェア開発・運用全体を通して使える超便利な体系としてオブジェクト指向の開発プロセス全体を確立させよう、としたものだと理解しています。
とはいえ曲がりなりにも統一されたのはその記法であるUMLくらい、というと語弊がありそうですが、この手の大統一理論にはありがちの「風呂敷を広げれば広げるほど規模が大きくなり収拾がつかなくなる」的なことがあったようで、同時期に現れた軽量な手法であるアジャイル開発的な手法・プロセスの方が広まり、現在では普通に使われるようになったことはご存知の通りです…と言っていいんでしょうか(正直RUPとかあんまり詳しくないというか、アジャイル方面に親近感をもっていたのでそうじゃない大規模手法をよく知らないのでした。なのでこの項目もおまけ的な位置づけとして書いています)。
現代だとかろうじてDDDがその名残をとどめているところもありそうですが、DDDであっても現代ではドメインエキスパートをモデリングに継続的に巻き込むことはほとんど行われないのではないでしょうか(そうでもないですか?)。たぶん、あの当時の夢は現実として定着することはなく、今はまた夢のままになっているかのようです。
かといって、現代の要件定義や設計に関しては、つまるところ「チームビルディング頑張る&テスト書きつつリリースサイクル早めて監視・計測すれば何とかなるのでは?」みたいな身も蓋もない「方法論」しか存在していないようにも感じられます。他には形式手法なども発達してきていますが、要件定義とはちょっと方向性が違いそうですし、まだ広く使われているわけでもなさそうです。
2021年にオブジェクト指向について語る際でも、このような過去の文脈は封印されたまま顧みられることはあまりなさそうです。そのため、「オブジェクト指向」という言葉が指すものが余計にふらついてしまい、過去の文献や知見を辿ろうとしても失われた文脈のおかげで今ひとつしっくりこない、という残念なことになっていそうです。しかし、今さら過去の夢を掘り返してみたところで正直実現しそうにも思えないので、どうしたもんかな…というところです。
Discussion
関連するスクラップ書きました。