オブジェクト指向で,どのように作るのか

限界まで技術的負債が重なった糞コード

この本の題材は,オブジェクト指向ソフトウェアの設計です。アカデミックな重い本ではなく,1人のプログラマーが書いた,コードをいかに書くかという話です。今日の生産性が翌月も翌年も持続するようなソフトウェアをいかに構成するかを教えます。

http://gihyo.jp/book/2016/978-4-7741-8361-9

とりあえず,初心者はコレを読めってことになる.*1 *2
ここで紹介されてるものは,一冊の本でまとまって紹介されることは滅多にないけど,とても基本的で重要な技術.OOPプログラマーは,こういう視点から世界を見てコードを書く.こういうことを知らないOOPプログラマーはモグリ.


いままでのあらすじ.

  1. 昔,オブジェクト指向でなぜつくるのか 第2版がオススメしてる人がいた.
  2. 自分はそうは思わない.
  3. 質問:じゃあどの本を読めば良いんですか?
  4. えっと,いまだと オブジェクト指向設計実践ガイド ?Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 が良さそうな気がする.
http://d.hatena.ne.jp/JavaBlack/20080727/p1

今回それをうけて内容を確認してみた.

まだ軽く目を通しただけだけど,どうやら思った通りの本だったので一安心.

第1章 オブジェクト指向設計
第2章 単一責任のクラスを設計する
第3章 依存関係を管理する
第4章 柔軟なインターフェースをつくる
第5章 ダックタイピングでコストを削減する
第6章 継承によって振る舞いを獲得する
第7章 モジュールでロールの振る舞いを共有する
第8章 コンポジションでオブジェクトを組み合わせる
第9章 費用対効果の高いテストを設計する

http://gihyo.jp/book/2016/978-4-7741-8361-9#toc

この目次を見れば,分かる人ならだいたいわかると思う.逆に言えば,この目次を見てピンと来ない人は基本が欠けているので,念のためこの本をチェックしておいた方がいい.


一方でこちらが「オブジェクト指向でなぜ作るのか」の目次.

第1章 オブジェクト指向はソフトウエア開発を楽にする技術
第2章 オブジェクト指向と現実世界は似て非なるもの
第3章 OOPを理解する近道はプログラミング言語の歴史にあり
第4章 OOPは無駄を省いて整理整頓するプログラミング言語
第5章 メモリの仕組みの理解はプログラマのたしなみ
第6章 OOPがもたらしたソフトウエアとアイデアの再利用
第7章 汎用の整理術に化けたオブジェクト指向
第8章 UMLは形のないソフトウエアを見る道具
第9章 現実世界とソフトウエアのギャップを埋めるモデリング
第10章 擬人化して役割分担させるオブジェクト指向設計
第11章 オブジェクト指向から生まれたアジャイル開発とTDD
第12章 オブジェクト指向を使いこなそう
第13章 関数型言語でなぜつくるのか

これら二冊の目次を比較すると,視点が大きく異なることに初心者は驚かれるかもしれない.



記述言語はRubyだけど,PythonJavaなど他のOOP言語プログラマーでも問題ないと思う.とはいえ他言語のプログラマでも,基本的なRubyの文法くらいは読めた方が良いだろう.


それ以上に重要なのは,OOP言語を使ってプログラムを開発し,思ったようにできなかった失敗経験だと思う.(下記の「二郎チョモランマ」参照.) 書いている内容自体は難しいわけではないけれど,そういう経験のない人がコレを読んでも,何が言いたいのかイマイチしっくりこないで消化不良になるだろう.このあたりのことを考えない人が書いたコードは,往々にして修正困難で加筆するごとに技術的負債が雪だるま式に増えていき,最後に残るのは誰もメンテできない糞コードになりがち.まさに開発者の悪夢.*3

そのため,ソフトウェアがうまくいかないと,とりわけ困ってしまいます。質の悪いソフトウェアはプログラマーの目的を阻害し,また,プログラマーの幸福を妨げます。かつての生産性は損なわれ,速かったものは遅くなり,満足していた心は不満でいっぱいになるでしょう。

この不満は,物事を終わらせるためのコストが高くなりすぎたときに現れます。私たちの内面にある計算機は常に動いており,達成した総量とそこに費やされた努力の総量を絶えず比較しています。仕事のコストがその価値を上回れば,努力が無駄になったように感じます。プログラミングが楽しいとすれば,それは自身を有能にしてくれるからです。反対に,苦痛であるときは,自分はもっとできる,いや,すべきであると感じているのではないでしょうか。プログラマーの喜びは仕事の足取り次第です。

http://gihyo.jp/book/2016/978-4-7741-8361-9

そして経営者にとっても悪夢だ.

バグの修正には何時間も何日もかかり、修正や機能の変更には数日から丸々1週間を費やし、この停滞が速く仕事を進めたいと思う他の関係者を足止めし、あなたは技術的負債を解消する時間を取ることもできず、痛みが悪化するだけという悪循環に陥る。言うまでもないが、この悪循環はしばしば死のスパイラルになり得る。

最初の「最小限の実行可能な製品」は完璧にエレガントでスケーラブルであることはなく、またその必要もないというのは事実だ。しかし、もしそれが泥沼ソフトウェアで、あなたが人々が望むものを構築仕損なっていた場合には、対応はとても困難で遅々として進まず、目標を変えるのも高価についてしまうのだ。一方高品質のソフトウェアを持つ競争相手や新規参入者たちは次々と素早く取捨選択を進めていける。

https://jp.techcrunch.com/2016/07/25/20160724how-much-does-it-matter-if-your-software-quality-sucks/

川上 4年前にニコ動の開発が行き詰まったんです。「変更に変更を重ねてきたけど、これ以上は無理だ、何の機能追加もせずつくりなおしたい、2年間は何も変えずに現状維持にしたい」と開発の責任者が言ってきた。

 そう言われて困ったんですよ。2年間新しいサービスが出せなければウェブサービスとしてニコ動に本当の寿命が来るだろうと。どうしようかを悩んだすえに決めたのが、世界で誰もやってない「イベントでごまかす」という作戦です。それがまさかの大成功をしたんですよ。

http://ascii.jp/elem/000/001/076/1076171/

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


Javaの場合だと,順番的には

  1. プログラミング言語Java
  2. Effective Java
  3. オブジェクト指向設計実践ガイド
  4. デザインパターン(GoF)

くらいかな?

Effective Javaの前に目を通してもいいけど,いずれにせよ失敗経験がないと,理解するのは困難だと思う.*4



なお原書はコレ.

Practical Object-Oriented Design in Ruby: An Agile Primer (Addison-Wesley Professional Ruby Series)

Practical Object-Oriented Design in Ruby: An Agile Primer (Addison-Wesley Professional Ruby Series)

https://www.amazon.com/dp/B0096BYG7C/

原書だと第二版も近日発売予定.

Practical Object-Oriented Design: An Agile Primer Using Ruby (2nd Edition)

Practical Object-Oriented Design: An Agile Primer Using Ruby (2nd Edition)


初版が2012年か.やっぱ日本は完全に周回遅れだな.

オブジェクト指向は、ラーメン二郎チョモランマではなく握り寿司を目指せ

なんとなく素人の書く糞オブジェクト指向設計をどのように伝えたらわかるか考えてたけど,ラーメンとにぎり寿司でたとえるのがいいような気がしてきた.それも二郎系で代表される、山ほどモヤシやチャーシューやその他いろんな具がマシマシで載ってて,これ以上積むこともできなければ山を崩さずに下から抜くこともできないような奴.

ダメなオブジェクト指向: 二郎チョモランマ モヤシ マシマシ マシマシ型

世界一美味しい「どん二郎」の作り方 誰も思いつかなかった激ウマ! B級フードレシピ

  • 継承を山盛りラーメンの「トッピング」だと思ってる. *5 *6
  • 機能を追加する時は,基本となるラーメンに,モヤシやチャーシューやシナチクを,ひたすら限界まで積み上げていく.*7
  • 最初は良いけれど,積めば積むほどそれ以上積むのが難しくなっていく.
  • 一度積んだが最後,外すことも順序を入れ替えることもできない.*8
  • 食べるときは原則として上から.下の物を取ったり追加したりするには,一度山を崩す必用がある.*9
  • 嫌いな具があっても,それだけを除去するのも難しい.毒を食らわば皿まで.*10

このような素人目にもヤバげな「オブジェクト指向」プログラムは,決して架空の話ではない.既に日本に実在している現実の悪夢だ.*11 良いプログラマは「チャーシュー麺ニンニク多め」くらいでやめておく.それ以上追加する場合はリファクタリングしてから.ダメなプログラマは全部そのまま上に載せる.最初はいいが,載せれば載せるほど,どんどん保守性が落ちていく.限界まで積んでもう積めなくなると,「動きません」と騒ぎ出すが後の祭り.


追記:ミノ駆動さんの「クソコード動画」シリーズも参考になる.よくあることなんだ.
https://twitter.com/MinoDriven/status/1157554468201746432

良いオブジェクト指向: にぎり寿司型

現代すし技術教本 江戸前ずし編~基本技術から名店・繁盛店の技術まで すしの技術大全

  • 継承は最小限にして,基本インスタンスの組み合わせで多様な注文に応じる.*12
  • 一つの握り寿司に一つの具材.*13
  • 個々のにぎり寿司は独立している.
  • 好きな物から一個単位で任意の順序で任意の種類を選択できる.嫌いな具があれば口にしなくても良い.*14
  • 醤油やワサビは食べる直前に付ける.*15
  • 並べる順序なども変更可能.
  • 人数が増えれば,寿司桶を増やすだけでいくらでも注文を追加できる.にぎり寿司一個単位の追加注文も可.*16


あくまで例え話だけど,だいたいのイメージは伝わるだろうか.*17


過去に二郎チョモランマを書いた人,読んだ人,デバッグさせられた人は,このたとえを見て過去のトラウマを刺激されるかもしれない.そして死ぬほど苦労した理由が,決してその言語やオブジェクト指向の欠陥(或いはあなたのスキル)などではなく,ただ単に最初に作った人がオブジェクト指向の使い方を完全に間違っていただけだと知ったら,あの苦労はいったい何だったのかと思わないだろうか?

これはそういう人たちのための入門書なのだ.

*1:出た時に一応チェックしてたのに,Ruby本なこともあってそのまますっかり忘れてた. http://d.hatena.ne.jp/JavaBlack/20160905

*2:作者は Metz であって Matz じゃないので注意.「有名なRubyのMetz本」なんて書き方をされたら,本気で間違いそうな気がする.

*3: SIer的な,納品後は「後は野となれ山となれ」のオレは関係ねえ型ビジネスだと,そういう保守性なんてのは最も軽んじられる部分になる.だってそういう契約なんだし.

*4:あのリストも加筆修正した方がよさそうだな. http://d.hatena.ne.jp/JavaBlack/20070825/p1

*5: 「いまさら聞けないJavaによるオブジェクト指向の常識 」 http://www.atmarkit.co.jp/ait/articles/0805/08/news152_3.html

*6:トッピングのたとえは,だいたい合ってる.しかし,やっていいのは「チャーシュー麺,味玉つき」くらいまでで,「トッピング全部載せ」はやりすぎ.
CoCo壱番屋で「カレーのトッピング全部のせ」に挑戦してみた 〜注文編〜」 https://gigazine.net/news/20080403_coco1_topping_all_1/

*7:「二郎チョモランマ」って通称もあるようだ.遭難しそう.

*8:技術的負債は単調増加.

*9:ソフトウエアにおいては,これは構造的にリファクタリング不可能で,一度完全に破壊して使えなくしてからやり直すのと同義.

*10:ソフトウエアにおいては,仮にバグが見つかっても原理的にデバッグ不可能だし,僅かでも仕様変更があったらちゃぶ台返しして全部書き直すことを意味する.

*11:アレは本気で死ぬかと思った.ここまで積み上がったら,手遅れだっつーの.

*12:「継承よりコンポジション」.1995年のGoF以後は最重要なテクニックの一つなので,すでに何度も目にしているかもしれない.

*13:単一責任の原則.ベストプラクティスの一つだ.

*14:つまりは疎結合.にぎり寿司くらいなら疎結合なのは自明だが,ソフトウエアは相互に強く関係していることが多いので疎結合に作るのは容易ではない.機械式時計における歯車やネジのように,普通に作ればガチガチの密結合になって,一つの部品を他のに差し替えただけでも動作しないのが当たり前だ.
SIBOSUN ブランド 無蓋 設計 機械式 手巻き 懐中時計 チェーン 男性 銀 シルバー アンティークギフト ボックス
時計でこれがゆるされるのは,仕様変更がおきないから.「60秒で1分,60分で1時間,24時間で1日」という仕様が,もし頻繁に変更されたなら,その度に膨大な修正が入って世界中の時計がほぼ全部作り直しになるだろう.
と言ってたら,「オリンピックのためにサマータイムしたい」というマジキチな政治家が現れた.まさかの展開. http://d.hatena.ne.jp/JavaBlack/20180813/p1 http://d.hatena.ne.jp/JavaBlack/20180825/p2

*15:ワサビをぬってない奴にあとから塗るのは簡単だが,一度塗った物を除去するのは基本的に不可能.ワサビを先にぬっても後で塗っても同じなら後回しにする.変更不可能で決定を遅らせることが可能な部分は,できる限りlazyに処理するのがベストプラクティス.そうすれば,ワサビが欲しい客にも欲しくない客のどちらにも対応できる.

*16:小皿単位で回ってくる回転寿司なら,変更にもっと強くなる.

*17:これはあくまで比喩だから,問題を正確には表現できてない.
たとえばモヤシましましラーメンなら,食べにくいとかぼやきつつも,端から崩しながら食べていけば良い.くずれて受け皿にこぼれ落ちてもご愛敬.それもB級グルメの醍醐味だ.「注文したけど,もやしは半分でいい.ニンニクおおすぎ.チャーシューはふたつで十分ですよ.」って場合でも,一度注文したものを作り直す必要はない.いらなきゃ残せばいいだけの話だ.
しかしプログラムはそうではない.できたコードはそこで終わりではなく,始まりなのだ.毎日のように使い続けるだろうし,仕様変更や機能追加,バグフィックスだって起きるだろう.その時オブジェクトが二郎チョモランマだったらどうなるか?「モヤシの下に埋もれてるチャーシューを一枚減らせ,ただし上に載ってるモヤシを崩してはならない」と言われて,それが簡単に実行できるだろうか?