どのようにしてオブジェクト指向を教えるか? (1)

 手続き型言語に一度慣れるとオブジェクト指向言語の考え方がなかなか理解出来ない。プログラミングパラダイムそのものを見直さなければならないのだから、「手続き型プログラミングからオブジェクト指向プログラミング」へのパラダイムシフトは、一般人が三次元しか見ないのを二次元しか見えなくするのと同じぐらい難しい。又は、二次元しか見えない人を三次元しか見えないように矯正するのと同じぐらい難しい。


 だから、オブジェクト指向の概念を教える際には慎重に教えなければならない。以下、この記事でのターゲットは「一度手続き型言語に触れた人」とする。

1. オブジェクト指向の利点を説明出来るか?

 何か良いことが無ければ行動には至らないのが人間である。…とまで言わなくても、普通の人であれば利点が無ければ今更慣れた手続き型プログラミングから移動する気にはならないだろう。
 ここに教える際の第一の壁があるように思える。「異なる (同一の interface より implementation された class ないし同一の abstract class より inherit された class) 対象に同一名の method 呼び出しを適用できる polymorphism な性質により…」なんて説明を始めたら、多分、みんなが気持ちよく寝てくれるだろう。キーボードで殴られなければ感謝しなければならない。


 よって (当然の事であるが) オブジェクト指向を全く理解していなくても理解出来る説明をしなければならない。最も、全ての利点を何とかして説明しなければならない、と言う事でも無くて「ちょっと面白そうだな」程度の興味を引きつけて、より応用的な側面での利点は後で感じて貰えば良いと考えられると思う。
 そんな訳で、こんな感じではどうだろうか。

  • オブジェクト指向プログラミングはより人間の直感に適っている
  • プログラムを部品として切り分けやすい。複数人での開発もしやすい
  • 変更に強い。ある部分を修正した際、どこに影響があってどこを直せば良いか分かる
  • 現在、オブジェクト指向なライブラリが多いが、それも使えるようになる

 ここでは、まず第一に「直感的プログラミングへの道」を実感して貰う説明手法やプログラム例を考えたい。

2. 順序良く説明できるか?

 まずは「わかりやすい概念」「慣れ親しんだ概念」から理解して貰ってから、より応用的な部分に入って行くべきであろう。手続き型言語に触れた人なら、構造体にメソッドをくっつけるとクラスになる、とか言った方がむしろ分かりやすいのかな。


 そんなわけで、こんな順を追ってみるとか。

  1. 手続き型で実際に実装してみる
  2. 手続き型とオブジェクト指向の概念図の比較
  3. クラスとインスタンスの説明
    • 「設計図」と「具現化した物質」
    • 構造体の定義と使用、と比較して説明すると分かりやすいだろうか
  4. メンバ変数とメソッド
    • 構造体が能動的に動き出すとか
  5. カプセル化とアクセス修飾子
    • 構造体的な使い方をしては意味が無いし
  6. (以下書きかけ)

3. 面白い実用的なプログラム例を出せるか?

 順序を追った説明に合わせたプログラム例を提示できるかも問題である。
 結構の入門書や入門サイトでは「人」とかがプログラム例に出される傾向があると思う。しかし、実際に「人」をオブジェクト指向の説明として取り上げたらどうなるだろうか。

#include <stdio.h>
#include <string.h>

class People {
	char  m_name[32];
	int  m_age;

public:
	People(const char* name,int age) {
		strcpy(m_name,name);
		m_age = age;
	}

	void Introduce() {
		printf("私の名前は %s です。私は %d 歳です。\n",m_name,m_age);
	}
};

int main() {
	People  p1("l.p.m.11",1024);
	People  p2("山田太郎",-8);
	
	p1.Introduce();
	p2.Introduce();
}

 最も良い反応でも「あぁ、確かに人っぽい (全然足りないけど) ものを表現出来た。でもこれが具体的に何の役に立つの?」と言われたら (「意味がわかりません」と言われなかっただけ感謝するべきだろう)、折角の例が台無しである (これをちゃんとカプセル化したり People を abstract にして男女に分けるとかオブジェクト指向経験者から見れば色々な考え方はあると思うが、初学者にとっては「結局何の役に立つのか」と言う部分からはどんどん遠ざかるのでは無いか)。概念として使うには有用な考えではあるが、はっきり言うと実際のコード例としては望ましく無いと思う。


 ここでは前述の通り「直感的プログラミング」の理解を第一としているのでそれを優先したプログラム例を考えたい。そこで、ここでは「電卓プログラム」の作成に附いて考える。まぁ人間をなんとなく表現するよりは、役に立つ感を感じることも出来るだろう。ただ、お題が電卓ではポリモーフィズムに関する説明がしにくい (と言うか出来ない?) ような気がするため、もう一つぐらい例が欲しいかもなぁ。
 (まぁ正直なところ、部品にしたくなるまで大きいプログラムにしないと実感出来ない部分もあるから難しい)。

3.1. 電卓を「手続き型言語」で実装 @ 単一の場合


 電卓は内部に演算した結果を保存するメモリと、所謂メモリ機能用のメモリの二つの記憶域を持つ。この記憶域と「演算と言う手続き」が不可分で有ることを気づかせて、電卓プログラム内で「電卓オブジェクト」を作ることによって直感的な表現を行うことが出来ることを実感させられるのでは無いか。


 例えば、手続き型プログラミングで演算を関数にて実装した時、加算時のイメージ図は右図のようになるだろう。加算の際にはメモリ域の値と入力値を加算ユニット (要は例えば add 関数) に流し込んで演算をさせているイメージである。


 しかし、電卓プログラムを作っているのに見た目あまり電卓っぽく無い。所詮電卓の中身ではこんな事をさせているんだ、と言って納得出来るプログラマ気質の方は何も感じないかも知れないが、この「メモリ」と「演算ユニット」をひとまとめでプログラム上で表現出来たら…面白いと思わないかな、と言って納得してくれるだろうか。


3.2. 電卓を「手続き型言語」で実装 @ 複数の場合 (MultipleInstance)


 複数の電卓を実装することになったらどうだろうか?手続き型プログラミングでは流し込むためのメモリ域だけを増やせば良いから、大体左図のようになるだろう。


 見てて妙な気分にならないだろうか。複数の電卓を実装しているのに、演算ユニットは一個ずつしか無い。複数の電卓を実装しているのにと言うところを強調しても、「複数の電卓だろうとこれが自然じゃないの?」と言われてしまうかもしれない。

 しかし、複数の電卓の事を思い浮かべる際には、普通はあの電卓が三つ四つ机に並んでいるところを浮かべるのが普通であって、「電卓の中にメモリがいくつもあってそれを切り替えて使う」とか「電卓の操作パネルは三つ四つあるけど、そこから導線が飛び出してて一つの演算ユニット群に電卓がそれぞれつながっている」所をわざわざ想像するような変な人はあまりいないだろう *1


(以下書きかけ)

X. と言う訳で書きかけ

 そんな訳で書きかけです。是非なんかうまい方法があればコメントしてくれると喜ぶよ!

*1:そういう人は、わざわざ教えなくても勝手にオブジェクト指向に附いて理解してくれる人である