頼むから凝ったコードを書かないでくれ

コーディングは自由度が高い。自由度が高いゆえに、同じことをいろいろなやり方でできてしまう。

There should be one-- and preferably only one --obvious way to do it.

とZen of Pythonにあるように冴えたやり方はたった1つだ。しかしながら、イマイチなやり方は無数にある。

無数にあるイマイチなやり方で、最もやめてほしいのは「凝った」ことをすることだ。凝ったコードというのは、クソコードを遠回しに言っているだけである。「凝ったことしているなあ」と言っていたら、「うわ、なんだこのクソコードは」と本当は言いたいのである。クソコードというとコードそのものを批判するのではなくて、書いた人を批判しているみたいになるので、婉曲的に言っているだけだ。

凝ったことをするというのは、意味もなく難しくしようとしていることである。素直に書けばよいものを、余計なことを考えて難しくする。そして、その余計なことの意図がコードに現れていなくて意味不明、あるいは成功していない。

やりたくなる気持ちはよくわかる。私もしているに違いない。凝ったコードを見ると、怒りとともに、私も似たようなことをしているのだろうという嫌悪を感じる。

新しいやり方を覚えたら使いたくなる。手段が目的より前に出ている状態ではあるが、目的は自由に選べない身分からしたら、手段しか工夫するところがないのだ。「手段のために目的は選ばない男」*1ではないが、目的なんて関係なく、いろいろなやり方を試したくなるものだ。その結果、工夫が明後日の方向に向かうこともあるでしょう。

意味もなく関数の再帰呼出ししたり、意味もなく継承をしてみたり、意味もなくラムダ式を使ってみたり、意味もなく抽象クラスを作ったり、意味もなくデザインパターンを使ったり、etc.

気持ちはわかるがやめてほしい。分かってないのに、分かった振りしているのは、余計に物事を複雑にする。難しいことを知っていて・使いこなせる達人なんだと思いたいのは分かるが、そういうことをする人が最も素人くさい。それはキャリアが何年あろうが同じことだ。分かってないのに難しいことをしようとする奴は素人だ。

それでも、使わなければ覚えないじゃないかという問題がある。新しいやり方を覚えること自体は善だ。新しいやり方を絶えず習得し続けなければ、静かに死んでいく。

本当に有効ならば、新しく覚えたやり方を使ってもいい。基準は、そのやり方を使った方が明らかに問題が簡単になっているかどうかだ。複雑さが隠蔽されて見かけ上問題が簡単になるならば、使うべきだ。シンプルさに寄与するならば正しい。シンプルになるなら、凝ったことをしているなんて誰も言わない。


さて、困るのはシンプルの基準というのはパラダイムの問題だということだ。パラダイムとは、物の味方や思考の枠組みのことだ。

自分の持つパラダイムにないことは理解できない。バイトコードを読むような人からしたら、サブルーチンを呼び出したり、スタックに変数を割り当てるのは、複雑なことで理解できない。手続き型が染み付いている人からしたら、関数ポインタを駆使するオブジェクト指向なんて複雑怪奇で理解できるわけがない。オブジェクト指向なんて難しいことをするなとなるわけだ。よく出てくるキラーワードが性能だったりする。

いわゆるバカの壁に阻まれている状態だが(お互いに)、これをどうしたらいいのかは今のところ答えがない。

トヨタ生産方式とアジャイル開発

トヨタ生産方式には7つのムダというものがあります。それぞれ、以下のようになっています。

  • 作りすぎのムダ
  • 手待ちのムダ
  • 運搬のムダ
  • 加工そのもののムダ
  • 在庫のムダ
  • 動作のムダ
  • 不良を作るムダ

トヨタ的な考え方では、製品を加工している作業が唯一付加価値を生む作業であって、他の作業は付加価値を生まないので極力排除していくべきものであるとします。それらは「ムダ」と呼ばれます。ムダには上記の7種があり、これらをなくしていくことが原価を減らし、企業の競争力を高めます。実際、トヨタ自動車は儲かっており、この考えを完全に否定できる人はいないでしょう。

さて、ソフトウェアの開発にもトヨタ生産方式の考え方は適用可能だと考えています。結論からいうと、トヨタ生産方式が目指すところはアジャイル開発とそれと共通している。

ソフトウェアの開発プロセスを工業製品の生産と同一視するようなソフトウェア工学は一般に嫌われがちです。ソフトウェアは一品ものであり、大量生産が行われる工業製品と一緒にしてはいけないだとか、開発者を工作機械と同等に扱っており人間性を無視しているだとかの批判があります。人月計算のウォーターフォール開発が非人間的であるということは、多くの人が述べるところであり、私も嫌いです。ウォータフォール開発はソフトウェアを生産する一本の長い生産ラインを想定したもので、そこではエンジニアはライン工です。同じことを延々と行うライン作業の非人間性は、ソフトウェア産業が生まれるよりかなり以前からに既に批判されています。古くは『モダンタイムズ』であり、これは1936年の作品です。人月計算のウォータフォール開発をしている企業は未だにチャップリンの時代から成長していない。

実は機械工業の世界では、もはやチャップリンの時代のようなことはしていないのです。生産ライン自体は存在しますが、ライン工は非人間的な仕事ではなくなっている。現場作業者自身が自分の仕事のムダを見つけて、「カイゼン」を行っている。カイゼンというのは創意工夫だから、それ自体面白いことだ。非効率極まりない非生産的な仕事を淡々とこなせというのが最も精神を蝕む。同じ仕事であっても、考えて工夫することが許され、それが評価されるならば、面白いものとなる。現代の工場というのは、モダンタイムズに描かれるような人を人として扱わないような場所ではなくて、作業者も知恵を絞って自分の職場をよくしようとしているような、活き活きとした場所である。*1別にカイゼンは、決して人間が気持ちよく働けるためにしているわけではない。ただ金のため、効率のためだ。効率化と人間の幸福は決して矛盾したものではない。鞭打って働かせても生産性が上がるわけがない。油と工数は絞れば絞るほど出るとか思っている人は、人間の可能性を信じてなさすぎる。人間を舐めるな。

トヨタ生産方式は、同一品種の大量生産によって効率を高めようというフォード式の生産方式の問題を解決するために生まれた。フォード式の生産方式を別の言い方で呼べばロット生産である。予め何をいくつ作るべきかが分かっていればまとめて作った方が安い。しかしながら、予想が外れて売れ残ったら大量の在庫を抱える。売れ残りというのは、原価もムダになれば、それを保管した場所もムダになれば、処分のための費用もムダになる、非常に恐ろしいリスクである。これが作りすぎのムダである。これを回避するために、必要なものを必要なときに作れば良いというJust In Time方式が発明された。

ロット生産の、何を作るべきか事前に分かっていなければならないという欠点は、ウォータフォールモデルが抱える欠点と同じである。ウォータフォール開発はロット生産と同じ発想だ。何かを作るときに手間がかかるのは「段取り作業」である。材料や金型を機械にセットして、加工が始められる状態にする作業に大きな工数を要する。ロット生産の発想に立つと、段取りにコストがかかるならば、段取りの回数を少なくしようとする。一度セッティングをしたら、できるだけそのセッティングを壊さないで、同じものをたくさんつくる。そうしたら、1つの製品あたりの段取りのコストは少なくなる。ウォータフォール開発も同じで、まとめて設計して、まとめて生産して、まとめて統合して、まとめてテストして、まとめて納品したら、一個の機能あたりの付随作業のコストは少なくなるし、最初に意図したものが確実に出来上がるだろうという発想だ。しかし、ロット生産と同様に不要なものを過剰に作ってしまう可能性が高い。

作りすぎのムダの何がヤバイのかというと、他のムダを隠してしまったり、他のムダを生み出してしまうということが問題である。一生懸命ムダなものを作っていたとしても、外からみるといかにも仕事をしているように見える。仕事がないという手待ちのムダだとかが隠れてしまう。忙しそうにしているけれども、その作業自体がムダだというのが分からなくなってしまうのだ。あるいは無意味なものを一生懸命作ってしまうことは、加工そのもののムダである。*2

作りすぎのムダを防ぐために、トヨタ生産方式ではどうするのかというと、必要になったらつくるということをする。*3まさにアジャイル開発でいうところのYAGNI――You ain't gonna need it――原則である。受注があるまでは作らずに、受注したら速攻で作れるような体制を整えておく。しかし、まとめて作るということをしないならば、段取りのコストがかかってしまう。なんてことはない、トヨタ生産方式では段取りの回数を減らすのではなく、段取り一回あたりのコストを減らすように工夫して、トータルの段取りのコストを下げるのだ。アジャイル開発でも同じことだ。なんの工夫もなしに、YAGNIを実践したらコストが増えるに決まっている。欲しくなったらすぐに作って配備できるように、ビルドだとかデプロイだとかテストだとかを自動化しておくのだ。機械加工だと段取りを迅速にするための仕掛けを治具といいますが、自動化のためのツールスクリプトはソフトウェア開発における治具なのです。

不思議なのは、偉大なるトヨタ生産方式が発明されたこの日本で、何故かソフトウェア産業においては未だにウォータフォール開発一辺倒であることです。何故かと考えると、顧客の程度が低いからでしょう。トヨタ自動車はかつて在庫を抱えて潰れかけました。そのために作りすぎのムダがいかに危険なものかを知り、カイゼンを行って今の栄華があります。ムダをムダと気づかないとカイゼンはできない。いわゆるSIerなるところにシステム開発を発注している企業なり官公庁は、無意味なものを大金払って作らせたとしても、それがムダかどうか判断つかないのだろう。SIerと呼ばれる業種はリスク引受業だと言われます。システムが品質・予算・納期を満たして完成するかどうかは、博打です。負ければ大損するので、SIerはそのリスクを引き受けて保険料を得るという商売をしています。しかし、SIerは作ったものが実は要らないものだったというリスク――作りすぎのムダのリスクは引き受けてくれない。予算超過なんかより無意味なものを作ってしまう方がはるかリスクフルだと思う。しかし、日本社会は作った動いたで満足なようで、それに意味があったとかは興味がないようだ。投資の効果をどうやって評価するかは私も知らないので、難しい話ですけれども……。

作りすぎのムダを排除して、初めて他のムダに手をつけることができる。材料を加工している以外の仕事は付加価値を生まないので、それらはなくせるものならなくしたいムダだ。ソフトウェア開発においては、コード触ったりテストをしている以外の作業はムダだ。コードも触らなければ、テストもしない人間の多いこと。必要悪ではあるのだけれど、こいつらをいかに排除して、コードを触れる人が作業に集中できるようにするかというのが、次の課題となる。そして、これらは創意工夫をともなう楽しい仕事だ。

トヨタ生産方式―脱規模の経営をめざして

トヨタ生産方式―脱規模の経営をめざして

*1:ちょっと機械メーカーを持ち上げすぎかな?鎌田慧自動車絶望工場』で描かれるように期間工は未だにしんどい仕事かもしれない。でも、けっこう給料がよいみたいなんだよな。

*2:7つのムダはMECEではない。作りすぎのムダが特別で、他の6個がMECEとなっている。

*3:トヨタ生産方式でも、生産計画というのは存在する。材料を発注したり、人を集める必要があるからだ。科学的でシステマチックなトヨタ生産方式においても、この部分は多分に博打的要素を含むようだ。アジャイル開発が無計画かというと、それも違っていて、迅速に開発できる体制を整え・維持するにもコストがかかるので、計画は必要である。いらんものを作るよりは計画がはずれる方がましということだろう。

クラスの凝集度を示すメトリクス:LCOM*

ソフトウェアメトリクスについて調査中です。今日はLCOM*について。

LCOM*とはLack of Cohision in Methods(メソッドの凝集度の欠陥)を示しており、以下の式で定義されます。

             1   a
           ――――― Σ μ( Aj ) - m
             a   j
LCOM* = ―――――――――――――――――――――――――
           1 - m

ここで、mはクラスに含まれるメソッドの数、aはクラスに含まれるインスタンス変数の数、μ(Aj)j番目のインスタンス変数にアクセスするメソッドの数です。

LCOM*はクラスごとに算出される値で、[0, 1]の値を取ります。

0だとすべてのメソッドがすべてのインスタンス変数にアクセスしていて、そのクラスは凝集度が高いと言えます。左がメソッドで右がインスタンス変数だとしたら、LCOM*=0のときは以下の図のようになります。

f:id:fjkz:20160424204412p:plain

一方で、1だと1つのメソッドしかそれぞれのインスタンス変数にアクセスしていないので凝集度が低いということになります。どのメソッドも最低でも1つのメソッドからアクセスされることを期待しています。LCOM*=1のときは以下のようになります。

f:id:fjkz:20160424204752p:plain

メソッドインスタンス変数の関係は要するに2部グラフです。Σ μ(Aj)はグラフのエッジの数となる。


[0, 1]になるように正規化しているようだが、この正規化が妥当かは疑問だ。一般にメソッドの数の方がインスタンス変数の数より多い。以下のようなインスタンス変数にアクセスしないメソッドがいたらLCOM*=1になる。

f:id:fjkz:20160424210937p:plain

しかし、以下のように2つ以上のメソッドインスタンス変数にアクセスすればLCOM*は低くなる。

f:id:fjkz:20160424211016p:plain

メソッドが多ければLCOM*は低く見えてしまう。値に対する操作が1つのクラスに集まっていれば、凝集度が高いと言えるので良いという意味なのだろうか。

また、この場合にはグラフは連結していないので、メソッドが増えたとしても凝集度は低いはずだ。LCOMにはグラフが連結しているかどうかという観点が抜けている。

LCOMが高いのはよくなさそうだが、LCOM=0がいいかというとそれも疑問だ。LCOM=0になるようには作れないだろう。良いものを見つけるのでなく、悪いものを見つけるのがメトリクスの目的と考えればよいのだろうか。

www.amazon.co.jp

オブジェクト指向で再利用性が高まるは嘘

オブジェクト指向でプログラムを作れば再利用性が高くなるというのは誤りだったと思う。オブジェクト指向プログラミング(OOP)についての本を呼んでいるとOOPは再利用性が高いというようなことが書いてある。すでに結論が出ている話な気もするが、これは必ずしも正しくないと思う。

正確には、OOPは結果として再利用性は高くすることもあるかもしれないが、それを目的にするものではないと思う。素直に作れば良いものを変に欲出して再利用性とか言って作ったら、そのプログラム内でも使いづらいし、他のプログラムになんか流用したくないものが出来てしまうだろう。

クラスの意味・役割は、プログラムの目的――ドメインって言うのか?――によって異なる。十得ナイフなんて作ろうとしたら、鉛筆削りにすら使えないものができるのでやめたほうがいい。特定のドメインのためのプログラムが他のプログラムに使いまわせることってあんまりないと思う。

逆説的だが、使いまわせるプログラムってわざわざ作る必要ないと思うのだ。そのドメインの問題が解決するプログラムがないから、わざわざ新しく作るのだと思う。あるなら別に新しく作る必要がないから、再利用なんて必要なくないだろうか。すでにそのドメインに既存のプログラムがあるなら、既存のプログラムの部品(クラス)をつまんできて再利用して組み直すより、組まれたものを拡張する方が正しいと思うのだ。

再利用性と拡張性は矛盾はしないけれども別の話だ。再利用性というとツブツブのクラスを流用できるという意味合いだと思う。一方で、拡張性というとクラス同士が結合した状態のものに新しいクラスを結合してプログラムを拡大できるという意味だと思っている。

OOPの良さは拡張性の方だと思う。大伽藍を作るときの設計にも非常に有用だが、それを増改築するときのしやすさが一番の利点なのではと考えている。綺麗に設計された構造になっていれば、増改築の影響は少数のクラスに影響に閉じるので、大きくしていくのが簡単になる。*1

特定のドメインのためのプログラムを作ったとしても、他のドメインでも使えそうな小さな部品が結果としてできることはある。ドメインに依存しないような部品ってのは当然ある。でないと標準ライブラリなんて存在意義がない。そういうのは、再利用すればいいと思う、できればドメインに依存しないような使いやすい形にして。

でも、それは別にオブジェクト指向だから再利用できる部品になったわけではなかろう。使いまわせそうなルーチンができるなんて、オブジェクト指向に限ったことではない。CでもシェルスクリプトでもFortranでも同じことだ。

もしかしたら、オブジェクト指向だと、ルーチンが小さいだとか、部品が整理された形で存在している可能性が高いだとか、名前が衝突しにくいといった理由で、部品が再利用できる可能性が高くなるかもしれない。しかし、それは副次的効果であって、期待してはいけないと思うのだ。

*1:それがどういう構造なのかは今調べているところ。

FlyweightパターンとMemoizationとメモリリーク

デザインパターンの1つFlyweightパターンは要するにMemoization (メモ化) のことです。Flyweightパターンというと何のことか良くわからないので、Memoizationパターンと読んだ方が適当なように思います。

メモ化とは、関数が返した値を覚えておいて、再度同じ関数が呼ばれた時に、覚えておいた値を返すことで同じ計算を何度も行わずに済ませることです。

メモ化を関数に適用した場合は値を返しますが、オブジェクト指向の場合はオブジェクトの参照を返します。そのため、結果として返されたオブジェクトは、複数のオブジェクトから共有されることになります。オブジェクトの生成のコストを削減することができます。

共有されたオブジェクトが書き換えられることは不都合です。どのオブジェクト間でメモ化されたオブジェクトが共有されるのかをコントロールすることは難しいからです。一般に、メモ化するようなオブジェクトはImmutableとしておくことが多い。Immutableであれば、書き換えられるおそれがないので、オブジェクトが共有されていても問題にならないからです。

返り値を覚えておくメモはstaticであることが多く、メモがオブジェクトである場合はSingletonを用いる。staticは嫌われるけれども、場合によってはstaticの方が効率的なのです。基本はdynamic?でつくって、最適化を施したいところをstaticにするというのが正しい戦略でしょう。

よく言われるように早すぎる最適化は良くないので、特に何も問題が生じていないのにメモ化を行うことはしてはならない。システムとして動くようになって、テストも存在した状態で、遅そうなところが特定のクラスのインスタンスの生成であるという仮説が立った時に初めてメモ化なりをしよう。無意味に最適化されたコードは、書くときは楽しいけれど、あとから後悔する。

例えば、シングルトンにメモされているオブジェクトへの参照が、増え続けるならばそれはメモリリークである。ガベージコレクタによって我々はメモリ管理から開放されたと思ったのに、メモ化という凝ったことをすると再びメモリリークの危険性に怯えなければならなくなる。

解決策としては、弱参照かソフト参照を利用することが考えられる。Javaの参照には

  • 強参照(普通の参照)
  • ソフト参照
  • 弱参照

という3種類がある。*1メモを強参照で持ったら、GCの対象にならない。しかし、ソフト参照か弱参照でオブジェクトをメモしていたら、そのオブジェクトを強参照しているオブジェクト(つまり使用中のオブジェクト)がいなければ、メモはGCの回収対象になる。

ソフト参照の方が弱参照より参照具合が強くて、ソフト参照はGCが動いても必ず回収されるとは限らない。ソフト参照は、メモリが非常に足りないときには回収されて、回収されるかどうかの判断はGC任せとなる。オブジェクトの生成が重いという場合はソフト参照を使うのがよいだろう。

弱参照はGCが動いたら回収される。オブジェクトを共有してメモリを節約したいという目的ならば弱参照を使うのがよいと思われる。

どのように実装するかはこんど考えてみることにする。

参考

*1:他にファントム参照ってのがあるらしいが、よう分からん

抽象クラスとは

今日は抽象クラスについて考えてみる。

抽象クラスとは、必ず継承して使わなければならないクラスのことである。ある抽象クラスAに属していて、Aのサブクラスに属していないようなオブジェクトはいない。

定義としてはただこれだけなのだが、一体何に使うのだろうか。

1つは置換可能性を保証するためである。リスコフの置換原則では、サブクラスはスーパークラスと置換可能でなければならないとする。

しかし、スーパークラスメソッドをオーバーライドしたときに置換可能性を満たせるのか。既に実装が存在して、その振る舞いを変えずに新しい実装に置き換えるなんてことは難しい。振る舞いを記述したものが実装だからだ。普通は実装を変えたら振る舞いも変わる。オーバライドする人は気を使わなければならない。

ならば、オーバーライドされることが分かっているメソッドは空で実装なんてなければよい。つまり、abstractメソッドにする。何もなければ、どんな実装に置き換えても振る舞いが変わったことにはならない。

抽象クラスは、abstractメソッドになっているところを任意に置き換えることができことを保証するわけだ。これによって、オブジェクトのメソッドを付け替えて多様な振る舞いをさせようという、いわゆるポリモーフィズムが実現できる。

f:id:fjkz:20160416171033p:plain


もう1つの抽象クラスの使い方は実装を共通化するためである。

これが、インターフェイスとの違いだ。インターフェイスは全てがabstractだが、抽象クラスは実装が持てる。

サブクラスで共通の実装を持つメソッドを抽象クラスで実装しておけば、サブクラス間で部品を共通化することができる。

例えば、Java標準ライブラリのAbstractMapクラスはMapインターフェイスを実装するための部品を共通化している。

f:id:fjkz:20160416152550p:plain

これ見ると、Mapインターフェイスは要るのかと疑問に思うけれども、この場合は要る。Mapインターフェイスの実装はAbstractMapのサブクラスだけではないからだ。Mapインターフェイスを実装するのに便利な部品として、AbstractMapが用意されているだけである。

ただ、部品を共通化する目的であれば、継承ツリーの中で頑張るのではなくて、共通部品を別のクラスに切り出した方が良い場合の方が多いのではと思っている。

インターフェイスとは

前回(クラスとは)はclassとは何かを考えてみたので、今日はinterfaceについて考えてみる。

インターフェイスとは、オブジェクトのに付与される性質の1つである。

クラスとは、同じ性質を持ったオブジェクトを分類 (classification) したものです。同じメソッドを持っていて、それらが似たような振る舞いをするオブジェクトが同じクラスに属していると言われます。

クラスに属するオブジェクトが共通して持っている性質は同じメソッドをもっているだけではありません。同じインターフェイスを実装しているという性質も共通しています。同じインターフェイスを実装すれば、結果として同じ名前のメソッドを持つことになりますが、同じインターフェイスを持つということの方が狭い意味になります。ただのメソッドよりインターフェイスを実装しているメソッドの方が特殊ということです。

Car, Fugitive(逃亡者), Procedureというクラスがあって、どれも run() というメソッドを持っているとしましょう。これらのメソッドは同じ run という名前ですが、クラス名から考えると全く別の意味になるでしょう。

f:id:fjkz:20160414193246p:plain

メソッドの意味は文脈で決まります。クラス名から推測すると、Car::run() は「走る」、Fugitive::run() は「逃げる」、Procedure::run() は「実行される」という意味になるかと思う。

これらのメソッドが Runnable インターフェイスを実装していたら、全く意味合いが異なってくる。runメソッドの処理がスレッドに乗せて実行できるという意味になる。(不自然極まりないが)

f:id:fjkz:20160414193840p:plain

このようにインターフェイスメソッドに文脈を与えることできる。たとえ違うクラスのメソッドであっても、それらが同じインターフェイスメソッドであれば、同じ意味になる。

インターフェイスとは、クラス間で共通した意味合いのあるメソッドの集合のことである。インターフェイスメソッドが1つの場合もあるし、複数の場合もある。

ダック・タイピングができるプログラミング言語では、Runnable インターフェイスを継承していなくても、Car, Fugitive, Procedureのrun メソッドは同じ意味とみなされる。意味が違うものにたまたま同じ名前がついていても、同じ意味として実行されてしまう。間違った使い方をしていないかどうかは使う側が気をつけるなければならない。

Javaの場合は、異なるクラスの間でも共通の意味のあるメソッドインターフェイスとすることで、かちっとした文脈を与える。プログラム上での文脈だとか意味だとかはしばしば「型」と呼ばれる。

メソッドを持たないインターフェイスというものもある。例えば、ClonableとかSerializableとかだ。これらも、メソッドはないけれども、オブジェクトに性質を付与するものだ。*1

クラスは同じ性質をもつオブジェクトの分類だが、インターフェイスとはオブジェクトの性質である。抽象クラスはクラスだ。したがって、抽象クラスとインターフェイスは、使い方は似ているが、使い道や意味が全く異なるものであるといえる。

では、インターフェイスがオブジェクトの性質で、クラスが同じ性質を持つオブジェクトと集まりというならば、同じインターフェイスを持っているオブジェクトは同じクラスであると言えるのではないかという疑問も湧いてくる。

我々の住む世界ではそれは可能だ。青い車、青い空、青い鳥といった異なるクラスに属するものを集めてきて、青いものという別のクラスをつくることはできる。

しかし、プログラムの世界ではそれはできない。1つの理由は単にJavaが多重継承を認めていないという理由だ。もう1つの理由は前回書いたが、プログラムの世界は似たようなオブジェクトを集めて分類してクラスを作るのではなく、クラスが先にあるからだ。あとから分類することは無理なのです。

青いものクラスの世界をつくるべきなのか、車クラスの世界をつくるべきなのかは「場合による」。それを決めるのが設計なのです。

*1:アノテーションがないころの名残なので、今インターフェイスをこのように使ってはいけない。