これは「オブジェクト指向」がよくわかってない人の書いたポエムである。
そういうのが嫌いな人はお帰りください。
はじめに
リンクは貼らないが「オブジェクト指向の本質とは現実を正しく捉えること」と書かれている記事(以下、元記事)がバズった。
私は正直「オブジェクト指向」の何たるかを理解しているとは言い難い。
しかし、そんな私でも元記事がいくつかの点でおかしい、もっと厳しくいうと開発現場に混乱をもたらす可能性を持っていることは理解できる。そこでこの記事では「オブジェクト指向とは〇〇である」という言及は行わずに、元記事の問題点を指摘するに留める。
長方形と正方形の例
オブジェクト指向プログラミングと現実世界の話というとBobおじさんが『アジャイルソフトウェア開発の奥義』に書いた正方形と長方形の話が有名だ。
話は簡単だ。「正方形クラスは長方形クラスを継承するべきか?」というものだ。
少しだけ詳しく見てみよう。
長方形クラスRectangle
がある。1
class Rectangle
{
public:
virtual void SetWidth(double w) {itsWidth = w;}
virtual void SetHeight(double h) {itsHeight = h;}
double GetHeight() const {return itsHeight;}
double GetWidth() const {return itsWidth;}
double Area() const {return itsHeight * itsWidth;}
private:
Point itsTopLeft;
double itsHeight;
double itsWidth;
};
このクラスを継承して正方形クラスSquare
を作ることにした。
正方形が長方形であるつまり「正方形 is a 長方形」であるということは、小学校で習う「事実」であり、正方形クラスが長方形クラスを継承することは「正しい設計」のはずだ。
正方形はご存知の通り縦と横の辺の長さが等しい長方形だ。そこでSquare
のメソッドは次のようになるだろう
void Square::SetWidth(double w) {
Rectangle::SetWidth(w);
Rectangle::SetHeight(w);
}
void Square::SetHeight(double h) {
Rectangle::SetHeight(h);
Rectangle::SetWidth(h);
}
ところでSquare
を追加する前に以下のようなコードがあったとしよう
void g(Rectangle& r) {
r.SetWidth(5);
r.SetHeight(4);
assert(r.Area() == 20);
}
Square
をこの関数g()
に渡せば落ちてしまうことがわかるだろう。つまり以前は動いていたコードをSquare
の追加で壊してしまったわけだ。この例ではassert()
があるためすぐ問題に気づくことができるが、実際には非常にわかりにくいバグを生んでしまうおそれがある。
なにがいけなかったのだろうか?
SOLID原則2を知っている人はリスコフの置換原則に違反していると指摘するだろう。つまり「正方形 is a 長方形」という考え方がまずかったのだ。
でもちょっとまってくれ、冒頭でも述べたように「正方形 is a 長方形」は小学校でならう「現実世界における事実」ではなかったのか?
Bobおじさんの結論
この話題の結論としてBobおじさんは次のように述べている。
モデルの正当性は立場によって異なり、立場抜きに正当性を証明することは意味をなさない
ここでいう「モデルの正当性」は「設計の正しさ」だと思って欲しい。
つまり、**どういうシステムをつくるのか、どういう問題を解決するのかという「立場」**によって正しい設計というのは異なるものであり、例に上げた「正方形 is a 長方形」のような「現実世界の真実」というのは数ある「立場」の中の1つに過ぎないのであり、その「立場」はこの例では適切ではなかったということなのだ。
「立場」を明確にせずクラス設計するということ
元記事にもあったが、一部の「オブジェクト指向」の入門書で見られるのが現実世界にある物(車だったり動物だったり)を「立場」を明確にせずクラス設計してみる、というものだ。
上にあげたBobおじさんの言葉に従えば、そうやって作られたクラスが正しい設計なのかどうかは判断しようがないことになる。
「立場」を明確にせずにクラス設計をするとどうなるか、例えば元記事にある電子レンジでいうと「設計者が考える「万人が共通で考える抽象化された電子レンジ」」という大変曖昧でふわふわしたものに立脚したクラス設計になるわけである。
このような設計について複数人で議論するのは困難である。私は実際にそのような現場に遭遇したことがあるが[^Carthago delenda est]、各個人が考える「万人が共通で考える抽象化された○○」なんてものは全然一致なんかしないのである。しかもBobおじさんのいうとおり正しさを判断することに意味は無いわけで、私がその現場で大変無駄で苦痛な時間を過ごしたことはいうまでもない。
「オブジェクト指向とは、現実世界を正しく捉えること」という理解はデメリットのほうが大きい
ここで表題を回収しようと思う。
オブジェクト指向とは、現実世界を正しく捉えること
この文言は危険である。というのは、上の文章を提示し「立場」を明確にしないままクラスを設計してみせる、というような行為は初心者に「立場から独立した正しいクラス設計」が存在するものと誤解させてしまう。そのようなクラス設計は不具合を発生させる可能性を高め、また「現実世界を正しく捉える」という観点で設計の正しさについて議論することは私が実体験したように開発現場に混乱をもたらす恐れがあるのである。
おわりに
読んでくれた方には申しわけないがQiitaの初心者向けの記事はこの記事を含めとにかく地雷が多いので入門に際してはいわゆる名著と呼ばれている書籍を購入して読むことを強く推奨する。
もしあなたが「リスコフの置換原則とか初めて聞いた」というレベルなのであればまずは無料で公開されている『プログラマが知るべき97のこと』あたりから読んでみることをオススメする。