«前の日記(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である
  • 継承を使った方が楽ができる

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


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