今年も言語教育を行いました、言語はJavaで詳細な内容は書ませんが気づいたことをメモ程度にここに追記していきます。こういったことって人によっては「社外秘では」なんて言う人もいて会社から怒られるかもしれないけどね。でも下には社外秘なんて気の利いたもんは書いていません、内容は本当に教える際に感じたメモ程度ですのであしからず。
学習範囲の選定
会社でxxを教育しろ!こうなったとき指示する方としてはアレもコレもと夢が膨らむと思います。でも正直アレもコレもと言うのは時間などの制約がある中で教えるのは無理です。そのなかで「これだけは!」と言う部分を教える前に考えます。あとは、教えている中で必要があったら教えますし、学習者が質問などで食いついてきたら教えます。
たとえばJavaを教えろ!っとなったら何を教えたいのか?文法を教えるだけ?物を作らせる?その場合Webアプリ?だったらWebアプリにする理由は?JavaだけであればSwingの方が覚えることが格段に少ないとか、単に設計書があるから作れるようになればいいのか、それともデザインパターンを駆使するような小難しくてマニアックな(人によってはそう感じるらしい)クラスの設計をするのが最終目的とか・・・
その中でJavaの中でも教える部分といらない部分も考える必要があります。正直インナークラスや無名インナー、スレッド、java.ioあたりのクラス、awt(アプレット含む)、ビット演算子なんかはわざわざ時間を使って学習するには効果が薄いとか。それを使ってガリガリとJava初心者が書くような状況に入れられるなんて状況は早々ないと思いますので。そういった意味でどんなところまで覚えればいいかをよく考える必要があります。
最終的に何を作るのか、それには何(サーバなどの機器、前提知識、テキスト類、提供基盤、ドキュメント類、・・・)が必要か、それはいつまでに用意すべきか、こう考えるとはじめる前段階としての作業は数日やそこらで出来るものではないです。それだけコストかけるんだったら社外の講習に頼んだ方がプロに教えてもらえるので安上がりになるかもしれません。
講習
ここからは講習で感じたことを書きたいと思います。
しゃべり方について
プロでもない人が人前でしゃべるとき、本人もゆっくりしゃべっているつもりでも結構、早口に聞こえます。また焦って突っかかったりするので余計にわかりづらくなります。なので、自分でも意識するぐらいゆっくりしゃべります。これは以前、プロの講習を見たことがあるのですが、自分じゃそんなゆっくりしゃべらんだろう!ってなぐらいゆっくりしゃべります。それを知っていたので、意識的にゆっくりしゃべろうとするのですが、なかなかできません。
学習環境について
Javaの初心者本なんかはjavacを使ったのが多いですが、もうそろそろjavacについては概念だけ教えて教える必要はないんではないかと思います。実際問題、開発環境でjavacを使っている人・・・見たことありますか?antでもなくjavacを自分の手でコマンドを打つプロジェクト・・・。一番初めにHello Worldを表示するJavaコードはjavacで雰囲気を味わってもらって、後はEclipseなりNetBeansなどでもいいと思います。
言葉の統一
テキストではもちろんですが、講師が発する言葉に統一がないと学習者の混乱のもとです。たとえばオブジェクト指向では「オブジェクト≒インスタンス」という認識のテキストがありますが以下のように色々な言い方ができます。うっかり使ってしまいそうですが、使うとまだ似た言葉を同じ言葉として認識しづらいため、混乱してしまいます。これらはテキストがあるのであれば、テキストの表現を使う必要があります。
オブジェクトを作成する オブジェクトを生成する オブジェクトをnewする インスタンスを作成する インスタンスを生成する インスタンスをnewする
このほかにはこんなのも。
スーパークラス <-- --> サブクラス スーパークラス <-- --> 具象クラス スーパークラス <-- --> 子クラス 基底クラス <-- --> サブクラス 基底クラス <-- --> 具象クラス 基底クラス <-- --> 子クラス ・ ・ ・
クラスのこと
Javaを勉強する上について、処理自体や、繰り返し、分岐といった構造化ででてくるような概念は比較的簡単に覚えます。だけど、クラスという概念を覚えるのが非常に難しいと思われました。ポイントはクラス(定義)とインスタンス(実態)を分けて考えることが難しい点です。
自分の場合は、しつこくクラスからオブジェクトができるというイメージ。オブジェクトとはどんなイメージかを教えるのではなく、考えてもらうのを中心とします。コードリーディングも講師が説明するのではなく、予想して実行、結果を確かめさせると言った泥臭い作業を中心としました。独習Javaなんかやるときはこんな感じですよね、読んで、予想して、実行して、確かめる。で、「ほほーん」って思う。面倒に見えますが教えるのではなく学習者の考えた予想を補佐するだけなので実は楽です。
引数、仮引数
引数は難しいですね。あと戻り値。呼び出し側の変数と呼ばれた側の仮引数の関係をきっちりやらないと何で呼ばれた側で引数を変更しても呼び出し元は変わらないのかとか、オブジェクト参照での問題などを説明できません。当初オブジェクト参照だのは教えないと考えていたのですが、仮引数を教えるとどうしても教えなくてはならない流れになってきます。困ったもんだ。
正直、引数に入れた変数が、呼び出したメソッドでも値が使えるよ程度に教えるのがいいのではないかと感じた。あとは仮引数だのオブジェクト参照だのは質問があったら教えるだけみたいな。学習する上で混乱する部分ではないかなと感じる。学習者には正直、サンプルを作って終わり、サンプルを作って色々実験する。の2種類がいるので少なくとも後者の人からは質問がくると思います。
サンプル
また、サンプルとなるコードからイメージ的なものをホワイトボードで描いて、イメージしてもらったり、その逆にホワイトボードにイメージを描いてもらったり。説明するためのコードの書き方については、本やWebサイトなどでもそうですが下記のように必要最低限の部分のみを書くと思います。
public void foo(){ Hoge h = new Hoge(); h.show(); }
上記ではHogeクラスの使い方をfoo()メソッドを作って表現した例と見る事ができるんだけど、この場合学習者からしてみれば、まずHogeと言うクラスがどこにあるかわからない。foo()メソッドが何のクラスにあるのかわからないと言う疑問になり、本質的な部分を書いたつもりが逆に混乱してしまうだけとなります。なのでこういった必要最低限のコードははじめは混乱するので、はじめは面倒でもクラスの簡単な定義、main()メソッドなども簡単に書くとわかるようになります。この際、mainメソッドを起点とすると分かりやすいようです。こういったのは分かるのであれば、はじめにプレゼン資料作っておきましょう。
class Hoge { ... 省略 } class Sample { public static void main(String[] args){ foo(); } public static void foo(){ Hoge h = new Hoge(); h.show(); } }
動的初期化子と無名インナークラス
無名インナーについてはSwingのイベント処理だとか絶対必要!って時にしか使わせないし、他に見かけた(書かせた)ことが無かったのですが、初めて見た人はまず「見て不審に思うコード」でイコール「質問してくるコード」です。動的初期化子に関して言えばJavaになれた人はサンプルとしてこんな風に書いてしまうかもしれません。
class Foo { List list = new List(); { list.add("1"); list.add("2"); list.add("3"); } }
または無名インナーと動的初期化子をあわせてこんな感じに
class Foo { List list = new List(){ { list.add("1"); list.add("2"); list.add("3"); } }; }
でもこれは初心者から見れば単に混乱するコードで、理由を教えればとりあえずは書けるようになりますが、Javaの構文をよく理解するまでは書かないでね!ってクギをさしておいた方が無難です。普通のプロジェクトでも同じです。初心者が大量に投入されるようなプロジェクトでは、いくら開発者の生産性を大事に!と言っても僕だったらこういうコードはNGにします。
コードフォーマット、チェック
Eclipseなどでの開発ではCheckStyleやFindBugsで大概のチェックはできますが、それだけでOK!と言えるコードは書かれないです。まず無駄がありますし、何より汚いです。そこからきっちりと一緒に見て、なぜこうしたか、こういう場合はこの方がよい(なぜか?も説明します)と改善していきます。ただ、なぜこうしたか?を聞くときに単に「なんでこうしたの?」と聞くと怒られていると勘違いしてしまい萎縮してしまうので「次の講習への資料となるんだけど、なぜこうしたか?単純に聞きたい」と理由をはっきり言ったほうがよいです。
インデントや"{"の開始位置などは"絶対こうやってね"と言うぐらいの気持ちで言わないとはじめはインデントがグチャグチャで書こうとします。結果して"{"に対する"}"の位置がわからなくなり、終端の"}"を忘れてコンパイルエラーで詰まります。下記のようなものは普通です。まるで昔行われた「醜いCのコンテスト」のかわいい版のようです。
class Hoge{ public void fuga() { System.out.println("hello!"); int foo = 55; System.out.println("hello!"); } }
インスタンス変数とクラス変数の違い
やっぱりここは必ずつまづきますね。はじめはクラスからインスタンスができるのでどうしてもクラス≒インスタンスみたいなイメージを持つようで、クラスとインスタンスを別のものとしてイメージできないうちは、なかなかインスタンス変数とクラス変数の違いは分からないようです。これも資料を作るなりホワイトボードに書くなりしてゆっくりとイメージを作ってもらいます。
継承、抽象クラス、インターフェース
継承・・・意外にこの考え自体はすぐ分かるようです。ただ、オーバーライドとオーバーロードといった似たような言葉があるので、説明のときに気をつけないといけないかなと感じました。抽象クラスもなんで抽象化する必要があるかを中心にやるといいようです。抽象クラスとインターフェースの違いをよく質問されます。この違いは説明が非常に、く・・苦しい・・・と感じます。自分の中でまだ整理できていないのでしょう。
フレームワークを使ったWebアプリケーション
ここからは最終的に作ったWebアプリケーションについてです。
Servletとフレームワーク
JavaでWebアプリケーションを作る場合、現在はフレームワークを使うのが主流になっていると思います。(Servlet自身もJ2EEフレームワークだよって意見は置いておきます)そのためにServletで一つ作ってもらい、そのServletをフレームワークに書き換えることで、フレームワークの良さを知ってもらおうとしました。でないと、「なんでフレームワークって物を使わなきゃならないの」ってのが説明できません。これは工数がとか、開発効率とか言っても正直分からんでしょう。身をもって体験してもらおうという趣向です。実際にはServletは、HTML部分からweb.xml、Servletと順繰りに一緒に作業して作ってもらいました。(約1日)
これは、ServletではHTMLをJavaコードで編集するとか、キャラクタエンコーディングの設定を各自しなくちゃならないとか、色々大変なのがフレームワークを使うと、あまり意識しなくなるよってのが趣旨です。あと、JavaでのWebの基本的な仕組みを知るというものもあります。(この部分を知らないプログラマが多すぎるぐらい、フレームワークが浸透しているということでしょうか)
でも、自分もServletの開発なんてのは趣味でServletだけで掲示板を作るぐらいしかやったことがないのだけど、結果としてはJavaをはじめました!の頃の人にServletを作ってもらってそれをフレームワークに改良したからってあまり利便性は伝わらないんだと言うことです。お題が悪かったのかもしれない。やるとしたら1週間ぐらいかけてServletでちゃんとしたアプリケーションを作って・・・(つまり時間をかけてServletに慣れてもらってから)と言った段階を踏んでからフレームワークに乗り換えると言った方法が有効かもしれません。
むしろ、フレームワークを使った場合だとフレームワークによってはたくさんの設定や、開発者の意識を分散させるようなクラス構成とか・・・を意識しなくてはならない場合もあるので、そういう面では学習者を混乱させるのかもしれない。ただ、JSPを使わないServlet開発では結果のHTMLをJavaのコード上で記述しなくてはならないのが大変って言うのはわかってもらえるみたい。
Wicket
Java言語学習の終わりにひとつWebアプリケーションを作らせましたが、今回フレームワークにはWicketを使いました。前回はClick FrameworkでしたがWicketのほうがどちらかと言うとはじめての方には向いているようです。(Clickも好きですが)Clickの場合は非常にとっつきやすい反面GETとPUTあたりの挙動、リクエストのたびにページのインスタンスが生成されると言った特性、Velocityの書式で突っかかることが多いのですが、Wicketの場合そういったことを考える必要がありません。ただ、Wicketの場合は無名インナークラスを多用するのでその面ではどうかなと思ったのですが、無名インナーはまずは「こう書けばいい」と規約なり何なりで書いてあげれば特に問題なく書いてくれます。もっと簡単なフレームワークがあったら教えてください。
今回の場合は基盤を作る時間が十分にあったために、各学習者に作ってもらう画面間の依存性をなくすようにしました。実際には画面遷移にはStrutsのように設定ファイルを介して行うようにします。そのための画面遷移メソッドを基底ページクラスにつくり、このメソッドを使うように規約内に盛り込みます。こうするとコード補完や、コンパイラによる静的なチェックはなくなりますが、各画面の依存性はなくなります。あとは、その画面に飛ぶのではなく実際にそのページが無い場合は特定のページに遷移させてやるだけです。通常のWeb開発でもこんなの作るでしょ?
class HogePage extends AbstracctHogeFigaPage { private void onClickNextPage(){ // こうじゃなくて //setResponsePage(NextPage.class); // 基底クラスで下記のメソッドを作り使用させる // メソッドでは、設定ファイルの文字列からClassクラスを生成して、 // setResponsePage(Class)を呼ぶようにしますが、 // 実際の開発時のページ遷移は、特定の画面に遷移する // 運用モードのときのみ、conf.txtの値を元に実際のページに遷移する。 setResponsePage("page.next"); } }
conf.txt
# xx画面のクラス page.next = jp.xx.hoge.page.NextPage
Wicketの場合は多くのフレームワーク側で定義されているメソッドはfinal指定されているので、こういった場合に不便だなぁと感じます。プロジェクト内では「フレームワーク側のこのメソッドではなく、基底クラスで定義したこのメソッドを使ってほしい」と思っても開発者には見えてしまうので規約なり何なりで明言しなくてはならない。使ってほしくないメソッドは検証した上で、RuntimeExceptionを飛ばすようにオーバーライドした基底クラスを作ってしまえば規約なんていらなくなるのにと感じてしまいます。
画面間の依存性が無くなるということは、前画面との依存性がある場合、その画面に行く手段がなくなると言うことです。開発者の端末によってWebApplication#getHomePage()の値を変えて!と言うのは微妙なので、(間違えてこれでコミットするとかと言った心配もあるし、セッションを使う場合はどうするの?とか)WebApplication#getHomePage()では、特定のダミー画面を返すようにします。その画面から開発者任意の画面に飛ぶようにできるようにしてあげます。あとは運用モードのときはWebApplication#getHomePage()の返る値を本当の画面に変更してあげるだけです。
あと、通常のWebアプリで必要となるセッションが切れた画面だの、404のときの画面だの500系の画面だの、WebSessionのサブクラスについてはこちらで用意してあげて設定しておきます。こうすると最終的には学習者は作りたい画面だけを考えることができます。
ただ、言語の教育とフレームワークを使うと言うのは別の次元であるので、フレームワークの学習コストを少なくしてやる必要があります。今回使うフレームワーク自身の説明や、簡単なサンプルの構築に関してはやる必要があるのですが、自分の場合はその他によく書かれるであろう各種コーディングのサンプルを書いた資料を渡しました。サンプルを読ませて応用して書かせるわけです。僕は通常のプロジェクトでもこの手法を使います。あとはサンプルに外れた分に関してはWebで検索してもらうなり、教えるなりという作業になります。ただ、これをやってしまうと「こうしか書けない」的なバカ専資料になってしまいがちで資料がセントラルドグマと化しますので注意が必要です。なので、「普通は自分で調べるんだよ」と念をおしておきます。
あと、Wicketのどこまで触らせるか・・・ですが、言語教育だけを考えるなら、(Wicket自体は面白く、好きなのをあえて言うのですが)正直バリデーションは必要ないと思います。講習でJavaコードを作るのだからその辺も自前でチェックを作らせるのがいいと思います。プロパティモデルも画面の設計の仕方によっては、不恰好ですが特に必要ありません。もっと言うとアノテーションを使った各種仕組み。便利ですがフレームワークを学ぶのではありません。正直こういったところはフレームワークの機能を十分に発揮するより学習者の学習しなければならないことに注力したほうが良いです。フレームワークと言うのは単に手段であり、機能を十分に発揮するのが目的ではないからです。
Wicketのエラーメッセージ・・・意外と詳細に書いてあってすべて英語ですがなれると簡単!って思いますが、ここら辺がはじめに突っかかりますね。中学校程度の英語力で読めるので「そのくらい読め!」って思ってしまいますが、そこはグッとこらえてはじめは説明します。もしかすると、Wicketのエラーメッセージ集と対応策を作った方がよいかも。
データベースについて
データベースに関してはHibernateなりのデータベースフレームワークを使うなりでよいと思います。以前はJDBCを生で使わせて、Daoを設計するなら設計してみろ!ってやっていたのですが、(ある意味当然ですが)そうそうDaoを設計できるJava初心者なんて見たことがありませんでした。結果して、JDBCの使い方、特性、お作法ではまってしまう。そして、誰かが作って奇跡的に動いたコードを元にJDBCを使うコードがコピペで大量に埋め込まれる。コピペ元が悪ければ(クローズを忘れていたり、例外がおきるとクローズされないコードだったり)そのうち開発環境がハングして作業が止まる・・・なので、JDBCを生で使わすのもいいですが、時間との兼ね合いになるだけだと思います。
Java初心者にとって上記のようにJDBCの仕組みを勉強しつつJavaのコードを書かなくてはならないし、コネクションのクローズし忘れ問題で環境がハングアップしてしまう場合もあります。こういった問題は今覚えたとしても現実的に言ってそういったクリティカルなコードを書く場所にすぐには配置されない(配置したくない)ので「あぁ、昔そんなことがあったなぁ」と懐かしい目をされるようになるだけです。こういった本質には関係ない問題を一次忘れてもらうため、前述のフレームワークなどを使うのも良いと思います。もしくはSQLを投げるとMapのリストを返してくれるような簡単なユーティリティを作ってあげるとか。でもJDBCの仕組みの概要は教える必要がありますね。
その他
質問について
質問は個々の質問に答えていくのもいいですが、Tracのチケットを使ってチケット(疑問)に対する回答方式でやったりすると質問と回答のデータベースが出来上がります。ただ、「前に書いたけど」とかいった回答の仕方は最悪です。これは質問をしづらくするだけです。よくWebの掲示板なんかでは「質問する前にログを見るように」とか「ググレカス」的なコメントが見受けられますが、自分で調べるということも大事ですが、前と同じ答えを書くのもいいし、このチケット参照とするのもいいし、単に答えてあげたほうが良いかと思います。(同じ質問が来るということは、次の講習のときに質問される可能性があるので教える重点項目というのが分かります)
「めんどくさいので」「今は言っても難しいので」は禁句
まぁ、これは当たり前ですが、学習者のモチベーションを下げるだけです。学習者は講習にいる間は非常に高いモチベーションを持っています。(少なくとも教えてほしいと思っている人は)でもそのなかで上記のような言葉は学習者を傷付けるばかりでなく、講師との信頼関係も無くなります。難しいことでもいったん説明して、「自分の説明も良いとは言えないし、今はわからないかも知れないが、将来同じようなことに出会ったときに思い出してもらえればいい程度です」と言い換えています。
所感
正直面白いです。私はインストラクターなど職業的に物を教える立場ではないので(プロではないので)いつもどうやればいいか四苦八苦しながらしゃべっています。こういった仕事もできるんであればウチの会社も受けて来ればなぁと思ったり。彼らの姿は僕のいつか来た道。と、ここまで書いて、いつも面白いエントリで敬愛するゆりぽっぷ先生が以下のエントリを作っていたようです。あっちの方が面白いかも。
極道的研修のあらまし「引数と戻り値が分かりません」
http://d.hatena.ne.jp/yuripop/20090701
あー、そんな感じ!ですね。上記のエントリのトラックバックで、下記のサイトにすごく少ない文章でまとまったのがあります。これこれ!こんなことを書きたかったの。あー、自分の文才の無さを感じるよ。
IT系新人研修のポイント4つ
http://blog.craft-works.jp/uru/20090703/teaching-tech