7 実装

7.1 阿部の活動

(文責:阿部)

学生の画面遷移部分

これらに加えてWEBデザインと画像素材作成をした。

7.1.1 問題と解決

実装を進める中でぶつかった問題とその対処を挙げる。 細かい内容のものもあるが、新たに共同開発を始める人々の参考になればと思う。

7.1.1.1 テスト環境の必要性

今回、担当者部分を実装する川勝との進み具合の違いにより、学生部分の動作確認が難しいことがあった。担当者が出題しない限り、学生は回答することができないし、担当者が回答を締め切らないと、学生が回答できなくなったことを確認できない。当面の対処として、TestServletという学生部分の動作確認用のサーブレットをつくり、担当者のログインから出題、締切、授業終了までのの一連の流れを記述した。デバッガーを使い、ブレークポイントでスレッドを止めることによって学生画面の動作を確認した。

のちに、岸が先に作られたプロトタイプを利用して、 CUIとサーブレットで並行して担当者の操作をできるようにした。 ここでもCUIのプロトタイピングを行った価値があった。上記のような問題は共同開発を進める 上で、十分起こりうる事態であり、同様に開発を進めている場合には このようなテスト環境をあらかじめ用意しておくと良いと思う。

7.1.1.2 複雑な状態遷移

みるみるの学生用画面は、学生のアクションだけで特定の画面に切り替わるのではなく、そのとき授業がどういう状態なのか(出題中なのかそうでないのか等)ということよっても、次に表示される画面が変わってしまう。この複雑な状態遷移を頭の中できちんと整理するのは困難な作業だったが、状態遷移図を書くことがとてもその助けとなった。また今回は仕様変更によって遷移が何度か変わったので私がもっとも苦労したところでもある。

7.1.1.3 IDの型の統一

はじめ、lectureIdはint型でstudentIdはString型だった。 studentIdは文字列だが、lectureIdは内部的に番号が順に振られていたためである。 両方ともIDという名前なのに型が異なるのは、分担作業の中で混乱すると思い、 岸と相談してパラメーターを渡しやすいStringに統一した。

7.1.1.4 処理結果画面をどこまでまとめるか

サーブレットで処理した結果をRequestでJSPにわたすことで、成功画面や失敗画面を1つにまとめることができる。しかし、次の画面へのリンク先は異なる場合は、意味の違いを考えて別のJSPとした。例えば、新規登録に失敗した画面と回答に失敗した画面は「失敗」という点では同じだが、次にすすべき画面はそれぞれ、再度登録する画面と、出題を待つ画面(など)で異なる。

7.1.1.5 インターフェース

手動で画面を切り替える場合「更新ボタンを押してください」という表示があったが、この更新ボタンというのは ブラウザの再読み込みボタンと誤解しやすいので、「手動更新ボタン」と改めた。

また、新規登録の際に氏名を入力するフォームで、氏と名の2つの入力欄を横に並べたが、ブラウザによっては改行されて表示されてしまうことがわかった。 その場合は、何を入力したらよいのか混乱してしまうため、tableタグを使ってレイアウトをきちんと整えた。

7.2 川勝の活動

(文責:川勝)

7.2.1 担当部分

私が担当したのは担当者の画面遷移部分である。

図 7.2.1.1 teacherState

7.2.2 苦労した点

去年はほとんどモデルしか実装していなかったので、 ServletやJSPを本格的に実装するのは初めての経験だった。 今回は私にも大きな担当が割り当てられたので、責任感もあり、限られた時間で 自分ができることをやれるだけやる必要があった。 その結果、理解していなかったことが多く、遅くなってしまったがなんとか実装することができた。

7.2.2.1 Servlet

松澤氏の協力でServletのテンプレートが用意され、それに従って実装することになったが、途中までその仕組みを理解できないまま進めてしまっていた。モデルを呼び出す部分は良いのだが、Servlet特有の記述が理解できなかったので、SevletContextやDispathcerの役割を勉強して少しずつ理解していった。

7.2.2.2 JSP

JSPで苦労したのは最初の宣言の書き方とデータの渡し方である。htmlのように記述できるという点で便利なはずのJSPが逆に私にとっては苦労の原因になってしまっていた。データの渡し方はどんなデータをどの形式で渡すか、ということを自分で決める必要があった。これはServlet同様少しずつ本などで勉強していった。JavaBeansに関しては他のJSPを見ながら記述してしまったので、これから勉強していこうと思う。

7.3 岸の活動

(文責:岸)

が岸が主に開発・管理を担当していた部分である。また、ユーザテストに備え、システム全体のバグ探しも行った。

7.3.1 モデルとその制御

7.3.1.1 mirumiru.modelパッケージ

mirumiru.modelパッケージには、情報の実体を表すクラス群(私たちはモデルと呼んでいる)である。 分析モデルのクラス図通り、 授業(Lecture)、学生(Student)、質問(Question)、回答(Alternative)の4つが ある。

各人の個別実装によって、モデルの実装は3つずつできていた。 そして、途中段階のレビューがお互いに入っていたため、ほぼ同内容であった。 本実装には、その中で簡潔性のある阿部の実装をベースにすることになった。 岸がこれを制御部分とつながる様に改変することになった。 コードの書き方が統一されていたし、自分も同内容のコードを書いた経験を持っていたため、 阿部の実装を改変するのは簡単であった。 メソッドの名前改変程度ですんだ。

モデル部分は 仕様の変更後にもあまり変更されていない。 Lecture、Questionクラスに識別子が追加されたくらいである。 熟考して設計された成果であろう。

7.3.1.2 mirumiru.appパッケージ

mirumiru.appパッケージはmirumir.modelパッケージの制御・参照を行い、モデルの整合性を取る部分である。 また、WebアプリケーションやCUIのビューに制御のインタフェースを提供する。

この制御部分は個別実装時の岸のクラスが元になっている。 理由は阿部、川勝の制御部分と違い、 ビューの部分(文字の入出力部分)を分離した形で個別実装がなされていたからである。 Webアプリケーションのビューと連携を取ることが容易であった。 "コンソールで動くアプリケーションを実装するときで、 GUIを載せる可能性があるときは、 制御とビューは分離する方が再利用性は高まる" と良いだろう。

個別実装時とモデルが変わり、岸モデルが阿部ベースのモデルになったわけだが、 モデル機能はほぼ同じだったため、ほとんど改変はいらなかった。 呼び出すメソッド名と引数の変更を若干行った位であった。 モデルがレビューによって熟考されていた成果であろう。

7.3.1.3 制御の同期化

今回はWebアプリケーションである。ローカルマシンでプログラムが動いているわけでない。 通常、サーバサイドJavaの実装では、リクエスト毎の処理が並行実行される(マルチスレッド)。 したがって、制御の実装は並行実行に耐えるものではならない。

実装途中、ライブラリjava.util.ArrayListクラスによって実装されている、 リストにデータの削除と追加の両処理が並行実行されると、 例外が発生する症状を発見した。 最初は、「同期化を実装しないjava.util.ArrayListクラス」をやめ、 「同期化をするjava.util.Vectorクラス」に置き換えること で対処した。 しかし、研究会のレビューにおいて、 この解決方は、 "例外発生を防ぎ、局所的なデータの整合性は保てる。 しかし、ある瞬間にはシステム全体としては整合性の取れない、 処理途中のデータが存在し、それに触れることができる。" 旨の指摘を受けた。

そこで現在の対処としては、 「整合性に問題のある公開インタフェースにはすべてロックをかける(同時に1つしか実行されない)」 状態にしてある。 具体的には関連の公開メソッドすべて"synchronized"と明記してある。 関連の公開メソッドの処理それぞれを1トランザクションととらえた。 この実装は必要以上に同期化をかけている可能性があり、 性能をやや低下させていると考えられる。 必要最低限の同期化だけをする ことが課題として残っている。

現在の実装についてもう少し詳細に触れる。 mirumiru.app.MiruMiruApplicationのソースコードでは 同期化するメソッドが制御関連以外のデータ参照関連にも及んでいる。 これは一見無駄に見えるが、リストのコピーをする際に、 並行してリストのデータを削除などをすると、整合性の取れないリストがコピーされてしまう現象が 観察されたからだ。 リストのコピーには注意が必要である。

7.3.1.4 ファイルへの保存(ログ取り・データ保存)

ということを学んだ。それらについて順に述べる。


[ログファイル保存]

制御部分は、最低限のログをファイルに保存している。 これは、何か異常な事態があったときに後で解析/復元できるようにするためだ。 ログ取りは松澤氏から要求があり、それに納得し、応えたものだ。 ローカルテストであれば、コンソールにログを出力してもよいが、 システムを実際に稼動した後であれば、 ログをしかっかりと残しておかなければ、 問題がおきても対処が難しい。 残念(幸い)ながら、まだこのログファイルは大きな活躍はしていないが、おおきな安心感は得られた。


[2箇所にデータ保存]

2箇所にデータの保存することについても松澤氏から要求をもらった。 データは適宜、ファイルに保存することで永続化している。 しかし、ファイル保存中にシステムが止まったときは、保存中のファイルは破壊される可能性がある。 これを回避するために、保存ファイルは2つ用意し、 最終変更のタイムスタンプを使って、交互に書き込みを行うようにした。 また、実行環境や状況により保存の必要性のない/保存できないときは設定ファイルにより、 非保存モードにすることもできるようにしてある。


[環境依存のファイル保存位置の弊害]

実は今回、ファイルの保存場所が原因で、Linuxサーバに設置する際に問題が起こった。 当初の実装ではログおよびデータのファイル保存先が実行環境依存になっていた。 Linuxサーバではシステムが書き込み権限のない場所が保存先となり、 例外を出力する結果となった(もちろん例外処理をしない実装のも問題がある)。 開発マシンはパーミッションを指定しないWindowsXP/2000を利用していたので、 このときは、問題がなかった。 この問題に気がついたのは、Linuxサーバにあげる少し前だった。

対処としては、 実行時に、環境から、 アプリケーションの保存用ディレクトリの実際のパスを取得し、 そこに保存する実装に変えた。 開発環境だけでなく、実際の稼働環境にも目を向けず、痛い目にあった体験であった。 実行される環境への配慮は重要である。

また、バージョン管理のツールCVSなどを利用して共同開発をしている場合は、 ログやデータ保存はバージョン管理外ディレクトリしないと、 開発メンバーに迷惑をかけるので気をつけたい。 CVSのサーバに自分の実行ログなどアップロードされてしまうからだ。 通常、ログや保存データの中身はその実行環境用のもので他の人には不要であり、 他の人のバージョン管理を邪魔する。 ちなみに、私はこれで怒られた。

7.3.1.5 仕様変更・追加による影響

実際にサーブレット周りの実装物ができあがっていく過程で、 仕様の、 決まっていなかった部分、 変更された部分が出てきた。 それに伴う主な実装の変更は、

などである。 一つ一つの変更・追加による影響・追加作業は意外と小さいものに押さえられた。 などがその要因になるだろう。 本実装の前に、統一したコードの書き方を取り決めていたため、 ソースコードはどこに何が書かれているが把握しやすく、すぐに修正部分が見つかった。 また、各実装各部分の役割がはっきりしていたことで、他の部分をあまり気にせずに修正できた。

システム開発において、顧客の要求はよく変わるものである。今回もある程度それを実感できた。 後からの修正を十分考慮し、実装しておくことは、大切である。

7.3.2 学生自動遷移部分

7.3.2.1 自動更新の仕組み

本アプリケーションでは, 学生の画面を授業の状態変更(出題、回答締め切り、授業サポート終了など) によって自動的に遷移させることができる。 この機能は、わかりやすい操作につながる大きな特徴となっている。

通常、アプリケーション側の持つ状態をアプリケーション側から即座にユーザへ伝えるというのは、 HTMLで動くWebアプリケーションでは困難である。 Webチャットシステムでは、 ユーザ(のブラウザ)が一定時間毎に自動的にアクセスしなおす仕組みにより、 システムは擬似的に新しく書き込まれたメッセージをユーザに伝える。 本アプリケーションではユーザ(のブラウザ)側でTCP/IPのソケット通信をし、更新イベント受け取る JavaAppletを利用することにより、サーバ側からWebクライアントに状態を伝えている。

この方式では、Fire Wall(パケットフィルタリング)にソケット通信が阻害される恐れがある。 サーバおかれる環境のFire Wallには要注意である。 幸い、今回CreWのサーバを利用させてもらった、問題はなかった。 使用ポートが遮断される環境にインストールすることを想定して、 設定ファイルでサービスポート番号を変更できる実装をした。 たまたま、該当のポート番号が使えない場合には回避できるので 利用できる環境の幅を広げている。

7.3.2.2 利用環境への依存度の低さ

今回は主なターゲットのブラウザををユーザの最も多いInternet Explorer+Windowsにした。 対応環境を焦点を絞っておくことで開発をしやすくなる。 しかし、同時に様々なブラウザに対応できる工夫もしてある。 もちろん、すべてのブラウザで動くわけではなく、 "Internet Explorer 5 for Macや Netscape 6.2(Windows XP)で怪しい動作も確認された。


[手動版との併用が可能である]

本アプリケーションはサーバでの処理の結果であるHTML以外に クライアントで実行するJava AppletやJava Scriptが利用されている。 しかし、クライアント(ユーザのブラウザ)は多種存在する。 したがって、Java AppletやJava Scriptの動作保証はない。

そこで、本アプリケーションはほとんどの環境で利用を可能にするため、 HTMLだけで動作し、自分で更新ボタンを押す、手動利用が可能である。 自動更新部分は手動利用を阻害しないように実装してある。


[Java Appletの利用環境の広さ]

自動更新版が利用できる環境に工夫がしてある。 JavaAppletを実行するVM(Virtual Machine)は、ユーザの利用するブラウザによって様々だ。 バージョンの違いに加えて、サンマイクロシステムズ社が提供するものと、マイクロソフトが提供するものなどがある。 一般によく使われているブラウザInternet Explorerはマイクロソフト社のVMが標準設定されていることが多い (SFCの特別教室のZXP???はそうなっている)。 しかし、マイクロソフト社のVMはメリーランド州ボルチモアの連邦地裁での訴訟の問題などで、 APIのバージョンが古いものしか処理できない。 開発環境はのJDKバージョンは1.4.1以降としたが、 多くの環境で自動更新版使えることを望み、 アプレットの実装にはバージョン1.1相当のAPIのみ利用している。

7.3.2.3 Applet関連技術調査

Java Appletがどのようにブラウザによって動作を規定されているかの調査および Webページ更新の実験を松沢氏をとともに行った。 そして、サーバと連動した簡単なページ表示プログラムを完成させ、自動更新のベースとした。 技術的に不安の残る部分は、小さなプログラムを作ることにより、 早期に全体像をつかんでおくと、後の実装でつまずくことが少なくなるだろう。


[アプレットライフサイクルの調査]

Appletにはinit(),start(),stop(),destroy()などのブラウザの動作にあわせて呼ばれるメソッドがある。 ブラウザのページ変移にともなってこれらのメソッドが実際に呼ばれるタイミングに関しての資料が手に入らなかった。 そこで、主なターゲットとしているInternet Explorer(windows2000)でそのタイミングを実験によって得た。 ここからわかったAppletの動作は、

というものあった。 ブラウザによって動作は多少違う可能性があるが、 この結果は、 "自動更新用のAppletが、自分自身が表示されているページを更新してしまうと、処理が続けられない" ということ意味し、 これからの実装に困難さを与えるものだった。

しかし、問題がはっきりしたので、あとは解決さえすれば良くなった。 今回はブラウザのウインドウ内に、ユーザには見えない位置にごく小さなフレームで区切られた領域を確保することで この問題を最終的には解決した。 "わからないことは、実際に試してその結果から知る"というのも、大事な手法であろう。


[ページ更新のサンプルプログラム]

Appletの動作がわかったところで、実装を試したことのない、 Appletとサーバとの通信およびAppletのページ表示を簡単なプログラムを書き、 試してみた。このサンプルプログラムによって実装の不安感は取り除かれた。

7.3.2.4 mirumiru.autoupdator.appletパッケージ

このパッケージはアーカイブされ(/jsp/smartwindow/mirumiruapplet.jar)ていて、 実行はクライアントサイドで行われる。Microsoft VM for Javaでの動作に配慮されて実装した。 主な動作は、 mirumiru.autoupdator.server.MiruMiruServerクラスから 授業の状態変化のイベントをソケット通信で受け取りると、 画面にイベント通知のメッセージを表示し、ブラウザ内のページを更新する。

イベント通知のメッセージの表示の仕方はMicrosoft社のWindows Messenger風に、 ウィンドウが画面にスライドしてくる方式をとった。 「スライドウィンドウ=通知」というイメージを Windows Messengerユーザが初めから持っていると考えた。 この人たちにとっては、わかりやい通知メッセージとなるだろう。 Windows Messengerとウィンドウの表示位置が違うのは実装上の都合である。

なお、ログアウト系のイベント通知でウィンドウ表示をすると JRE1.4での実行において、ブラウザが動作しなくなる現象が現れたため、 現在はログアウト系のイベントは通知しないようにしてある。 詳しい原因はまだわかっていない。 保守・改変する時は要注意である。

7.3.2.5 自動更新の画面遷移

学生の自動更新版で表示される画面は基本的には手動更新版のものをそのまま利用している。 したがって、自動更新版の画面表示の部品の再開発を防ぐ意味がある。 ただし、ログインとログアウトの部分は画面遷移を分かり易くするために、 別の画面を用意している。

自動更新部分の画面遷移は、初めから完全に決まった状態で実装に入ったわけではない。 実装のしやすさとわかりやすい遷移のバランスをとりながら、途中何度も変更した。 技術的に困難な箇所を避けられるメリットは大きいが、 遷移に関するドキュメントを逐次更新して開発メンバーに公開しないと、 他のメンバーが該当部分の遷移を把握できない可能性がある。 実際、途中段階の遷移図を更新しなかったため、 開発者以外、テスト・デバッグができかった(正解の遷移図がないため、動作が正しい判断できない)。 これが反省点である。 自動更新も含んだ修正版ログインの画面遷移図を図[修正版ログイン画面遷移]に示す。

図 7.3.2.5.1 修正版ログイン画面遷移

7.3.2.6 Appletのリファクタリング

Applet部分は、途中、実装の追加・変更を繰り返したため、 特に複雑化してしまった。 そこで、リファクタリングを行い、ソースコードの可読性の向上を図った。 その際、松澤氏に指導していただいた。

ここでのリファクタリングで学んだことで印象深かった事柄を列挙する。

・並べて書く処理は粒度を統一する
構造が把握しやすくなる。
粒度をそろえるために処理のメソッド化を利用すると良い(例えメソッドの処理内容が少なくても)。
・メソッド名と処理の中身の対応には注意を払う
メソッド名から連想される処理と実際の処理とに差があると、思わぬ勘違いを起こしやすくなる
逆にメソッド名とその処理の内容がうまく対応すれば、コメントがなくても十分な可読性が確保できる
・try文は大きなブロックにかける
tryのなかには正常時の処理が並べられるので、これを大いに利用すると、例外処理がない見やすいコードが書ける。
・仕組みがわからないで、とりあえず動くだけのコードは危険である。なるべく仕組みを知ろう
目の前で動くコードが、何らかの実行環境の変化(関連ソフトウェアのバージョンアップなど)で動かなくなるかもしれない。
たまたま動いているだけかもしれないのだ。
また、このリファクタリングでの感じたこともあげておく。
・局所的な最適化より全体的な最適化をするとよい
たとえば、変数やメソッド名前ひとつとっても、タイピングの手間を考えたら、短いほうが早く実装できる。
しかし、デバッグや保守・拡張改変などで、ソースコードを後で何度も読み返す可能性は高い。
開発全体を考えたら、可読性をとって、十分わかりやすい名前をつけるべきである。
・可読性の視点で見ると、メソッド化は動作・処理に名前付けをすることである
・実装物は小まめにローカルから共有されているネットワークスペースに上げるべきである
リファクタリング途中、松澤氏のPCが動作しなくなり(起動不能)、作業途中のファイルを入手するのに苦労をした。
開発マシンにいつ何が起こるかわからない。そのときの保険にもなるので、小まめにCVSにあげるべきである

7.3.3 デバッグ

7.3.3.1 モデルと制御部分

モデルや制御部分のデバッグには、小さなテストプログラムを利用した。 試したい内容を処理して結果をコンソールに出力するプログラムである。 スレッド(並行処理)プログラムを利用し、 複数のユーザの同時アクセスと同様の状況も作り出したこともあった。

7.3.3.2 Appletとバージョン管理

Appletは実行される環境がクライアント側(ブラウザ)であり、ブラウザはキャッシュをよく行うので注意が必要だ ここで役に立ったのは、AppletのコードおよびAppletを起動するhtmlにはバージョン番号をつけたことだった。 これをコンソールに表示されることで、実際にそのとき動いているコードがどののバージョンなのかが把握できる。 また、どのインスタンスが動いているかを把握するためにhashCode()メソッドの利用も効果的だった。

Appletに限らず、 バージョン番号およびインスタンスの把握は 何が起こっているかを知る糸口となるので、大切だ。 実際、

などを解決するのに活躍した。

7.3.3.3 Web画面遷移のCUIとの連携

本システムの学生画面遷移は複雑で、 学生の操作と担当者操作によって変化する授業の状態によって様々に変化する。 私は自動更新での画面遷移をテストする必要があった。 しかし、実装は分担され、並行に進められていた。 テストするにもWebアプリケーション全体が完成してない状態では、 様々なテスト状況はそのままではつくれないでいた。

初期にとった解決策は、 担当者の動きをシミュレートするプログラムを用意するものだった。 しかし、これでは、単純な状況を繰り返すことしかできなかった。

そこで、プロトタイピング時につくったCUIのビューを利用するというヒントを松澤氏からいただいた。 岸のCUIビューを制御部分につなぎ、Webアプリケーションとしてブラウザからアクセスすることも、 コンソールでCUIを使ってアクセスすることも両方可能にした。 CUIビューには必要なユースケースを実現するインタフェースがすべて用意されていたので, あらゆる状況が作り出せた。 CUIのビューがある場合、これを利用することでデバッグを大いに助けられる。 デバッグ用CUIはmirumiru.cuiパッケージに今回用意してある。

CUIビューのWebアプリケーションデバッグへの利用で注意する点をあげる。

・CUIビューと制御部分が分離された構成をとる必要がある
制御部分はCUIビューとWebビューとで共有する
・CUIの処理を受け付けるサーバとCUIの入出力を請け負うクライアントの別プログラムが必要である
わりと実装がしづらいプログラムであるが、サーバとクライアントは汎用のものを作成しておけば再利用できるはずである(今回は残念ながら十分な汎用性はもたせなかった)

利用価値は十分あるが、 利用の仕組みを用意することは意外と大変で、労力を必要とされる。

7.3.3.4 システム全体のバグ探し

第2回ユーザテストに備え、システム全体のバグを見つけチェックリストを作成し、対処を促した。 バグ探しは、 基本的には、仕様でのシナリオを満たすかどうかを実際システムを動かして判定した。 時には、ソースコードを眺めて、問題の起こりそうな箇所を重点的にテストした。 そのときのチェックリストを図[バグチェックリスト]にしめす。

図 7.3.3.4.1 バグチェックリスト

今回、バグを探しながら、別メンバーが実装を続けていたので、 以前に確認した動作が、その時点でもまだ維持し続けているか、疑問を抱くこともあった。 また、行き当たりばったりのテストも多々あった。 適切で十分なテストケースの作成とドキュメントというのがひとつ課題として残るだろう。 適切なテストケースが作成されドキュメントとして残っていれば、 適宜、それを全部こなせば、一応は正常に動作していると言え、安心感が得られるだろう。