«前の日記(2003-08-05) 最新 次の日記(2003-08-07)» 編集

Matzにっき


2003-08-06 [長年日記]

_ [OOP]継承は悪か

Linuxビボ〜ろくより。

greenteaさんが 「GUIでは継承を使わない」って書いたことが発端です。

で、その中でgreenteaさんは

継承を多用することについての戒めとしては「オブジェクト指向スクリプト言 語 Ruby」でも「継承は最後の武器だ」と述べられています。

と述べておられます。

確かに私は『オブジェクト指向スクリプト言語Ruby』の中で、 「継承は最後の武器だ」と書きましたが(p.219, p.562)、別に継承を避けろというつもりはないのです。

例によって私の表現が安易だったのですが、この機会に補足説明させてください。

まず、元ネタから説明しましょう。この言葉の元となった「拳銃は最後の武器だ」というセリフは、 『忍者部隊月光』の劇中で多用されるのですが、 実際には拳銃はばんばん使われてます。 ですから、意図としては「使うな」ではなく、「使う前に考えろ」です。

さて、技術的な点から考えると、greenteaさんも引用しているように 「継承はカプセル化の概念を破壊する」 あるいは「クラス継承よりもオブジェクトコンポジションを多用すること」 とはよく言われます。

しかし、私は同意しません。

(ちゃんと考えた後でも)継承を使いたくなるケースでは、 オブジェクトコンポジションの方が継承よりも優れていることはまれだからです。 むしろ、明示的な委譲の定義が繁雑で、「こんなこと自分でしなくちゃいけないのは変だ」と感じます。 そんなことを言語に押しつけて自動で片付けるための仕組みが、 オブジェクト指向言語の継承機能であったはずです。

「継承はカプセル化の概念を破壊する」件については、また別にもう少し考えなければなりません。 なぜ継承がカプセル化の概念を破壊するかといえば、 サブクラスはインスタンスの構造に直接触ることができるからです。

インスタンス変数を直接参照すれば、スーパークラスの実装に強く依存してしまいますし、 インスタンス変数を直接更新すれば、インスタンスの不整合な状態を作り出すことができます。 確かにこれはカプセル化の原則に対立します。

しかし、スーパークラスで定義されたインスタンス変数に直接触ることができることは、 オブジェクト指向言語にとって必須ではありません。たとえばC++はprivateなメンバには サブクラスからもアクセスすることができません。そしてさいわいなことにprivateがデフォルトです。 これが私の考えるC++の唯一優れた点なのですが。

Rebecca Wirfs-Brock女史は1988年のOOPSLAの論文でResponsibility Driven Designについて述べています。

その論文の中で、クラスの利用者は

  • 外部利用者(第三者)
  • 内部利用者(つまり自分自身)
  • 継承利用者(サブクラス)

の三種類があり、それぞれに見せるべき視点が異なると述べています。C++のpublic, private, protectedは まさにこの三種類に対応しているのです。余談ですが、去年のOOPSLAで会った時にWirfs-Brock女史のサインをもらってきました。彼女のファンなんです(ミーハー)。

結局問題なのはRubyのような言語がスーパークラスのインスタンス変数に簡単に触れてしまうという 「言語上の欠陥」なのであって継承そのものの問題ではないのですよね。 Rubyの次の世代の言語はぜひ「インスタンス変数はクラスローカル」が常識になってもらいたいものです。 事実、Perl6ではそうなるってDamian Conwayが言ってました。

というわけで、私の言葉を引いて継承を使わないほうがよいというのは、 私の本意とは違うんだ、ということです。

最後にどんな継承がまずいのか、あるいは不要なのか、について述べておきます。

私個人が継承を用いるかどうかについての唯一絶対のルールは「is-aの関係にあるか」です。 この関係が成立しない時に継承を使うのは純粋に間違いです。

では、この話の出発点に戻って「GUIに継承を用いるべきか」ですが、 ケースバイケースとしか言いようがありません。 ただ、

  • 今作ろうとしているエンティティが既存のクラスのis-aである
  • 継承を使った方が楽ができる

場合には遠慮無く使った方が良いと思うのです。ちゃんと考えた後でね。

本日のツッコミ(全1件) [ツッコミを入れる]
_ ま2 (2003-08-06 18:04)

smalltalkが世に出はじめたころに「オブジェクト指向の利点とは何か」を,よく他人に説明させられました。で,そのとき考えたのは以下のような論法です。<br><br>1. 問題を解くのにオブジェクト指向でも手続き指向でもコードの量が変わるわけではない。<br>2. プログラムは手続きとデータで成り立っている。モダンな言語はデータ定義をする機能もある。Cで言うと,手続き=関数,データは整数とか配列とか,データ定義はstruct。これがオブジェクト指向言語だと,手続き=メソッド,データ=オブジェクト,データ定義はクラスとなる。<br>3. 手続きはデータをパラメータとして受け取る。そして時系列に連続する処理を記述する。"時系列に連続する処理"を修正するのは楽である。しかしパラメータであるデータの形式が変化すると修正が大変。オブジェクト指向ではこれが逆で,メソッドはオブジェクトの添え物。またオブジェクト(=データ)の内部表現の変化への追従は楽。<br>4. で実際問題として,手続きが変わるより,データ形式が変わることの方が多い。したがって,実際問題としてオブジェクト指向の方が威力を発揮する。<br><br>つまり,オブジェクト指向は「データ中心」の産物で,「データ中心世界」では強い。そこでプログラミングの対象が「データ中心世界」なのか「手続き中心世界」なのかが重要だということです。結局オブジェクト指向とは「ものの見方」なのですね。うーんと,まとまりませんが,このへんで。

お名前:
E-mail:
コメント:
[]

«前の日記(2003-08-05) 最新 次の日記(2003-08-07)» 編集

track feed Matzにっき Creative Commons License This work is licensed under a Creative Commons License.