プログラムは処理を機械の手続きにあわせて実装するものでした。でも、そうすると人間が制御可能な複雑さを超えてしまいます。制御不能になるということは、つまりバグが入るということです。そこで、機械には少しがんばってもらって、その分人間が理解可能なレベルまで複雑さを落とすためのテクニックが生まれてきました。
そのステップを追ってみます(厳密には概念の誕生ははるかに古いものなどもあります。実際に用いられるようになってきた歴史をプログラム改善の流れと結びつけて並び替えてあります)。
手続きの抽象化。
抽象化が進んだとき、引数の多段受け渡しが発生する。
どこかにまとめておいて、全部そこを参照すればいいんじゃね?
グローバル変数問題。
グローバルだと管理しきれない。スコープを制限しよう。→モジュールプライベートな変数
モジュールプライベートになったので、その変数に束縛されたデータを使いたい手続きは全部そのモジュールへ集めないといけない。
抽象データ型の誕生。
構造体の配列とそれを第一引数にとり、構造体の内部データを操作する手続きをまとめたモジュール。
構造体のとりまわし面倒。設計方針として縛ってるだけで強制力がない。
クラスという概念で言語機能として取り込み。内部状態を個別に保持し、アクセスは強く制限され、構造体へのアクセスは自動的に保証され、引数での取り回しが不要になった。
今度は条件分岐を抽象化していく。分岐は似て非なるものに表れる。どんどん追い出し、ディスパッチ関数へと抽象化していく。そうすると、似て非なるもののメンテナンスがパターン化されていく。ディスパッチ関数に分岐を追加し、呼び出す処理を決定する。これを必要な処理の数だけ変更する。
DRYじゃない。
呼び出し元を一元化する。
分岐条件が決まる瞬間に呼ばれる関数も決定されてるはず。分岐条件と呼ばれる関数をセットにしてまとめる。その条件で呼び出すべき処理は一意に確定する。
これを言語の機能として実装したのがポリモーフィズム。
話をまとめると、何もしないとプログラムは複雑で人間には把握できない。というのが基本的な理解にあります。
そこで、複雑さを人間がわかる程度に抑えるためのテクニックがあって、それは
- 手続きの抽象化(関数)
- データの抽象化(構造体)
- データ+手続きの組を抽象化(抽象データ型)
です。
これはプログラミングのテクニックであり、設計方針であり、今では言語仕様でもあります。
「クラスを設計しよう」と考えると、「設計されたクラス」という正解の姿があるかのように思えますが、それは単に複雑さを取り除いていった結果の姿であり、複雑さを取り除くためには上記のようなステップを踏んでいます。その結果として、クラスとして括るべき単位が現れてきます。