レベル: 中級 山元 和斗志, , リコーテクノシステムズ株式会社
2004年 5月 28日 実際に動作する掲示板ソフトの開発をとおして、アプリケーション開発の上流過程からコーディングまでを実践的にご紹介します。
※この講座はリコーテクノシステムズ株式会社 山元和斗志様の投稿講座です。
0. はじめに
皆さまいかがお過ごしでしょうか。
掲示板システムをWebアプリケーションで構築するまでを、設計段階からご紹介するレポートの第2回をお届けします。
第1回レポートでは、大まかに掲示板の機能を洗い出し、設計をするところまでをご紹介しました。
この第2回レポートでは第1回の内容を踏まえて、掲示板の基本的な機能を作り上げていく過程をご紹介します。よろしくおつきあいください。
この掲示板を構築するにあたり、Jakartaプロジェクトの偉大な成果の一つであるStrutsを使用します。
Strutsに関連する基礎的なことについては触れませんので、詳細は下の「Strutsに関連する基礎情報」を参照してください。
なお、Jakartaプロジェクトの成果物を和訳している有志「Ja-Jakartaプロジェクト」も、あなたの助けになることと思います。筆者もお世話になっております。
Jakartaプロジェクト (英語)
Strutsに関連する基礎情報 (英語)
Ja-Jakartaプロジェク
1. 画面遷移を考える
今回の開発範囲は前回のユースケース図を参考に、掲示板主要機能を対象にします。
すなわち、
となります。
また、詳細な画面遷移図と、Struts-configに記述するアクション名及び関連クラスを定義します。
Struts-configの書式を一部、図1-1と図1-2に示します。
図1-1. Struts-config/actionの書式

図1-2. Struts-config/form-beanの書式
Struts-configが完全に記述されている場合は、登場するクラス、及びJSPを定義して行くこともできるのですが、あくまでもシステム開発を行うのは人間ですので、やはりXMLよりは図で理解して、XMLに書き写すほうが得意です。また、システム開発の成果はプログラムのみではなく、その設計や仕様を明確にする文書も含みますので、その文書を作成する意味でも、図を描くことは決して損ではありません。
早速、基本的なユーザーの使用できる機能について、図にまとめてみましょう。
画面遷移図を考える
図1-3. 画面遷移図(ユーザー権限)
ログイン画面を除く全ての画面には「メニュー 掲示板」が表示されています。画面の右側で、その画面の機能を満たしている、というデザインになっています。矢印は画面遷移を表現していますが、ここでは基本的に画面遷移がサーブレット処理を経由する、という前提で考えておいてください。
なお、ログイン後のメニュー画面のソースは次のようになっています。
図1-4. JSPのソース
図1-3を見ると「メニュー 掲示板」という部分が常に存在します。同じ記述を繰り返すのは非常に面倒ですし、メニューに関する少々の変更が、莫大な工数を生み出す可能性を秘めていますので、共通の部品として利用しようと考えました。
menu_list.jspがメニューの一覧(掲示板一覧を含む)です。information.jspは「利用上の注意」です。
ここで、アクション名とJSPファイル名を図1-3に追加します。
全てのJSPにheader.jspとfooter.jspがincludeされているものとしてください。これは、全てのJSPに共通のヘッダー・フッターのファイルである、という位置づけにします。
図1-3a. ファイル名とアクション名を追加
入力項目を考える 今度は、画面遷移に伴う入力について考えて見ましょう。
基本的に、ユーザーは画面のリンクをクリックするだけなのですが、例えば(2)の遷移で、掲示板を選択した場合は、掲示板IDがサーバーに送信され、掲示板IDを元にSQLが組み立てられ、選択された掲示板に存在するスレッドの一覧がデータベースから取得される仕組みが実装も楽にできそうです。
また、ログインの(1)とスレッドの作成の(7)及びメッセージの作成の(5)は、より多くの入力項目を必要とすることは、この段階でも容易に想像できます。入力項目を完全な形で洗い出すことは、この段階では無理ですので(そして、完全である必要もありませんので)簡単な表にまとめて見ましょう。
図1-4. 入力項目の洗い出し
| (1)ログイン | /login | ユーザーID、パスワード | | (2)スレッド一覧 | /board | 掲示板ID | | (3)メッセージ一覧 | /thread | 掲示板ID、スレッドID | | (4)メッセージ作成に移動 | /go_new_message | 掲示板ID、スレッドID、メッセージID | | (5)メッセージ作成 | /new_message | Entityクラス:Messageに準じる | | (6)スレッド作成に移動 | /go_new_thread | 掲示板ID | | (7)スレッドの作成 | /new_thread | Entityクラス:Thread及びEntityクラス:Messageに準じる |
ここで明らかになった共通点などは、次にサーブレットクラスを分析する段階で、重要な資料となります。
2. サーブレットを考える
ここまでで、基本的な機能と、それを実装するクラスの分担の骨格が出来上がりました。
Strutsフレームワークを使用して開発をする予定の掲示板システムですので、図1-3aの画面遷移毎にサーブレットのセットを定義していく必要があります。
Strutsのサーブレットは次のようなセットが一単位で管理されます。
図2-1. Strutsのクラスのセット

Strutsフレームワークの基本構造が持つ特徴として、呼び出し元のJSP(HTMLも可)から呼び出されるサーブレットはorg.apache.struts.action.Actionクラスの拡張クラスである、ということが挙げられます。
そのため、図2-1ではActionクラスと表記してあります。
Formクラスは、Actionクラスと対を成す重要なクラスで、画面からの入力内容を保持して、Actionクラスの内部で扱えるようにしてくれます。これは、入力項目毎に独自の内容にする必要があります。
これはorg.apache.struts.action.ActionFormクラスの拡張クラスですので、Formクラスと表記しました。
ビジネス・ロジックはデータベースからの値の取得、データベースに対する更新処理などを行うクラスです。
その内容ごとに独自の内容を記述するため、このクラスの開発が、システム開発のキモということができるでしょう。Strutsフレームワークの目的の一つは、開発者がビジネス・ロジックの開発に集中できる環境の提供ですから、その重要さが想像できるでしょう。
このビジネス・ロジックは、システム毎、いや、システムの機能毎に独自の内容となりますので、Strutsフレームワークはノータッチになります。名称、拡張すべき親クラス・・・なにも制限はありません。
それでは、機能を実現するためのクラスを、画面遷移図及び入力項目の一覧から考えてみましょう。
クラスの洗い出し Strutsベースで開発を行う場合に、最も簡単にクラスを洗い出す方法は、まずアクション名にちなんだセットを全て記述し、その共通点や相違点をチェックして、分割や統合を行う方法です。
潔く分割や統合を行わないのも一つの手ではありますが、設計があまりきれいにならないことと、クラスの分割、統合についてのセンスを磨くチャンスをみすみす逃すようなものなので、納期や、そのほか大人の事情が差し迫っていない場合は、統合や分割、名称の変更を検討することをお勧めします。
まずはアクション名にちなんだセットを全て記述します。
図2-2. Strutsクラスセット
| アクション名 | Actionクラス | Formクラス | ビジネス・ロジック |
|---|
| /login | LoginAction | LoginForm | ユーザー認証、掲示板一覧の取得 | | /board | ThreadListAction | ThreadListForm | スレッド一覧の取得 | | /thread | MessageListAction | MessageListForm | メッセージ一覧の取得 | | /go_new_message | GoNewMessage
Action | GoNewMessage
Form | メッセージ作成画面に移動、返信先メッセージ取得 | | /new_message | NewMessageAction | NewMessageForm | メッセージを登録 | | /go_new_thread | GoNewThread
Action | GoNewThread
Form | スレッド作成画面に移動 | | /new_thread | NewThreadAction | NewThreadForm | スレッドを登録 |
この図2-2と、図1-4を横に並べて見ると、入力項目の共通点=Formクラスの共通点であることを利用して、クラスの統合を行うことができます。
- ログインに必要な情報を保持する
- スレッドの作成に必要な情報を保持する
- メッセージの作成に必要な情報を保持する
- 掲示板ID、スレッドID、メッセージIDを保持する
の、4種類にFormを統合すると、図2-2のFormクラスがぐんと数を減らし、管理が楽になります。
同時に、クラスの分割についても考えて見ましょう。
/loginで実行されるビジネス・ロジックの欄には「ユーザー認証」と「掲示板一覧の取得」が記述されていますが、例えばLoginLogicクラスの中で掲示板一覧を取得するコードを書くことは、クラスの役割を明確にしようという考え方に反しますので、分割することにしましょう。
また、メッセージを登録、スレッドを登録した直後には、それぞれメッセージの一覧、スレッドの一覧を表示する必要がありますので、そのアクションを追加しましょう。
これは、メッセージを登録した直後、ユーザーがブラウザの「F5」キーでリクエストを再送信した場合に、キーの重複などでエラーとなることを防ぐための措置です。もしも、プライマリ・キーを設定していないデータベース設計の場合には、キーの重複でエラーにならない代わりに、全く同じ内容のレコードが何度でも登録されることを意味しており、どちらにせよ、この措置は必要なものです。
そこで、図2-2に以下を追加するものとします。
図2-3. Strutsクラスセット
| アクション名 | Actionクラス | Formクラス | ビジネス・ロジック |
|---|
| /go_board | GoBoardAction | GoBoardForm | スレッド一覧を取得 | | /go_thread | GoThreadAction | GoThreadForm | メッセージ一覧を取得 |
既にお気づきとは思いますが、この二つのFormクラスは、結局掲示板ID、スレッドIDを保持していれば機能として十分なため、図2-2のFormクラスについて考察した部分の「4」に当たります。
こういった分割・統合・追加を繰り返すことで、クラスが洗練されていきます。
余談ですが、工場のベルトコンベアから生産されてきたものを工芸品に仕立て上げていく過程に似ています。
筆者がしつこくしつこく洗練した結果、最終的には、図2-4のようになりました。
図2-4. Strutsクラスセット改訂版
| アクション名 | Actionクラス | Formクラス | ビジネス・ロジック |
|---|
| /login | LoginAction | LoginForm | ユーザー認証 | | /board | ThreadListAction | ViewForm | スレッド一覧の取得 | | /thread | MessageListAction | ViewForm | メッセージ一覧の取得 | | /go_new_message | MessageAction | ViewForm | メッセージ作成画面に移動、返信先メッセージ取得 | | /new_message | NewMessageAction | NewMessageForm | メッセージを登録 | | /go_new_thread | MoveAction | ViewForm | スレッド作成画面に移動 | | /new_thread | NewThreadAction | NewThreadForm | スレッドを登録 | | /returnMenu | MoveAction | ViewForm | 掲示板の一覧を取得 | | /go_board | MoveAction | ViewForm | スレッド一覧を取得 | | /go_thread | MoveAction | ViewForm | メッセージ一覧を取得 |
視覚的に統合の様子がわかるように、色をつけておきましたので、図2-2の紹介から、ここまでの文中で変更された内容が正確に反映しているかどうか、確認してください。
なお、文中に記述されておらず、図2-4で変更されているのは、
- 掲示板ID、スレッドID、メッセージIDを保持するFormクラスをViewFormとする
- メッセージ作成画面に移動するアクションは、第1回レポートの図2に準じて、返信する対象の情報を取得するため、MessageAction、MessageFormに名称を変更
- 「移動するだけ」のアクションはMoveActionに統合し、コーディングで工夫することとする
の3点だと思います。
ビジネス・ロジックについて、まだ曖昧なままですが、とりあえず図1-1、図1-2にあるような、設定ファイルを記述する上では、ビジネス・ロジックの名称や役割による洗練(=分割・統合・追加)は優先順位の低い内容になりますので、スペースの関係上、今回はここまでにしておきます。
しかし、ビジネス・ロジックの洗練は、それ自身の再利用性について非常に大きな影響を与えますので、決して軽く考えているのではないことを、ご承知おきください。
クラスの命名と役割について ここから先(次回以降も含めて)、ビジネス・ロジックを記述するクラスの名前として「*****Logic」が多く登場します。世の中には「ビジネス・ロジックはBeanだろ!」という原理主義の方もいらっしゃいます。
しかし、筆者の頭の中ではBeanはJavaBeanを意味するので、BusinessLogicを記述するならLogicクラスなわけです。このあたりは命名規則などの兼ね合いもありますので、Biz001Logicだとかいう正体不明のクラスとお付き合いなさっている方も多数いらっしゃると思います。押し付けるつもりはありません。
実際、筆者もInju10DBBeanなどのクラスと長い間不可侵条約を結んでいたのです。
このような、クラスやメソッドの命名法、クラスの集約や分割といった、頭痛の種に良く効く、半分がやさしさでできている、有名なあの薬のようなすばらしい書物を紹介しておきます。
リファクタリング[プログラミングの体質改善テクニック]
マーティン・ファウラー 著
児玉公信、友野明夫、平澤章、梅澤真史 訳
ピアソン・エデュケーション(Object Technology Series 10)
あらかじめ白状しておきますが、私の「事前の完璧な設計は必要ない」という主張はこの本の受け売りです。
早速書店に駆け込み、書物に夢中になって、ここから先を読まない方もいらっしゃるかもしれませんが、筆者はその隙にstruts-config.xmlを作成して、実際にコードを書き始めます。
3. サンプル・コード
さて、今回のサンプル・コードはSQLを実行してDBを参照したり更新したりするLogic系クラスを紹介させていただきます。Logicクラスはつまるところビジネス・ロジックの記述ですので、最も手のかかるクラスのうちの一つです。多くの方が頭を悩ませ、それぞれに工夫を凝らされていることでしょう。
そんな工夫のうちの一つである、程度に考えて、肩の力を抜いていきましょう。
SQLは参照系と更新系
ご存知の通り、SQL分の種類はSELECT、INSERT、UPDATE、DELETEの4種類と考えています。
- SELECTを参照系とします。参照系SQLを実行すると、ResultSetを返します。
- 更新系であるINSERT、UPDATE、DELETEを実行すると、結果として「影響を受けた行数」を返します。
- DBへの接続は適切に作成・破棄をしなければなりません。
ということから、以下のように「掲示板の一覧を取得するLogic」のクラス図を考えました。
図3-1. 掲示板の一覧を取得するLogicのクラス図

DBへの接続やその破棄などをLogicクラスに記述します。このことで、テスト環境から本番環境への移行など、アプリケーションとDBの関係が変更になっても、対応できるようにします。
このLogicクラスの作り方によっては、Logicクラスに変更を加えるだけで、OracleからDB2に乗り換えることになっても大丈夫になります。
QueryLogicとUpdateLogicを分けた理由は、更新系の処理で「3つのテーブルを更新するが、途中で更新ができなかった場合は全てRollBack」といった処理を想定したためです。必ずResultSetが返され、それについての処理を行うQueryLogicとは明らかに役割が違います。
ここでは「掲示板の一覧を取得するLogic」について話を進めますので、UpdateLogicについては、とりあえず忘れてしまって結構です。
BoardListLogicクラス BoardListLogicのexecuteメソッドが呼び出されると、内部に保持されたSQL文が実行され、ResultSetが返されますので、その中身を取り出してBoardクラスに格納します。
システムに、掲示板は複数存在しますのでBoardクラスはArrayListに格納され、executeメソッドの呼び出し元に返される、という処理になるのですが・・・。
もしかして、こんなことしていませんか?(図3-2参照)
図3-2. こんなこと

ここで、実行されているSQLは「SELECT board_id, board_name, board_type FROM bbs_board」とします。
今回は項目が3つなので良いとしても、もし60項目あったらどうでしょうか?筆者は247項目を取得する機能に直面したことがあります。あれは、正直言って悪夢でした。
しかも、徐々に徐々に「取得すべき項目」が増加して247までに増加したので、途中のタイプミスでsetメソッドにnullが入ったりして、苦労したものです。
そこで、Jakarta-Commonsのパッケージの一つであるBeanUtilsを紹介します。これはいいものですよ!
BeanUtilsのテストクラス なお、BeanUtilsを利用するには、同時にCommons-Loggingパッケージと、Commons-Collectionパッケージも必要になりますので、jarファイルを入手してください。
パッケージは、このレポートの冒頭に紹介したJakartaプロジェクトのサイトから入手できます。
というわけで作りました。

throws Exceptionの記述はコードの簡潔化のためにつけています。
本来は、以下のExceptionをコードの中で処理するようにしてください。
| IllegalAccessException | | InvocationTargetException |
HashMapを作成してArrayListに格納します。
Iteratorを用いて、先ほど格納したHashMapを取り出しています。
ここで作成したTestBeanのインスタンスに、BeanUtils.populateメソッドを利用して、値を代入します。
最後に文字列表現を出力します。 |
TestBeanクラスのtoStringメソッド(文字列表現を返すメソッド)は、

のようになっております。また、これに対応した各Get/Setメソッドも存在するものとします。
メソッドの名前は、フィールド「xxxxx」に対して「getXxxxx/setXxxxx」でなければなりません。ご注意を。
実行結果は次のようになりました。

というわけで、BeanUtilsパッケージを利用することで、ResultSetの内容をEntityクラスに格納する作業がぐんと楽になるどころか、数行で片付いてしまうようになりました。取得すべき項目が増加してもコードの量は増えませんし、タイプミスも(DBのカラム名を書かないのですから)発生しません。
慎重にSQLと、Entityの対応するフィールドを書き換えれば、あっという間に変更に対応できるのです。
テストクラスでは、はじめからHashMapに値が入っていましたが、SQLの実行結果としてはResultSetが呼び出し元に返されますので、それをHashMapに入れ替え、ArrayListに格納しておく必要があります。
このあたりは、そんなに難しくないですので、筆者が使用しているResultSet -> ArrayList(HashMap入り)の変換メソッドを紹介しておきます。

このメソッドは、実際にサンプル掲示板で使用されており、QueryLogicクラスに記述されています。
縦に長くなるので、コメントも削除してあります。読みにくくて申し訳ないです。
4. 今回までの成果
今回、開発の対象とした部分(図1-3a)を、せっせと作ってみましたので、画面キャプチャーをご覧にください。
筆者の趣味で、掲示板の名前とロゴも作ってみました。
図4-1. ログイン画面

図4-2. 利用上の注意

図4-3. スレッド一覧

図4-4. メッセージ一覧

図4-5. スレッド作成画面

図4-6. メッセージ作成画面

Strutsフレームワークと便利なCommons系パッケージを利用した開発がすこしづつ進行しています。
「掲示板」はDBに直接書き込みましたが、スレッドはアプリケーションを通じて作成しました。筆者と筆者の同期は早速「独身"漢"料理スレッド」で情報交換をしております。(図4-4を拡大鏡でご覧ください)
同期に教わった"漢"料理を試しますので、今回はここで切り上げましょう。腹が減っては戦ができません。
よろしければまた次回、お会い致しましょう。
おつかれさまでした。
参考文献
著者について  | |  | 山元 和斗志,リコーテクノシステムズ株式会社 |
記事の評価
|