カレーなる辛口Javaな転職日記 RSSフィード

2018年 10/24

憂鬱本の継承者

オブジェクト指向を一億万ぱーせんと理解する記事だ.*1 *2

たのむからやめてくれ.目が腐る.

「継承」「カプセル化」「ポリモーフィズム」。オブジェクト指向の3大要素と言われていますが、そんなことより大事な事がオブジェクト指向にはあります。

「現実世界を正しく捉える」のがオブジェクト指向を言われていますが、抽象化し過ぎているし現実世界を正しく捉えなければオブジェクト指向を使う意味がないかと言われるとそんなこと全くありません、ですが現実正解を正しく捉える考え方をするとオブジェクト指向プログラミングが容易になるのは正しいと思います。なので7割不正解です。

ああ,これもあの歴史的悪書,憂鬱本流のなんちゃってオブジェクト指向に洗脳された人なのね.

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

凄まじい悪書です.大事なことなので二回言いました.


簡単な例で解説しますので、今回は「タケシが持っている冷凍ギョウザを電子レンジに入れたら食べられるギョウザとなって出てくる」という例で解説したいと思います。

その「簡単な例」とやらが,一体なんの例えになってるというのだ.過去の失敗から何も学んでない.

よくある間違いが、「冷凍可能な食べ物」もしくは「食べ物」をインターフェースで定義することです。このような間違いは次のように見るとどっちが正しいのかわかります。

「冷凍可能な食べ物」は「もの」なのか「機能」なのかです。「機能」だったらインターフェースです。

言うまでもなく、「もの」ですのでこれはインターフェースではありません。

「冷凍可能」は「機能」なのでインターフェースです。

ネタで言ってるんだよな?

絵に描いたように典型的なダメオブジェクト指向の悪臭がぷんぷん.*3


これって典型的なインターフェースの乱用なのよね.可読性や保守性を損ねることはなはだしい.これを多用するとIDE頼みの初心者は手も足も出ないし,熟練者でも工数が数十倍以上になるのはザラ.しかも影響範囲が大きすぎてリファクタリングも困難.一度コードを全部破棄して最初からやりなおす他なくなる.

もし抽象クラスで解決しようと考えた場合、継承だらけでカオスなことになるのは目に見えています。

インターフェースは柔軟性に非常に優れているのです。

これもウソだなあ.

インターフェースを上手に使えば柔軟性はあがるが,上記のような指針で使うなら地獄の門が開く.抽象クラスの方がまだマシだ.GoFもリスコフの置換原則も理解できてないし実践できない初心者は,インターフェースや抽象クラスには手を出さない方が無難だろう.APIで提供された物を使うのはいいけど,自分で書くのは10年早い.*4


オブジェクト指向設計実践ガイド ?Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方

「インターフェース」と一口に言っても,さまざまな概念が山ほどあります.ここでのインターフェースとは,クラス「内」にあるようなインターフェースです.クラスはメソッドを実装し,そのうちいくつかは他のオブジェクトから使われることが意図されています.それらのメソッドがそのクラスのパブリックインターフェースを構成します.

実際,パブリックメソッドは、責任の説明として解釈できるものであるべきです.パブリックインターフェースは,クラスの責任を明確に述べる契約書なのです.

分かってる人の感想は「うんうん」「せやな」くらいだが,初心者だとわけわからんと思う.



例が具体的でとても分かりやすかったです。

『あらゆる変更に耐えられるように突き詰めると手間が上回って逆にデメリットにもなる』について、普段、感じていたモヤモヤが言葉になってスッキリしました(´ω`*)

https://qiita.com/yoship1639/items/5878bf9d64816e93610b#comment-9e5dc3174a92b7abc8cb

まさに憂鬱本のレビューを見るかのようだ.

「具体的で分かりやすい」けど,分かった気になってるだけで1000%間違いだから!*5


こうやって不幸のお裾分けは続くのか.


今回は例題が「タケシが持っている冷凍ギョウザを電子レンジに入れたら食べられるギョウザとなって出てくる」プログラムなので、継承やインターフェースを使う必要がないシチュエーションですね。結果、「恩恵がほとんどないバージョン」がベストな実装だと思いました。

https://qiita.com/yoship1639/items/5878bf9d64816e93610b#comment-fdab62544a5cc13ba754

するどい指摘.*6


作者の「ゲームエンジンを用いない個人ゲーム開発」という経歴も,OOPを理解する妨げになってのかも.そのくらいなら「敵機オブジェクト」「自機オブジェクト」「ビームオブジェクト」「衝突判定インターフェース」みたいに作っても,まあそれなりに動くから.*7 自作なら元請の仕様変更に振り回されることもない.同じゲームばかりしてると飽きるから,同じソースを十年も二十年も保守し続けることもないだろう.

まさにこんな感じ.

「ゲームソフト開発を題材にしたオブジェクト指向入門」

  • あらゆるモノの名前を抽出することが、オブジェクト指向の第1歩です。
  • 抽象度の高いクラスは汎用性も高いので、様々なソフトに採用することができます!『相手を想って様々なところで使える”共有部品”を作りたい!』と考えるのは素敵なことです!
  • ただし「飛ぶ」は動詞なので、オブジェクト指向の考え方… つまり『オブジェクト(名詞)からクラス(部品)を考える!』というコンセプトが崩れちゃう!
  • 『読み手のことを想ってUML(ソフトウェア設計図)を書くのが正解』
https://cpp-learning.com/object-oriented/

うへえ,絵に描いたような憂鬱本系なんちゃってオブジェクト指向だ.

今世紀に入った後も,なぜかこういう失敗パターンが何度もくり返し見られたのだ.憂鬱本は本当に害悪にしかならん.



「現実世界の物をクラスにするオブジェクト指向」は非常に筋が悪い.たしかにそのやり方でも小規模アプリなら問題なく作ることができるが,それはオブジェクト指向を導入する前のやり方から,何一つ進歩してないのだ.むしろ一手間増えるために,工数や品質にしわ寄せがいく. *8 *9 *10


口直し.

http://d.hatena.ne.jp/JavaBlack/20180617/p1

いずれも具体的なソースコードをまじえてOOPを説明する名著.


それなのよな.

「現実世界の物をオブジェクトにする」方式だと,FileReaderとかBufferdReaderとかIterator やObserverみたいなオブジェクトは,まずお目にかかれないのだ.でもOOPの世界では,今じゃこういうのは基本テクニックの一つになってる.そういうOOPの根幹をなす優れた設計に到達できないのが,現実世界ベースの設計手法の数多くある欠点の一つだ.OOPを使いこなしたければ,まずは現実世界の固定観念を捨て去ることだ.Metz本はそのための指針を指し示すもので,GoF本はそのためのうってつけの練習課題になる.


むしろ「物をオブジェクトやクラスにするのが良い」という考え方は,随分前に廃れたと思ってたのだがな.おそらく80年代か90年代前半にはそういう話になってて,GoFがトドメを刺したかたち.*11 それ以降に出た憂鬱本が,なにゆえ10年前にタイムスリップしたのが不思議なくらいだ.*12


よくある失敗パターンと初心者への助言

たとえば,この辺が失敗パターンかな.

  1. プログラミング初心者:大規模で複雑なコードを読んでも理解できない.OOPによってコードがシンプルになっても,モデリングでコードがスパゲッティになっても,それが理解できない.だから「現実世界の物をモデリングするのがオブジェクト指向」というウソがウソと見抜けない.→ オブジェクト指向は糞の役にも立たない.むしろ害ばっか.
  2. 小さくて単純なアプリしか作らない: オブジェクト指向を使う必用がない.ムリして使うとかえって品質が下がる.→ オブジェクト指向は糞の役にも立たない.
  3. プログラミングスキルが低い:大規模なコードは書かない/書けない.ムリして書いても,大規模化・複雑化してオブジェクト指向がいるようになる前にスパゲッティになって丸ごと廃棄するしかなくなる.→ オブジェクト指向は糞の役にも立たない.
  4. 非OOPのプログラミングだけは上達したけど,オブジェクト指向を現実世界のモデル化だと思ってる:間違ったオブジェクト指向のせいで,スパゲッティ化してコードの品質が下がる.→ オブジェクト指向は糞の役にも立たない.*13

初心者はまずは4を目指して,プログラミングの基礎を固めるべき.初心者でもMets本やGoF本を読めばOOPの知識だけならたたき込めるけど,それだけでは実践できないし,何の役にも立たないよ.


これのたとえで「変化球の打ち方ばかり覚えたがる,野球の初心者」というのを思いついた.

まだ野球を始めたばかりで,普通に投げられたストレートのボールでも振り遅れて三振するレベルなのに,なぜか「一流のバッターなら変化球も打ち返せますよね!おれ変化球を打つのだけは誰にも負けたくないんです!」と言ってる超初心者のようなもの.

そういう初心者に対して「まずはストレートを打ち返せるようになれ.変化球の打ち方を覚えるのはその後だ.」と助言するのは,そんなにおかしな話だろうか?変化球の打ち方も普通のボールを打つテクニックの延長にあるのであって,普通のストレートが打てない人に教えてもあまり意味がない.


それから「オブジェクト指向のプログラミングテクニック」と「オブジェクト指向言語を使ったプログラミング」は区別した方がいい.

初心者にはなぜか「オブジェクト指向言語を使うならオブジェクト指向的に書かなければいけない!」という強迫観念があるみたいだけど,そんなことは全然ないから.「理屈はわからないけど,プログラミング言語Javaに書かれてるやり方で書いてる」くらいで十分まともだ.Effective Javaも読んで実践してたらなおよし.初心者が気をつけるべきポイントはOOPの他にも山ほどあり,そっちの方が優先度は高い.

Effective Java 第3版

Effective Java 第3版

そうやって経験を積んで,色んな優れたコードを読んで,設計を見て,少しずつ上達していく.その上で非オブジェクト指向的なやり方ではどうも上手く行かないという壁にぶつかって,初めてGoFがその問題をエレガントに解決する答だと気付いて目から鱗が落ちて一回り成長する.OOPってのはそういうものだと思う.


「「オブジェクト指向とは、現実世界を正しく捉えること」という理解はデメリットのほうが大きい」

https://qiita.com/maueki/items/1176980026c18a6f093f

まとまっていたので引用.そういえばこの本に書いてあったんだっけ.

*14

この話題の結論としてBobおじさんは次のように述べている。

モデルの正当性は立場によって異なり、立場抜きに正当性を証明することは意味をなさない

ここでいう「モデルの正当性」は「設計の正しさ」だと思って欲しい。

つまり、どういうシステムをつくるのか、どういう問題を解決するのかという「立場」によって正しい設計というのは異なるものであり、例に上げた「正方形 is a 長方形」のような「現実世界の真実」というのは数ある「立場」の中の1つに過ぎないのであり、その「立場」はこの例では適切ではなかったということなのだ。

オブジェクト指向とは、現実世界を正しく捉えること

この文言は危険である。というのは、上の文章を提示し「立場」を明確にしないままクラスを設計してみせる、というような行為は初心者に「立場から独立した正しいクラス設計」が存在するものと誤解させてしまう。そのようなクラス設計は不具合を発生させる可能性を高め、また「現実世界を正しく捉える」という観点で設計の正しさについて議論することは私が実体験したように開発現場に混乱をもたらす恐れがあるのである。

これらは極めて基礎的で重要なこと.それもあって現実世界の者をオブジェクトにする方法は完全に廃れた.


こっちの本だと「act-like-as」という基準を提案してたとおもうけど,いずれにせよ「is-a関係なら継承.has-a関係なら委譲」みたいな基本方針は間違いであることが既に知られている.だから今じゃ完全に廃れて,ほとんど死語だと思う.


まれに「初心者向けの説明だから,分かりやすさのために正確性が犠牲にされるのもやむを得ない」みたいなことを言う人もいるけれど,「現実世界の物をモデル化」というのは,あまりに危険な間違いなので初心者向けにしても悪影響が大きすぎるのだ.

  • OOPが不要な簡単なプログラムの場合: モデリングしても書けなくはないが,従来通りの手法より若干悪化する.そもそも従来型で問題なく書ける.
  • OOPが必用となる複雑なプログラムの場合: 「現実世界のモデリング」は従来型より品質が激しく悪化.最悪ではスパゲッティ化してプロジェクトが破綻する.

そして初心者はその判断ができない.気がついたときには既に迷路に迷い込んでて脱出不可能.あなたはこの世の地獄を見ることになる.


もはや「物を使った例え」は,クラスとインスタンスの概念を説明する時くらいにしか使い道がないだろう.

*1:しかもこんなのが多数の「いいね」を集めてる.また同じ失敗をくり返すつもりなのか.

*2:「オブジェクト指向を5000%理解するシリーズ」とでも呼ぼうか. http://d.hatena.ne.jp/JavaBlack/20180929/p1

Rubyを256+倍使うための本 場外乱闘編

Rubyを256+倍使うための本 場外乱闘編

256倍シリーズなんて目じゃないぞ.

*3: ほんっとにこんな気分.「こいつはくせえッー!ゲロ以下のにおいがプンプンするぜッーーーーッ!!」 https://dic.nicovideo.jp/a/%E3%81%93%E3%81%84%E3%81%A4%E3%81%AF%E3%81%8F%E3%81%9B%E3%81%88%E3%83%83%E3%83%BC%21%E3%82%B2%E3%83%AD%E4%BB%A5%E4%B8%8B%E3%81%AE%E3%81%AB%E3%81%8A%E3%81%84%E3%81%8C%E3%83%97%E3%83%B3%E3%83%97%E3%83%B3%E3%81%99%E3%82%8B%E3%81%9C%E3%83%83%E3%83%BC%E3%83%BC%E3%83%BC%E3%83%BC%E3%83%83%21%21

*4:10年はいいすぎか.5年くらいだな.

*5:「努力・友情・勝利っていわれても,抽象的でなにをしていいかよくわかりません.」「努力とは鉄下駄履いて兎跳びした回数.友情とは拾ってやった戦友の骨の数.勝利とは貴様がその手で殺した敵の数だ.」「具体的で分かりやすいです!目から鱗が落ちました!!」
抽象概念を具体的に言いかえるだけなら簡単なのよ.間違いで良ければ.

*6:コーナーが全く無い直線道路で自動運転車のスピード競争すれば,ブレーキを外してエンジンパワーに集中させて,速度制御機能を外した車が優勝するよ.不適切な問題設定は,不適切な結果を招いてしまう.

*7:「犬がワン」だの「社長が起立」だの.

*8:素直に実装すれば,敵機や自機の一機ごとに,それを保持するデータ構造をもち,それを処理する関数を用意して,敵機と自機ごとにグループ化して処理する以外のやり方があろうか.構造体をインスタンスにかえ,mallocをnewと呼び変えたところで,プログラマ視点では大差ない.

*9:初心者は「物をクラスにする」という説明を簡単に受け入れるが熟練者は鼻で笑うのには,従来型のプログラミング技術を知ってるかどうかの差が大きい気がする.初心者は「モヤモヤがスッキリした」と思うけど,(非OOPの)熟練者からは「それじゃ今までと同じ」「物をクラスにするオブジェクト指向なんてクソの役にも立ちません!」ということになるから.

*10:日本におけるオブジェクト指向の有効性に関する議論の半分が,現実世界ベースのモデリングに費やされてるような気もする.なんという時間の無駄.
プログラミング初心者「(現実世界ベースで)目から鱗が落ちました!」 Staticおじさん「(現実世界ベースなら)手続き型で書いても同じ.糞の役にもたたん」.
それどっちも間違いだから.せめてGoFまで理解できれば話はすすむんだが,それには一通りのプログラミング経験が必用.そして日本ではプログラマほど軽視されてる職業はないわけで,理解できるくらい経験を積む頃には大半が引退して管理職になると.

*11:おそらく90年頃には既に「現実世界をそのままモデル化してオブジェクトにするのは筋が悪い」という認識はあったけど,「じゃあどうすればいいのか?」に対する答がまだ示されてなかった.それに対して「こうすればいいんだ」という答を叩きつけたのが,GoFのデザインパターンだったと認識している.GoFによってOOPの世界は塗り替えられた.

*12:憂鬱本がなんと1998年という驚愕の事実.

GoFが94年末には出版されてるというのに.

*13:一部の熟練Cプログラマなどに見られるのがコレ.正しい使い方さえ教えれば,一番成長するのもここだろう.

*14:旧版:

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。

トラックバック - http://d.hatena.ne.jp/JavaBlack/20181024/p1