« 2005年12月8日 | 2005年12月10日の投稿 |
2005年12月14日 » |
ぼくがオブジェクト指向言語を勉強しはじめた90年ころは、「継承」という概念がとても流行っていて、継承によって「差分プログラミング」ができることがオブジェクト指向設計の再利用性の典型例のように言われていた。もちろん、こういう誤解は95年くらいには、みんなウソだと分かってきていた。
しかし、それでもときどき、
すべてのクラスの頂点のような「神様クラス」を作ってしまうことがある。
例えば、90年代の多くのC++オブジェクト指向データベースは、Persistenceのようなクラスを継承することで永続オブジェクトとなるクラスをマーキングしたり、あるベンダーのコレションクラスは、Objectというクラスを継承したクラスのオブジェクトのみがコレクションの要素となることができたり、という具合に。また、EJBも最近まではEntityBeanを継承することでEntityBeanの資格が得られるし、ServeletもHttpServletを継承することで、リクエストを拾うことができる。このように継承を使うと、サブクラスが「必ず実装すべきメソッド」をコンパイラを使って強制することができるので都合がよい。
しかし、こういう、「神様クラス」には弊害が多い。特にテスト容易性(EoT=Ease of Testing)をそいでしまうことが多い。神様クラスがないとテストができない(Cactusを使ってテストをするのはあまり気持ちがよくない)。
97年にぼくがJUDEの開発を始めたとき、UNDO/REDOをコマンド実装者に負担をかけないで(Command/Mementoパターンを使わずに)行える、メモリ上のデータベースを設計した。この実装は、ぼくが最も得意とするもので、過去に2回、同様の実装をC言語、C++言語で成功した経験があった。今回はJavaで実装した。このときも、このデータベースに保存できUNDO/REDOの編集対象となるエンティティの「神様クラス」を導入しようとした。実は、過去の実装ではそうしてきた。しかし、その時は、その考えをやめた。それは、そのデータベースのライブラリがなくても、エンティティをテストできるようにしたかったからだ。今風にいえば、POJOを使ったDIコンテナだ。JavaはReflectionを持っているから、こういうことができる。(ちなみに、Martin Fowlerが使ったPOJO=Plain-Old-Java-Objectという言葉は、C++がC言語のstructとの互換性を保ちながらclassを設計した経緯で使われたPOCO=Plain-Old-C-Objectから来ている)
C++を設計したStroustrupも、抽象クラスを多用する弊害について言及し、「具体的でシンプルなクラスが最もよい」と言っていた(The C++ Programming Language, 2nd/3rd)。そして、
クラスライブラリは森にならず林にすべきだ
と言った。すべての頂点クラスは必要なく、複数の頂点があってよい。
そして、C++はtemplateを導入した。templateのパラメータ型は、特定の演算子やメソッドを実装していることが求められるが、それは「継承」という形では強制されない(ここはEiffelやJava Genericsと異なる)。templateがインスタンス化されてクラスになるときに、例えば、find 関数は、イテレータが operator++()を実装していることを期待するが、メソッドを持っていないクラスをイテレータに入れるとコンパイルエラーとなる(そして、配列に対するイテレータである「ポインタ」は、この++を持っているために、クラスでなくともfind関数の引数となれる!)。つまり、「継承」を使わずに、「決め事」として実装しなければならないメソッドを強制する(しかもコンパイルエラーを出す)。
もう1つの例。JUnit という、今では多くの人が使っているツールも、当初はテストクラスはすべてTestCase というクラスを継承することが求められた。しかし、JUnit4 ではこの継承が不必要となった。(以前書いた、振る舞い定義スイートである、jBehave はもともと継承を必要としないコンセプトで開発されている)
このようにして、今では1つの常識ができた。利用者に強制的に継承を迫るのは、は嫌がらせだ。
神様ルートクラスを嫌い、POJOを好め
« 2005年12月8日 | 2005年12月10日の投稿 |
2005年12月14日 » |