オブジェクト指向とインターフェース

*「ふっかつのじゅもんがちがいます。」:オブジェクト指向が嫌われるのは、オブジェクト指向そのものの設計が間違っているからだという中々刺激的なタイトルの記事を読みました。

世間ではオブジェクト指向って嫌われているんですか?私は大好きです。

数学の集合の記述風に(いい加減な書き方だが)こんな風に書くのは
File = { x | x.open() / x.close() / x.read() / x.write() できるもの }
プログラミング言語では(擬似的にJava風に書くと)


public interface File {
public void open(/**/);
public void close(/**/);
public void read(/**/);
public void write(/**/);
}


に相当するだろうし、

Fileの一種であり特別な場合であるPipeは集合としてはFileの部分集合である。集合風には
Pipe = { x | x ∈ File かつ x は パイプ }
Pipe ⊆ File
こう書けるし、プログラミング言語では例えば


public class Pipe implements File {
public void open(/**/){ /*実装*/ }
public void close(/**/){ /*実装*/ }
public void read(/**/){ /*実装*/ }
public void write(/**/){ /*実装*/ }
}


こうなるだろう。
クラス定義 ≡ 集合を定義
であり、
インスタンス作成 ≡ 集合から元を一つ取り出す
であり、
継承 ≡ 部分集合を定義
である。

クラスをインターフェース(ここではメソッドとして何が用意されているかという意味ですよね)の観点から眺めると、まるでOCamlみたいになっちゃいますよ。クラスにインターフェースを基準とした集合論的な定義を与えた場合、インターフェースが同一のクラスは区別が出来ないことになってしまいます。

class a =
  object
    method get = "a"
  end
;;

class b =
  object
    method get = "b"
  end
;;

let f (x:a) =
  print_endline x#get
;;

let _ =
  f (new a); (* 当然大丈夫 *)
  f (new b)  (* こっちも大丈夫 *)

私が最初OCamlに触れた時この事にとても驚いたのを覚えています。でももちろんjavaはインターフェースが同じであってもクラスが違うとダメです。

class A {
  public String get() {
    return "a";
  }
}

class B {
  public String get() {
    return "b";
  }
}

public class Test {
  public static void main(String args[]) {
    System.out.println(f(new A())); /* 当然大丈夫 */
    System.out.println(f(new B())); /* クラスAとBが区別されるからダメ */
  }

  static String f(A obj) {
    return obj.get();
  }
}

というわけで、インターフェース基準でクラスを集合論的に見て良さそうなのは、私の知っている範囲ではOCamlだけです(私はあまり計算機言語を知りませんから、他にもきっとあるんだろうと思います、詳しい人是非とも教えて下さい)。しかし上の例はあくまでも引数の型チェックの問題でクラスの集合論的な関係を一切捉えていないと考えることも出来ます。ところがRubyにはModule#undef_methodがあるから油断なりません。これだと完全にインターフェース基準の集合論像が崩れます。

class A
  def get
    "a"
  end
end

class B < A
  undef_method :get
end

puts A.new.get # => a
puts B.new.get # => undefined method `get' for #<B:0xb7c819fc> (NoMethodError)

javaや他の多くのオブジェクト指向的な言語は決してインターフェース基準でクラスを定めているわけではないので、インターフェース的に「興味深い集合を定義する」のが難しいという点がオブジェクト指向の本質的な難しさではないだろうと思われます。普通に考えればインターフェース定義が難しいのは別にオブジェクト指向に限った話ではないでしょう。また現実的な話をすれば、クラス設計は必要十分な範囲で事足りるので、そんなに汎用的で興味深い上位のクラスを設計する必要はないというか、そんな恐るべきことされたら却って、面倒なヤツ!とか思われちゃうんじゃないでしょうか。

そのようなわけで、インターフェース定義の難しさをもってオブジェクト指向の設計が間違っているというのは、前提から違っている以上根拠にならないと思います。もし言うのであれば、OCaml限定で「間違っている!」と言うのがよろしいかと思います。というアレな結論はどうでしょう?