読者です 読者をやめる 読者になる 読者になる

Line 1: Error: Invalid Blog('by Esehara' )

または私は如何にして心配するのを止めてバグを愛するようになったか

>> Zanmemo

あと何かあれは 「esehara あっと じーめーる」 か @esehara まで

『オブジェクト指向でなぜつくるのか』の第一版について気になったところをメモ

はじめに

知人から『オブジェクト指向でなぜつくるのか』の第一版を譲り受け読んでいて、それなりに勉強になったものの、一部分で気になったところも多少あったりした。

最初は、割と厳しい文章になってしまったのだけれど、この本の発売が2004年で、現在第二版が出ている。第二版は2011年に発売されており、関数型についての言及もされている。著者のサポートページを信じるならば、「すべての文章を丹念に読み返して、不適切な論旨の展開や文章表現を細かく修正」しており、第一版を持ってして、内容を悪くいうのはフェアではないな、と思い、そういう部分については全面的に取り下げている。

とはいえ、ある意味において、2004年頃の「オブジェクト指向とはなんなんだ」といったような解答について、当時の雰囲気を今の時点で知ることもできるし、現時点で、このあたりの記述はどうなんだろう、ということを書き残すのは悪いことではない気がするので、その辺りをメモしておくことにする。

従って、今から書くことは、第二版では修正されているかもしれないし、第一版の中における歴史的制約の可能性も大きいことは、公平のために最初に書き残しておく。

ソフトウェア開発の難しさ

これは、そのもののサブタイトルの章があるのだけれど、個人的に気になったのは、ソフトウェアの難しさといったときに、二つのポイントが印象深かった。その内容とは、設計、あるいは実装方針における「モデリング」の難しさ、そして実装する上において、どういう風に作ると上手い形になるのか(例えば「デザインパターン」)といったところ。とはいえ、自分が普段から意識していることではあるので、そうだろうなー、という印象に止まった。

では、設計といったところで、どういう風に設計するのか、という点で、「オブジェクト指向は現実の比喩ではない」ということが第二章で述べられている。さすがに、「現実世界で動く人間は、個人個人が意志を持って動くけど、オブジェクト指向の世界のインスタンスは、外部からの指示がないかぎり自分から動くことはない(p.51)」という説明に関しては、ちょっと辛いかもと思ったりもした。

ただ、フォローをしておくとするなら、自分自身が、「オブジェクト指向は、人間のメンタルモデルに一致している」といったような話は正直疑っている。これはやはり慣れの問題だと思っているし、必ずしも現実を反映させることがポイントなのではなく、あくまであるプログラミングについて、スムーズな開発をするためによいアプローチでしかない、という風に考えているので、その意味では、同意するところは多い。この辺は当時の雰囲気を如実に表していると思う(サポートページで著者も「批判を和らげた」という風にしている)。

この辺りについては、また機会があればブログの記事にしたいと思う。

継承について思うところ

色々疑うところがあって、確かに「オブジェクト指向は現実を反映したものではない」というのはわかるのだけど、比喩として不適切かなあ、と思うところがある。

僕がよく思うことの一つに、「馬と牛は『動物』種類として括り出すべきなのか、それとも『乗り物』で括り出すべきなのか」ということがある。Rubyのコードだけど、次のようなコードを考えてみよう。

module Life
  class Animal
    def cry
      self.class::MESSAGE
    end
  end

  class Horse < Animal
    MESSAGE =  "ヒヒーン"
  end

  class Cow < Animal 
    MESSAGE = "Super Moo Power"
  end
end

puts Life::Horse.new.cry
puts Life::Cow.new.cry

一般的に、クラスとは何か、を説明するにあって頻繁に利用されるのが、「馬や牛の抽象的なクラスとしての動物」というのは、確かに生物学的に考えてみると、確かにそうだし、直感的であることは認めざるを得ないし、そういう風に書いてある。ただ自分の感覚としては、これらについてあまりよくないのではないか、と思っている。

その理由としては「馬や牛のどのような側面を種類として括りだしているのか」というのが分かりにくい、というのがある。例えば、これを「移動手段としての乗り物」の種類として考えるとすると、継承がどういうことかわかる。

module Movement
  class Vehicle
    attr_accessor :distance
    
    def initialize(distance=0)
      @distance = distance
    end
    
    def move
      @distance += self.class::SPEED
      return self
    end
  end

  class Horse < Vehicle
    SPEED = 5
  end

  class Cow < Vehicle
    SPEED = 1
  end
end

puts Movement::Horse.new.move.distance
puts Movement::Cow.new.move.move.distance

僕にとっては、このように「そのクラスがどのような側面を共通化したいのか」ということを示唆したほうがよい気はしている。Animalの継承としてHorseという風にすると、最初に抽象的なモノが来るかのようになってしまうけど、たぶんキモは「どの側面を切り出したいのか」という部分にある気がしている。

ちなみに、参考までに『独習オブジェクト指向開発』のほうを読んでみたところだと、このような継承関係を説明するにあたって、「白黒テレビ」の機能拡張を行ったものとして「カラーテレビ」、「カラーテレビ」の機能拡張を行ったものとして「ハイビジョンテレビ」という説明をしている。これも難しいところではあるけれど、ある部分において、機能を拡張するという側面に目を向けているという例えとしてはよいかもしれない。

ソフトウェアの歴史において必要とされていたこと

オブジェクト指向を理解するためには、それ以前、つまり

  • 機械語
  • 構造化プログラミング

の時代というのを理解しないといけないくて、第三章の歴史は普通に参考になる。少なくとも、なんでオブジェクト指向が要請されるようになったのかがわかりやすく解説されている。

どういうことかといえば、構造化プログラミングの目的として、サブルーチンで必要な機能を切り分けることによって、独立性を高め、その上で再利用性を担保することにある。そのことによって、機能毎に切り分けられるため、保守性も高くなっていくということが重要になる(実際、この手の問題に関しては、オブジェクト指向言語でも、実装者が意識しないといけない部分だとは思う)。

とはいえ、課題として残されていることとしては、「貧弱な再利用性」と「グローバル変数による状態の受け渡し」というところだけれど、ここから「だからオブジェクト指向だ!」とするのには、若干微妙なところもあって、これ自体の解決は、プログラミング言語の柔軟性によるもの、という部分も考えられる(例えば、通称「関数型言語」に関して「貧弱な再利用性」と「状態の受け渡し」について困るのは、むしろ実装方針のミスのようにも感じる)。

補足説明: オブジェクト指向におけるシングルトンパターンについて

そこで「グローバル変数」の何が問題なのか、を詳しく考えてみるならば、それはその変数に対して変更が加えられるタイミングの範囲が大きすぎる点にある。例えば、あえてRubyでスコープの大きい変数を定義して、その中でいろいろといじってみよう。

@bad_global = 100

def foo
  @bad_global *= 10
end

def bar
  @bad_global += 10
end

def hoge
  @bad_global -= 100
end

foo
bar
@bad_global = 1000
hoge
p @bad_global

これはまだ追いやすいものだけれども、このようにあちらこちらでグローバル変数を変更すると、どこでバグが起きたか、分かりにくくなるという欠点がある。

しかし、だからといって、オブジェクト指向を使えばこれが解決するかというと、当然のことながら、全くそうではなく、例えばデザインパターンの中でも悪名高き「シングルトン」を引用すれば、これが簡単な問題ではないことがわかる。

「シングルトン」といえば、そのコード全体において一つのインスタンスしか作らせず、既に何らかのインスタンスが存在している場合には、そのインスタンスを利用する、というデザインパターンだ。Rubyにはどうやら標準でsingletonという標準ライブラリが存在しているので、それを利用してみる。(参考: Ruby 2.0.0で学ぶ、14個のデザインパターンを作りました[GoF][Design Pattern] - 酒と泪とRubyとRailsと)

require 'singleton'
class AlmostGlobal
  include Singleton
  attr_accessor :value
end

AlmostGlobal.instance.value = 100

def foo
  AlmostGlobal.instance.value *= 10
end

def bar
  AlmostGlobal.instance.value += 10
end

def hoge
  AlmostGlobal.instance.value -= 100
end

foo
hoge
AlmostGlobal.instance.value = 1000
hoge
p AlmostGlobal.instance.value

おい!!! さっきと変わらないじゃねえか!!!ふざんけんな!!!

……と怒らずに、ちゃんと解説しておくと、「シングルトン」のキモになっているところは、「インスタンスは一つだけ生成し、もし他でそのクラスを使いたい場合は、既に生成されているインスタンスを返す」ということにある。従って、何も考えずに、「シングルトンは何処からでも使えるから便利~」とかやっていると痛い目にあう。それは隣のプログラマの物理的な攻撃かもしれない。

そういう冗談はともかくとして、確かに「構造化プログラミング」は「再利用の貧弱さ」と「グローバル変数」という二つの点で困難があったことは認めるにしても、それは大局的な問題ではないのではなく、むしろ実装方針においての問題だろうと思う。

もちろん、シングルトンを利用すると便利な場面もあると思うので、一概には否定できない。例えば結城浩の『Java言語で学ぶデザインパターン入門』では、利用するべき事例として「コンピュータそのものを表現したクラス、ウインドウシステムを表現したクラス」として表現されている。

この辺の混乱については、例えば他のページにも見られていて、例えば「クラスに備わる三つの仕組み」という部分において、「サブルーチンと変数をまとめる」という説明の仕方をしているけれども、この「サブルーチン」に引きずられているために、こういう困難が生じているように感じる。

ちなみにシングルトン周辺の議論に関しては、下の記事が参考になると思う。

まとめ

上のように、ちょっと何癖っぽいものを書き残してしまったけれど、2014年になってもうオブジェクト指向というのは、ある程度の筋道みたいなものが見えている時代からすると、まだ制約が多かった筈の2004年の記述をあれこれ言うのは、余りにもフェアではないかもしれない。

歴史の話をすると、例えば手元にある『プログラマーの復権』(1997年)の60ページによると、1994年くらいの雑誌には『オブジェクト指向は失敗した』という雑誌の特集が組まれていることが紹介されている。今になって「オブジェクト指向は失敗した」と言う人はいない。

あともう一つ。オブジェクト指向ですら、実はまだまだ誤解が多いのかもしれない。例えば、Erlangの作者であるJoe Armstrongは下のようなことを言ってたりする。

何はともあれ、ソフトウェア開発も、それに付随するモデリングも難しく、一つ一つ考えて積み重ねていかなきゃいけないなーと思い直したのでした。

今回引用した本のリスト

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

独習 オブジェクト指向開発 第2版

独習 オブジェクト指向開発 第2版

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

プログラマーの復権

プログラマーの復権

  • 作者: エドワードヨードン,Edward Yourdon,松原友夫
  • 出版社/メーカー: ピアソン桐原
  • 発売日: 2010/12/17
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る