(原文はこちらです。
Why OO Sucks by Joe Armstrong)
(訳注: 原文はけっこうテキトーな文章なのでテキトーに解釈しました。)
なぜオブジェクト指向はクソなのか by Joe Armstrong
私が最初にオブジェクト指向プログラミングの考え方に導入されたとき、何故だか分からないが、私は懐疑的だった。 - それは "間違っている" と感じた。その後、オブジェクト指向プログラミングは非常に人気となり(理由は後で述べる)、オブジェクト指向プログラミングの批判はむしろ "教会の宣誓"(swearing in church) のようなことだった。オブジェクト指向は、すべての立派な言語が持つようになった。
Erlangは普及するようになったとき、我々は、よく「Erlangはオブジェクト指向ですか?」と聞かれた。 -まあ、正解は「もちろん違います」だったのだが、我々はこれを大声で言うことはなかった。それが(あなたがたくさん手を振ったとき)Erlangはオブジェクト指向(の一種)であるように印象づけ、(あなたが私たちの実際に行ったことに耳を傾け小さな活字を読んだとき)本当はそうではないというように設計された慎重な方法だった。(イミフ)この点で私は、IBMの当時の上司がパリ第7回IEEEロジックプログラミング会議で聴衆に宛てた基調講演を思い出す。IBM prologは、オブジェクト指向の拡張機能の多くを追加したが、その理由を尋ねられると、彼はこう答えた。「 我々はオブジェクト指向prologを作ったので、わが社の顧客は、オブジェクト指向prologを望んでいた。 」私は「どれくらい単純か、良心の呵責はない、無反省、「これが何のために正しいかと」と尋ねないこと...」と思ったことを覚えている。
なぜオブジェクト指向はクソなのか
オブジェクト指向プログラミングに私の原則異議が関与の基本的な考え方にまでさかのぼり、私は彼らにこれらのアイデアといくつかの問題点を概説する。問題点1 - データ構造と関数は一緒にするべきではない
オブジェクトは分割できない単位で関数とデータ構造に結びつける。関数とデータ構造は全く異なる世界に属しているので、これは根本的なエラーだと思う。なぜか。
- 関数はなにかを行う。関数は入力と出力を持っている。入力と出力はデータ構造であり、関数によって変わる。ほとんどの言語では関数は、命令列からなる。 "これをして、次にこれをして..." 機能を理解するには、物事がが行われる順序を理解する必要がある(Lazyな関数型プログラミング言語と論理的言語ではこの制限が緩和される)。
- データ構造はちょうどそれ自身である。彼らは何もしない。彼らは本質的に宣言的である。データ構造を "理解" するのは、関数を "理解" よりもずっと簡単である。
関数は、出力に入力を変換するブラックボックスとして理解される。私は、入力と出力を理解すれば、関数を理解した。 これは私が関数を書くことができると言っているわけではない。関数は、通常、型T1のデータ構造からに型T2のデータ構造へ変換するという計算システム内のものであることを観察することによって理解される。関数とデータ構造は、完全に異なる種類の動物であるので、同じケージにそれらを閉じ込めることは根本的に間違っている。問題点2 - すべてをオブジェクトにしなければならない
"時刻" について考えてみよう。オブジェクト指向言語では "時刻" はオブジェクトである必要がある。しかし、非オブジェクト指向言語では "時刻" は、データ型のインスタンスである。例えば、Erlangで時刻の異なる種類がたくさんあるが、これらは次のような型宣言を使うことで、明らかで明瞭に指定することができる。-deftype day() = 1..31.
-deftype month() = 1..12.
-deftype year() = int().
-deftype hour() = 1..24.
-deftype minute() = 1..60.
-deftype second() = 1..60.
-deftype abstime() = {abstime, year(), month(), day(), hour(), min(), sec()}.
-deftype hms() = {hms, hour(), min(), sec()}.
...
これらの定義は、どんな特定のオブジェクトにも属していない。彼らはシステム内のあらゆるところで使え、時刻を表すデータ構造体は任意の関数で操作することができる。付随するメソッドは存在しない。問題点3 - オブジェクト指向プログラミング言語では、データ型の定義があらゆる場所に広がってしまう
オブジェクト指向インタプリタ言語のデータ型の定義ではオブジェクトに属している。だから私は一箇所ですべてのデータ型定義を見つけることができない。ErlangまたはC言語では、単一のインクルードファイルやデータディレクトリ内にすべてのデータ型を定義することができる。あるオブジェクト指向プログラミング言語では、それができない - データ型の定義は、あらゆる場所に広がってしまう。この一例を挙げる: ユビキタスデータ構造を定義すると仮定する。 ユビキタスデータ型は、システム内の "すべての場所" で発生するデータ型である。Lispプログラマは長い間知っているように、ユビキタスデータ型のたくさんの数のデータ型と少ない数の関数を持つことより、少ない数のデータ型とそれらに働く少ない数の小さな関数を持つ方がが良い。ユビキタスデータ構造は、連想リストや、配列やハッシュテーブルまたは、より高度な時刻または日付やファイル名などである。あるオブジェクト指向プログラミング言語では、ユビキタスデータ構造を定義するための基礎オブジェクトを一つ選ぶ必要があり、このデータ構造を使用したいすべてのものは、このオブジェクトを継承しなければならない。もし "時刻" オブジェクトを作りたいと思ったとき、それぞれのデータ構造はどのオブジェクトに属するべきか?問題点4 - オブジェクトが非公開な状態を持つ
状態はすべての悪の根である。副作用のある特定の関数は避けるべきである。プログラミング言語では状態は望ましくないのだが、現実世界では状態であふれている。私は自分の銀行口座の状態に興味があり、私が銀行からお金を預けたり引き出したとき、自分の銀行口座の状態が正しく更新されることを期待する。現実世界に状態が存在するとき、それらを扱うためにプログラミング言語はどのような機能を提供する必要があるだろうか?
- オブジェクト指向プログラミング言語では、 "プログラマから状態を隠す" と言う。状態は隠され、アクセス関数だけを通して見ることができる。
- 従来のプログラミング言語(CやPascal)は、状態変数の可視性は、言語のスコープ規則によって制御されていると言う。
- 純粋な宣言型言語では、状態は存在しないと言う。
システムのグローバルな状態は、すべての関数に運ばれ、すべての関数から出てくる。関数型プログラミング言語におけるモナドや論理型言語に置けるDCGsのようなメカニズムは、プログラマから状態を隠し、 "まるで状態は重要ではないように" プログラムできるが、状態へのアクセスはできなければならない。オブジェクト指向プログラミング言語によって選ばれる"プログラマから状態を隠す"オプションはより悪い選択肢である。状態を明かにし、状態への迷惑を最小限に抑えることをせず、状態を離して、見えなくしてしまう。なぜオブジェクト指向は人気だったか?
- 理由1 - 学習が容易であると考えられた。
- 理由2 - コードの再利用を容易にすると考えられた。
- 理由3 - 売り込まれた。
- 理由4 - 新しいソフトウェア業界が作成された。
私は1と2の根拠は見たことが無い。理由は技術の原動力であるように見える。ある言語が、その言語自身の問題を解決するための産業を作り出す場合、それを作るということは利益を出したい輩に対して良いアイディアであるに違いない。これはオブジェクト指向プログラミングの背後にある真の原動力となっている。