オブジェクト指向
プログラミング

分かりそうで分からない少し分かるオブジェクト指向プログラミング

対象読者

プログラミングの初学者を対象としています。
学習の足がかりとなるように、オブジェクト指向はどんなジャンルのモノか、使うと何が便利になるかを説明していければと思います。

以下の文章は私がITエンジニアの仕事を俯瞰してみた時に、
オブジェクト指向がどういったものかを定義・配置したという主観的な認識であり、他の素晴らしいエンジニアさん達の理解を否定するものではありません。

まとめ

長くなりましたので、最初にまとめ
とりあえず以下を覚えて帰りましょう。

  • ITエンジニアの仕事は「情報処理」
  • プログラミング言語は「数式」や「ネイティブな文」を作る為に設計された
  • オブジェクト指向では「情報処理」を代行する「オブジェクト」を作る
  • オブジェクトは「情報処理」の仕事の内容から「比喩で命名」

オブジェクト指向プログラミングの学習は、
以下の順に行えば良さそうだと思います。

  • まずダック・タイピング(ポリモーフィズム)を覚える
  • 次にDIを覚える
  • 学習したい言語でどうやって実現するかを学習していく
  • オブジェクトの命名を洗練させていく

何故我々はオブジェクト指向の勉強がはかどらないのか

それは我々がITエンジニアとして何をしなければならないか知らないからです。
ゴールが分からないのに、その手段のオブジェクト指向?
何をどう勉強して、コードをどう便利にすればゴールか言えますか?

もし我々が全員オブジェクト指向プログラミングとやらが自体が好きで好きで仕方がなくて、無目的なままいくらでも勉強出来るのであればそれはそれで素敵なことですが、もし必要に迫られて勉強しているのであればまさに「木を見て森を見ず」な状態であり捗らないことでしょう。

そこで森から木にフォーカスを移していくように、
ITエンジニアの仕事を掘り下げて行きながらオブジェクト指向がどんな役割を担っているのかを私と一緒に復習してみましょう。

ITエンジニアとは

ITエンジニアのITは「情報技術」です。
他の人に情報を(必要に応じて加工し)届ける仕事を「情報処理」といいます。
この情報処理でお金を稼ぐ人をITエンジニアと呼びます。

例えるなら農家からお肉や野菜を仕入れて、お惣菜にしてスーパーに並べるまでの全てを行うような仕事です。

  • input: 肉や野菜を仕入れる
  • processing: 惣菜に調理
  • output: スーパーに並べる

この内、processingの部分は必要がなければ行う必要はありません。
単なる複製も立派な情報処理に含まれます。
例えばメールやクラウドストレージサービスなどでは、情報を保存して同じものが改変なく相手に届く事が求められます。

これをITエンジニア的な言い方で定義するとこうなります。

  • input: Aから情報を受取る
  • processing: 情報を加工・変更する
  • output: Bへ情報を届ける

AやB、加工方法は文脈により変わります。
しかし多くの新規案件の要件は上記の文章に、具体的な例と共にこれらのワードを当てはめるだけで作れます。

  • A、B: 「ファイル」「データベース」「他のマシン」「実行マシン自身」「マシンを操作している人間」...etc
  • 伝達手段: 「メール通知」「WebサーバへのHTTPリクエスト」「DBアクセス」「ファイルの読み書き」...etc
  • 情報: 「文字列」「数値」「複数の情報をまとめたコレクション」...etc
  • 処理: 「参照」「検索」「保存」「削除」...etc

この「Aから情報を受け取り、加工して、Bへ届ける」のプロセスは、オブジェクト指向プログラミングを行う時まで常に意識続けるべき原則なので忘れないようにしてください。

ITエンジニアとしての仕事の流れ

受け取ったり、届けたりする情報というのは一言で言えば「文字列」です。
通信では波しか送れませんし、機械は0か1かしか認識出来ないので、実際には波で0や1を表したストリームを送りつけるという事をやっているのですが割愛

文字列ならば何でも良いというわけではありません。
情報処理で他人を喜ばせてお金を貰うお仕事なので、
用途や要望によりこれを「ASCII等の文字コードで符号化されたコード」「数値」などなど、内部ではルール決めを行っていますけどね。

  • 文字列をHDDに書き出せばファイル
  • DBに書き込めば保存
  • コンソールへの出力結果
  • 画面上への表示

色々ありますが全部「文字列」の受け渡しと考えて差し支えありません。

しかし、現実世界の顧客が明確に「○○の文字列」が欲しいと定義してくれる美味しい仕事はありません。
要望が明確に伝えられるのであれば、もうとっくの昔に完成してもおかしくないはずで、出来ていないってことは、言ってる事と欲しい情報が全く違ってて炎上するケースが多いですね。
お前は作って下さいと言って散々こき使っておきながら、後になって思ったのと違うから金払わんとか言いやがって

まぁ、大抵の客というのは「ユーザーに素晴らしい体験をさせたい」とか「既存業務を楽にしたい」とか「営業成績を上げたい」といった言いたい事は分かるけどさぁ…どうやって実現するつもりなの?というよく分からない要望がスタートラインです。
この要望を解きほぐしながら、「Bに○○の文字列を届ける」という事をゴールとして定義するのが最初の仕事(要件定義)です。

ゴールが定まれば、ゴールに必要な文字列から逆算して、必要な情報を集めていきます。
スタートラインや情報の加工ルールを決定させるのが設計。
そもそもスタートラインから情報を転がしてゴールにたどり着けるかを検証するようなプロセスもあります。

現実世界の現状を文字列に変換し、
現実世界が求める文字列に加工、
受け渡しをすることがITエンジニアの仕事になります。

プログラミングとは?

実はプログラミングというのは情報処理の1手段でしかありません。
もしプログラミングを伴わずに情報の加工・伝達を完遂させ、お金を頂けるのならそれは立派なITエンジニアの仕事です。

その具体例は「シス管系女子」に沢山載っています。
私はこんな奥深く便利な世界があったのかと驚きました。
既にITエンジニアとして活躍している方も世界が広がると思うので是非読んでみてくださいね。

プログラミングは、この「情報処理」を自動化して誰でも実行出来る形にするためのものです。

プログラミングからオブジェクト指向までの流れ

オブジェクト指向は、プログラミングを上手くやるための手段の1つです。

元々アセンブリ言語で既にコンピュータで自動的な仕事というのは完成されています。
しかしそのコードは「Aに1を加算しろ」、「Bを2倍にしろ」、「100行目から処理を継続しろ」という命令の羅列であり、これでは目的を共有しない無能な上司です。

その結果どうなるか?
アセンブリ言語ではプログラムを作って実行する所までは良かったのですが、
コードが超読みづらく改修・機能追加が難度の高い作業となっていました。

もし人間が「数式」や「ネイティブな文章」で目的を書き、それをコンピュータが勝手に理解して実行してくれれば話が早いですし、後から文章の校正も楽になりますね。
それをゴールに高級言語が至る所で開発されました。
開発された言語は全部で400個以上であり、現在もまだ増え続けていますが、その多くは数式やネイティブな文章での実現を夢見て散っていき、現代の礎となった言語ばかりです。

数式と英文を程々の頻度で使い分けるALGOLというプログラミング言語があり、その流れを汲んだC言語が爆発的に普及しました。
現在の多くの言語はC言語を参考にしているものが多く、既にプログラミングコードは数式と英文で記述出来ると言って良い状態です。

そしてC言語が画期的なのは構造化プログラミングを採用しており、処理群に名前を付けて取り出せるようにしたことです。
今までのコードから考えると飛躍的にネイティブな英文に近いコードが書けるようになりました。

オブジェクト指向ってどういうもの?

構造化プログラミングを更に発展させ、よりネイティブな文章でコードを表現出来るのがオブジェクト指向です。
オブジェクトはプロパティを積めるというのが特徴で、プロパティを設定した後にメソッドを実行することで自身のプロパティ値に応じて情報の加工処理を補正してくれます。

オブジェクトという単語は我々には馴染みがないので、「からくり人形」と呼びましょうか。
このからくり人形は文字列Aを加工して、文字列Bを返す機能(メソッド)をもっています。
「情報処理」を我々の代わりに行ってくれるプログラムを作りたいんだから、情報処理しない人形作っても仕方ないでしょ?

そして出来上がった「からくり人形」ですが、「からくり人形」のままでは何をやる存在なのか分かりませんね。
なので名前を付けて登場人物として扱う必要があります。

例えば、図書検索システムを作るという想定で、仕事を行ってくれるからくり人形を1個作りました。
「この人形は探して欲しい本の名前を言ったら、データベースに検索しにいって、書籍の情報を返してくれる。まるで司書さんみたいだね……そうだ!君の名前は司書だ」とコードを書く人が比喩として名付けるものです。

「現実世界の具現化」というのは情報処理に於けるプログラミングの本質で考えれば逆です。
しかし、図書検索システムの場合、図書館にいらっしゃる本物の司書さんの仕事を観察し、文字列のやり取りに着目して同じ条件の仕事を満たせる人形を設計し、その人形に「司書」と名付けるようなことはよく行います。
システム全体で既に比喩の世界に入りながら設計を始めているわけですね。

図書検索システムに「シェフ」や「板前」、「ウェイトレス」みたいな登場人物が現れたら、
このシステムは何がしたいんだろう?と読者(未来の自分や協業者)が困惑してしまいます。
TPOを考えて、登場しそうな登場人物を作っていくようにしましょう。

世界観を統一して比喩を上手く使いシステムを設計する手法をDDDと呼ぶそうですよ。
受託開発(SIer)が業務知識が必要云々言ってるのは、お客さんと固有名詞をすり合わせて、出来るだけ差異が出ないように人形のやることや名称を現実世界に近づけたいから言ってる訳ですね。

オブジェクト指向プログラミングまとめ

  • 文字列を加工する人形を生成
  • 人形に指示を出す(メソッド実行)
  • 人形は情報の加工・受け渡しを代行する
  • 人形には現実世界で存在しそうな名前を比喩として与える

合言葉はif文を減らせ!

ITエンジニアの仕事は、Aさんから情報を受け取り、加工して、Bさんに引き渡すことでしたね。
コードはそれを「数式」や「ネイティブな文章」で表現したい。

コードに登場するif文というのは「ただし書き」と同じような存在です。
「ただし書き」ってのは法律によくある「ただし、……の場合は……ものとする」ってやつです。
この「ただし書き」が大量にある文章は分量が膨大になり、読みづらいものとなります。
(え?try〜catchという例外があるだろ?ただし書きも十分例外に含まれると思いますが…何か良い言い回しがあれば教えてください。)

また、ただし書きを増やせば増やすほど、新しい機能を盛り込もうとした時のコストが増加します。

想像がつきませんか?
では、我々エンジニアの卵でも分かるように例を出しましょう。
著作権の新しい法律を作る事を考えましょうか。
著作権法 - CRIC

全文検索で「ただし」と入力すると82件ヒットする中々の大作です。
(ただし書きの解説もついてるので、本文に使われている回数は少し減ります)

実は「検索エンジンが他のWebサイトを丸コピーして利用者の検索ワードに備える事」は、元々は違法であることはご存知ですか?
私は引用で行けると思っていたのですが、どうも他人の著作物を丸コピーして公開する行為が引っかかり、どうみても引用の範囲外だろと認識されているようです。

アメリカではフェアユースという特例があり、公共の利益が認められれば著作権違反行為とセットでも許されるケースがあるようです。
日本にはフェアユースというルールはないので、何とかして検索エンジンを合法なものに作り変えなければなりません。

その内容は既に、この著作権法の文面に取り込まれていますが、
上記の著作権法に「検索エンジンは合法」という趣旨の文章を書き加えるという視点でざっと眺めてみてください、追加する想定なので「Ctrl+F」キーで検索エンジンみたいなワードで調べてズルしちゃだめですよ?
1箇所でも既存文と矛盾するようであれば、日本全国の至る所からツッコミが来て税金泥棒と叩かれるので真面目にやって下さいね

はい、既存の文章に矛盾なく1行追加するだけの作業がどれほどの無理ゲーか想像つきましたか?
我々は一刻も早く帰宅して、アニメやゲームをして英気を養う義務があります。
誰もただし書きの山で時間や精神を消耗したくはありません。

脇道にそれてしまいましたが、無駄なif文は取り除きたい。
(流石に全てを取り払う事は無理ですし、ただし書きの方がふさわしい場面もあります。)
でも構造化プログラミングだけでは、これがなかなか難しいねんな…
それを解決するのがオブジェクト指向なのです。

真っ先に学習するのはダックタイピング(ポリモーフィズム)

if文の代わりになるのはダック・タイピング、もしくはポリモーフィズムです。
どちらもだいたい同じ事を言っていますが、特にメリットの同名のメソッドを所持しているのでオブジェクトを差し替えて実行仕分ける事が可能に着目してください。

イメージ的にはこんな感じ。

function job (a, processing, b) {
  var info = a.get();
  var newInfo = processing.change(info);
  a.send(newInfo);
}

引数のa, process, bに着目しましょう。
これらは全てメソッドを持ったオブジェクトという想定です。
例えばaはgetというメソッドを所持しており、もし改修で情報の入力元がファイルからデータベースに変わったとしてもその差異はaをすげ替えるだけで完結します。

アマゾンダッシュボタンみたいですね。
利用者は手にとってボタンを押すだけ、そしたらボタンに応じた品物が届く。
違うボタンになっても、同じ位置にボタンがひっついており、同じように手にとってボタンを押すだけで使える。

実際はaがデータベースやファイル、どっかのWebサーバからHTTPアクセスで取ってくるようなケースもあります。
でも、どのケースでもaオブジェクトにgetで取得しにいくように設計しさえすれば、我々は仕様がどう変更されたとしてもa.getを実行するだけで良いからその先を考える必要はなくなります。
同じようにprocessingの集計ロジックが変更、bの出力先もファイルやメール通知といった風に色々とオブジェクトが変化して対応することになります。

ちなみにダック・タイピングとポリモーフィズムの違いですが、
オブジェクトがメソッドを所持している事をどうやって保証するかの証明方法が主な相違になります。
プログラミング言語のインタフェース・継承などの機能を利用して実現するという感じなので、言語に該当する方を学習すれば良いと思います。
(例: Rubyならダック・タイピング、Javaならポリモーフィズム)

元々はポリモーフィズムという言葉しかなくて、
我々ITエンジニアの内、動的型付き言語を触るエンジニアはポリモーフィズムを覚えた後に、その仕様の一部を使うという学習をしていくというフローでした。
最近ではそのサブセットに近いダック・タイピングという言葉が流行ったので、こちらから勉強すれば捗るかもしれないですね。

ダック・タイピング(ポリモーフィズム)の限界

ダック・タイピング、もしくはポリモーフィズムを駆使して、
if文を駆逐することに成功しました。
しかしそれは悲劇の始まりに過ぎなかった……

ちょっとでもオブジェクト指向プログラミングをやったことのある人ならこう思うのではないでしょうか?
「いくらなんでもメインロジックの部分が3行で終わるわけないだろ、夢見すぎ」

全くもってそのとおり!
上記のコードのからくり人形さん3人は1メソッド叩くだけで全ての仕事をこなすスーパーマン(スーパー人形?)です。
ちょっと凝った事をさせるだけですぐに数百行、数千行に膨れ上がるのがコードですから、さぞかしそれぞれのメソッドには大量のコードが詰まっている事でしょう。

人形を換装可能っていっても、この数千行の代わりに別のことやらせたいの?
だったらまた数千行必要じゃん、共通部分は元々居る人形からコピペで……

こんな風に負けパターンに入ってしまいます。
コードは文章で人が読み書きしますから、そう簡単に数千行をあちらこちらにコピペしたら明日の自分が整合性保ちながら読み書き出来ずに死にますよ?
重複した文章は別紙参照とかに飛ばしてコピペしたり何度も書かずに終わらせるべきです。

でもそんなこと無理じゃね?無理ですね。
実はまだオブジェクト指向プログラミングのメリットは引き出せません。
これがダック・タイピング(ポリモーフィズム)を単体で使った場合の限界です。

次に学習するのはDI

依存性の注入(Dependency Injection)とか言われる奴で、
オブジェクトにオブジェクトを持たせてダック・タイピング(ポリモーフィズム)をネストさせる作戦です。

からくり人形のプロパティに別の人形を保存するよう修正して下さい。
そして、メソッド実行時に、プロパティのメソッドを決め打ちで実行しにいく……
こういう作りにすることで、部下を複数人従えて、共同作業で仕事をこなしていくチームリーダー的存在なからくり人形を実現できます。

ここで重要なのは、からくり人形がメソッド内で別の人形を作らないようにしてください。
あくまでプロパティに後付けで持たせて使うに留める所が鉄則です。

小さな仕事を一つの仕事を100%正確にこなす職人的な人形を複数用意して、
それらの人形を使役するまとめ役の人形を用意すれば大きな仕事も100%正確にこなせます。
優れたエンジニアならば1メソッドの中身は多くても10行程度で十分になります。

残った課題とこれからの方針

こうして我々はオブジェクト指向プログラミングのおかげで、
比喩を使ってコード内のただし書きの多くを駆逐できるようになりました。

しかし、コード上で大量の人形を生成して組織的に動かす事を描ききるのは簡単ではありません。
大量の人形1個1個、見分けが付くように名前を付ける必要があります。
名前を付ける際、このオブジェクトに頼めばAからBの情報に加工してくれそうだなと、誰が見ても分かるような名前にしなければなりません。

となれば、役割や責務で割って、職種で名前を付けるような事になるでしょうけど、
大きなシステムを構築する為に、100個単位でオブジェクトを生成するようなケースでは一瞬で語彙が尽きます。

今回の記事ではダック・タイピング(ポリモーフィズム)とDIで大きな問題を解決することができましたが、まだまだ読みやすいコードを構築するという事では不足していることでしょう。

でも、場数を踏めば命名の勘所も分かってきますし、
プログラミング言語でサポートされている機能、フレームワークで用意された機能、デザインパターンを駆使することで、より前に進んで行けるはずです。

最後に

オブジェクト指向プログラミングは長い道のりです。
記事たったの1つで完璧に理解出来るようなら分厚い書籍はいりません。
少しずつ分からない→分かったのサイクルを繰り返しながら共に歩んで行きましょう。