構造化プログラミングの改善としてのオブジェクト指向

オブジェクト指向に関する記事はあまたあれど、その前提がはっきりしていないのでオブジェクト指向のどこら辺が新しい技術だったのかわかりにくい。
一度、構造化プログラミングまで立ち戻った方が、オブジェクト指向はわかりやすいので記事としてまとめてみた。
※なお、ここで構造化プログラミングという言葉はダイクストラの構造化プログラミングを指すものとする。
## ダイクストラの構造化プログラミング（一つのプログラムにつき一つの計算スレッドだけを使う）
まず、ダイクストラの構造化プログラミングを振り返る。

ダイクストラの主張は、『go to文有害説（Go To statement considered harmful）』（木村泉訳）にはっきりあらわれている。
>次に申し上げたいのは、われわれの知力というものが、どちらかというと静的な関係を把握するのに適しており、時間とともに起こるプロセスの展開を思い浮かべる力はあまりよく発達していない、ということです。したがって、われわれは（自分の限界を心得たかしこいプログラマとして）静的なプログラムと動的なプロセスの間の概念上のギャップを小さくするように最大限の努力を払い、それによって（字づらの上にひろがる）プログラムと（時間軸上にひろがる）プロセスの間の対応をできるだけに自明なものにすべきなのであります。

『構造化プログラミング論』における主張もまとめると、ダイクストラは、
+ 静的なプログラムと動的な実行プロセスとの間には文章を読むように上から下に読める形でできるだけ自明な対応が存在しなくてはならない
+ goto文はその自明な対応を壊してしまうのでできるだけ使用すべきではない
+ 常人が理解できるように上から下に読める形にするにあたっては、制御の規律を守るため３つの基本構造文（順次、選択、繰り返し）のみに限定する
ということを主張した。

これはつまり、静的なプログラムを動的な実行プロセスすなわちフローチャートに翻訳することができるということである。
![翻訳.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62130/f65cbaab-3d32-fc3e-e28a-bb51011a5673.png)

この方針により、goto文を駆使する奇々怪々な恐ろしいプログラムというものを撲滅、とまではいかないまでも共有するプログラムとして提示しても公然と拒否することができるようになった。公共のマナーとして共有するプログラムとは構造化プログラミングの基準でコーディングしたもの、となったわけである。

### 構造化プログラミングの欠陥
純粋にダイクストラの構造化プログラミング的な開発手法については、以前以下の記事でまとめた。
[構造化プログラミングとは、だいたい段階的詳細化法のことだ。](https://qiita.com/iHdkz/items/de8a0bf1a3d6c6c24215)
チームでの開発もでき、一見非常に良さそうな開発手法に思える。ところが、この構造化プログラミングによる開発手法には大きな欠陥がある。その欠点とは、
**<font color="Magenta">既存コードの再利用が非常にしづらい</font>**
というところである。

単純に思い返してみるとわかるが、**そもそも構造化プログラミングには既存コードを再利用するという手法がほとんどない**。
まったくないわけではない。変数環境をすべて把握したうえで **連接（concatenation）** するという手法や関数呼び出しによる抽象はダイクストラも言っている。さらにいえば、真珠の首飾りも一応言ってはいるが、現代から見れば非常に使いづらい。ダイクストラの言う手法だけを使おうとすれば、**変数や関数名の衝突**というものに非常に気を遣うプログラミングとなってしまうのである。

### 具体的な問題
話を非常に単純にする。ここにそれぞれ独立に開発された構造化プログラムとして、与えられたデータをレンダリングして画面に表示するpreviewというプログラムと、与えられたデータを印刷するprintというプログラムがあるとしよう。それぞれ、正常に稼働することが確認されており、検証も済んでおり、ソースコードもすべて公開されているとする。当然、開発言語も同一であるとする。

ここで、印刷前に、印刷上の不備を発見するためにプレビューさせてから印刷するプログラムpreview_and_printというプログラムを開発する命令を受けたとしよう。仕様としては、ちょうどプレビューについてはpreviewプログラム、印刷についてはprintプログラムに準じるとする。

さて、このとき構造化プログラミングだけの手法を使って、preview_and_printというプログラムを効率的に開発できるであろうか？検証済みの正常に稼働するプログラムpreviewとprintのソースコードはいくらでも自由に使っていいものとする。

この答えは、お察しのとおり、大抵の場合効率的に開発できない。

#### 効率的に開発できない理由
効率的に開発できない理由を説明するにあたって、previewプログラム、printプログラムのフローチャート翻訳したものは下図のとおりと置く。
![previewとprint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62130/bc0aaa48-1fe6-ed35-15e4-6c7d649b9bb1.png)
これらを構造化プログラミングの教義に従い単純に連接（concatenation）すると、次の図のようになる。
![preview_and_printプログラム？.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62130/3efa57e9-78d7-bbfc-be05-d12ffbfc98a5.png)
プログラミングの素人がこんな設計図を出して来たらあなたなら即座に少なくとも以下のダメ出しをすることだろう。
+ 変数名・関数名が衝突することが考慮されていない
+ 初期化処理、終了処理が考慮されていない

実際、これらによりエラーが出る可能性は非常に高い。

さらに、preview_and_printプログラムの趣旨からプレビュー画面でミスが見つかった場合は印刷を中止しなければいけないし、プレビューするまでもないすでに確認済みのデータについてはプレビューを省略するなど、条件分岐が発生するので、単純に検証済みプログラムを連接するだけでは、目的のプログラム・コードにすることができないというのは明らかだろう。

## 一つのプログラムにつき複数の計算スレッド（マルチスレッド）を使う
構造化プログラミングでは既存コードの再利用は非常にしづらいものであることは納得できただろう。この再利用しづらさの原因は主に変数名・関数名の衝突に起因したものであったが、より根本的なところとしては構造化プログラミングの前提にある。

上で、
>+ 静的なプログラムと動的な実行プロセスとの間には文章を読むように上から下に読める形でできるだけ自明な対応が存在しなくてはならない

と説明し、プログラムはフローチャートに翻訳することができるとした。ここで大きく前提として入り込んでいることなのであるが、
**<font color="Magenta">フローチャートは単線（シングルスレッド）であることを前提にしている</font>**
のである。つまり**一つのプログラムにつき一つの計算スレッドしか使えないというのが構造化プログラミングなのである。**

この「一つのプログラムにつき一つの計算スレッドしか使えない」という構造化プログラミングのドグマを乗り越えた先にオブジェクト指向がある。つまり、「一つのプログラムにつき複数の計算スレッド（マルチスレッド）を使う」というプログラミング手法の存在が見えてくるわけである。

つまり先ほどの構造化プログラミングでは効率的に開発できない問題も、検証済みの正常稼働するpreviewとprintそれぞれのプログラムを主プログラムから呼び出して疑似並列的に実行させてしまい必要なルーチンは主プログラムからメッセージを渡して処理させるという方法で解決することがすぐに思いつくわけである。
![疑似並列実行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62130/528e697e-9005-2edd-46d9-e62aedde229f.png)

もうお分かりのように、このような手法を実現する機構として使えるのが、オーレ＝ヨハン・ダールによってSimulaで初めて導入されたクラスとオブジェクトの概念である。単線（シングルスレッド）プログラミングしかできないAlgolはシミュレーション言語として使い物にならない（並列的に運動する物体の挙動や相互作用を扱えない）。そこでAlgolにクラス（当初はプロセスと呼ばれた）の機構を導入することで疑似並列的に並列処理を可能にしたのがSimulaである。その副産物としてできるようになったのが、上で説明したマルチスレッドを利用したプログラミングである。

### 余談：オブジェクトの概念
上で、previewプログラムとprintプログラムを並列的に実行させて処理させればいいともっともらしく説明したが、
**プログラムを並列的にでも実行させてしまえば、それぞれプログラムが実行・終了してしまう**
じゃないか、という疑問を持たれる人もいるだろう。

その点は、オーレ＝ヨハン・ダールも当然考えたことである。

Simulaにおけるクラスとオブジェクトの提案がなされたオーレ＝ヨハン・ダールとC.A.R.ホーアによる論文『階層的プログラム構造』からクラスとオブジェクトの定義を引用すると疑問は一気に解消する。
>**呼び出されたときだけでなく、存在し続けるブロック[^引用者注1]の実例（インスタンス）のもとになる手続きをクラス（class）という。** そして、その実例をそのクラスの対象（object）という。クラスは、引数をもっていてももっていなくてもよく、手続きとまったく同じ方法で宣言される。
　＜クラス宣言＞：：＝class＜クラス名＞＜仮引数部＞；
 　　　　　　　　　＜規制部＞；＜クラスの本体＞
　＜クラスの本体＞：：＝＜文＞
 クラスの本体に局所的に宣言されている変数、手続き、および値呼びや参照呼びの仮引数はどれも、そのクラスの所属物（attribute）であるという。クラスの本体がブロックでない問には、それがブロックのかっこbegin ... endで囲まれているとみなすことにする。
>*E.W.ダイクストラ/C.A.R.ホーア/O.-J.ダール著『構造化プログラミング』p.202*
[^引用者注1]:Algol言語のbeginとendで囲まれた部分。関数・プログラムと考えればよい。

脚注にも書いたが、ブロックはプログラム・関数と考えればよい。つまりクラスとは呼び出しをしてもメモリから消滅しない変数、関数定義の塊の領域で、自由に変数や定義された関数の呼び出しが可能なデータとして当初定義されていたのである。逆説的に、上で説明したマルチスレッドを利用したプログラム手法ができるように定義されたのがクラスとオブジェクトともいえる。

**こういう利用用途であるクラスとオブジェクト概念を駆使するプログラム開発手法で**、四大要素と言われる抽象、カプセル化、ポリモルフィズム、継承を備えるプログラミングを**オブジェクト指向プログラミング（object oriented programming; OOP）** と呼ぶのである[^2]。

この「こういう利用用途で～」の部分が普通の説明では省略されているからわかり難い。この記事ではそのオブジェクト指向の前提部分について解説する記事として書かれました。
[^2]:単純に呼び出しをしても消滅しない、存在し続けるブロックのインスタンスであるオブジェクトを駆使するプログラミングをオブジェクト指向と呼んでもいいと思う。アラン・ケイはそういう意図だったのではないだろうかというのは答えの出ない私の個人的な推理の一つである。

## まとめ
<dl>
<dt>構造化プログラミング</dt>
<dd>シングルスレッド・プログラミング。設計図としてフローチャートに翻訳できる。</dd>
<dt>オブジェクト指向プログラミング</dt>
<dd>クラス機構で実現される疑似並列的なマルチスレッド・プログラミング。設計図としてフローチャートに翻訳できない。設計にあたってはUML図などを使用する。</dd>
</dl>