スパイラル1は最初のとっかかりを作る。 ここでも、きちんと要求分析、システム分析、設計、実装、テストの工程を実行する。
要求分析とは「何を作るのか?」を明確にする工程である。
自分がこれから作ろうとしているソフトウェアがどんなものであるか、明確に定義することが目的となる。
要求分析は、以下の3つの手順からなる。
このシステムで登場するユースケースは2つである。 また、登場するアクターはユーザとデータベースの2つである。
ユーザは両方のユースケースに関係する。 データベースも、起動時にも予約のリストを得るために使うので、両方のユースケースに関係する。
- 年と月と会議室を入力して表示ボタンを押す。
- 表示ボタンが押されたことが制御パネルに通知される。
- 制御パネルは、年と月と会議室名を取り出して、その情報をシステム管理者に通知する。
- システム管理者は、年と月と会議室の情報をデータベース管理者に渡して、予約リストの取り出しを依頼する。
- データベース管理者は、指定された年月と会議室の予約リストをデータベースから取り出しシステム管理者に渡す。
- システム管理者は予約リストをカレンダー管理者に渡して、表示の更新を依頼する。
- カレンダー管理者がカレンダーを更新する。
- 利用者が会議室予約システムのページを開く。
- 制御パネルはデータベース管理者に会議室リストを要求する。
- データベース管理者はデータベースに問い合わせて会議室リストを得て、制御パネルに渡す。
- 制御パネルは会議室のドロップダウンメニューを更新する。
- 制御パネルは現在の年と月の値をテキストボックスに設定する。
- 制御パネルは、現在の年と月と“全会議室”を使ってシステム管理者にカレンダーの更新を依頼する。→ここから先は予約の閲覧と同様。
シナリオとは、ユースケースが実際に実施される際の様子を述べる文章である。 1つのユースケースに対して複数のシナリオが考えられるが、代表的なものを書けばよい。 また、例外フローがある場合、それについてのシナリオも作成する。(今回は省略)
太郎君は2015年10月の会議室Cの予約状況を見るために、年に2015、月に10、会議室名に会議室Cを入力して表示ボタンを押した。表示ボタンが押されたことが制御パネルに通知された。制御パネルは自身の部品から年と月と会議室名を取り出し、システム管理者にカレンダーの更新を依頼した。システム管理者は与えられた情報をデータベース管理者に渡して予約リストの取り出しを依頼した。データベース管理者はデータベースに問い合わせて予約リストを得て、システム管理者に渡した。システム管理者は予約リストをカレンダー管理者に渡し、カレンダーの更新を依頼した。カレンダー管理者はカレンダーの表示を更新した。
太郎君は会議室の予約状況を確認するため、会議室予約システムのページを開いた。制御パネルはデータベース管理者に会議室リストの取り出しを依頼した。データベース管理者はデータベースに問い合わせて会議室リストを得て、制御パネルに渡した。制御パネルは会議室のドロップダウンメニューを更新した。制御パネルは現在の年と月を取り出し、テキストボックスに設定した。制御パネルは現在の年と月と“全会議室”を使ってシステム管理者にカレンダーの更新を依頼した。カレンダーが現在の予約状況を表示した。(手順は予約の閲覧と同様)
システム分析では、要求分析で明確化したシステム要求を設計につなげるための橋渡しを行う。 具体的には、
を行う。
オブジェクト指向開発においては、システムに登場する「もの」がオブジェクトである。 が、どれが「もの」なのか?、を決めることはなかなか難しい。
システム中に登場する「もの」を決めるには、システム全体を見渡して完成イメージを固めつつ、これまでの経験や勘を動員して行う必要がある。 かなり経験豊富な開発者でも、最初の段階から「もの」を見分けることは難しいし、そもそも「もの」にするのかしないのか、どちらでもよい場合もあるからである。
例えば、「登録」、「編集」、「削除」、「表示」という4つのボタンを持つシステムがあったとしよう。 これら4つのボタンをそれぞれ別な「もの」として分析を進めることもできるし、4つとも同じボタンでラベルが違うだけだ、と見ることもできる。
したがって、スパイラルが進むにつれてオブジェクトが変更になることもありうる。
しかし「もの」を決める方針はある。
それは要求分析の最後に書いたシナリオである。
シナリオは、各ユースケースが実行されるときの様子を具体的に記述した文章であるから、その文章の中にシステムに登場する「もの」がかかれていることが多い。特に名詞や名詞句はその候補となる。
そこで、もう一度シナリオを名詞と名詞句に着目して見なおしてみる。
太郎君は2015年10月の会議室Cの予約状況を見るために、年に2015、月に10、会議室名に会議室Cを入力して表示ボタンを押した。表示ボタンが押されたことが制御パネルに通知された。制御パネルは自身の部品から年と月と会議室名を取り出し、システム管理者にカレンダーの更新を依頼した。システム管理者は与えられた情報をデータベース管理者に渡して予約リストの取り出しを依頼した。データベース管理者はデータベースに問い合わせて予約リストを得て、システム管理者に渡した。システム管理者は予約リストをカレンダー管理者に渡し、カレンダーの更新を依頼した。カレンダー管理者はカレンダーの表示を更新した。
太郎君は会議室の予約状況を確認するため、会議室予約システムのページを開いた。制御パネルはデータベース管理者に会議室リストの取り出しを依頼した。データベース管理者はデータベースに問い合わせて会議室リストを得て、制御パネルに渡した。制御パネルは会議室のドロップダウンメニューを更新した。制御パネルは現在の年と月を取り出し、テキストボックスに設定した。制御パネルは現在の年と月と“全会議室”を使ってシステム管理者にカレンダーの更新を依頼した。カレンダーが現在の予約状況を表示した。(手順は予約の閲覧と同様)
この中で、今回のシステムでオブジェクトとして抽出するのは以下の4つである。
このシステムにおいて、ボタンは押されたことを通知するためだけに使用されるので、わざわざオブジェクトにしなくても良いと考えられる。 また、リストはJavaScriptでは標準でサポートされるオブジェクト(ここでいう「もの」ではない)があるので、これもわざわざオブジェクト(=もの)として抽出しなくても良いと考えられる。(C++やJavaなどの一般的な言語ならオブジェクトして抽出するかもしれない)
シーケンス図とは、ユースケースごとにメッセージの流れを時間を追って記述した図である。 したがって、ユースケース記述を図で表現したものとなる。 この図を描くことにより、オブジェクト間のメッセージ関係がはっきりするので、クラス図を描く基になる。
予約の閲覧はユーザが表示ボタンを押したタイミングで開始される。 ここでは、表示ボタンが押された通知は直接的には制御パネルに伝えられ、制御パネルが必要な情報(年、月、会議室名)を取り出して、システム管理者にその情報を渡す。 それを受け取ったシステム管理者は、データベース管理者に情報を渡してデータベースから予約リストを取り出してもらう。 取り出した予約リストをカレンダー管理者に渡し、表示が更新される。
起動時の処理はページが読み込まれた時に開始される。 ここでの最初のオブジェクトは、このシステムそのものである。つまり、ページである。 ここでは各オブジェクトの生成処理が行われるが、その順序が重要である。 なぜなら、オブジェクトAがオブジェクトBを知っている必要がある場合、先にBを生成し、その後Bを与えてAを生成する方が楽だからである。 もしAとBが相互に知っていなければならないオブジェクトである場合は、Aを先に作り、Aを与えてBを作り、その後にAにBを教えてあげる必要がある。 今の場合、「予約を閲覧する」のシーケンス図から、制御パネルはシステム管理者を知っており、システム管理者はデータベース管理者とカレンダー管理者を知っている、という関係がわかり、このシーケンス図から制御パネルはデータベース管理者を知っていることがわかるので、生成順序が決定する。
上記のシーケンス図より各クラスの関係がわかるので、初期クラス図は以下のようになる。
なお、ここではオブジェクト=クラスとして扱っているが、必ずしもそうはならない場合もあるので、初期クラス図を作成する前にはクラスについての検討が必要である。
設計工程では、そのままプログラムコードのスケルトンを生成できるような詳細クラス図を作成することを目標とする。
そのため、以下の手順を行う。
クラス名や変数名は日本語でも扱えるが、カナ漢字変換が面倒なので英語化しておく方が良い。
詳細シーケンス図の作成では、クラス名を英語に変えるとともに、メソッド名と引数も決定する。また、Ajax通信を使用する部分では、非同期に呼び出しが行われるので、メッセージの戻り値として描いていた初期シーケンス図と違い、非同期メッセージ通信として描く必要がある。
データベースへの問い合わせにAjaxを使用している。 このため、この部分の処理は実際には非同期に行われるため、予約リストの獲得は戻り値ではなくメッセージ通信によって行われる。 したがって、メソッド呼び出しと戻り値の形で描いていたgetReservation()メッセージは、非同期メッセージとして書き直してある。
こちらも同様にAjaxを使用している部分については非同期メッセージに書き直した。 さらに、上記のシーケンス図とこのシーケンス図から、データベース管理者はシステム管理者と制御パネルを知っている必要があることがわかるので、その設定のためのメッセージを追加している。
以上の考察をもとにして詳細クラス図を描くと次のようになる。
このクラス図にはすべてのクラスについて、持つべきメンバ変数とメソッドが記入されている。
この設計において2つのPHPコードを使用している。
1つは予約リストを得るためのもの、もう1つは会議室リストを得るためのものである。
それぞれのPHPファイル名は、getReservationList.phpとgetRoomList.phpである。
画面の構成はHTMLとCSSにより行う。
最初に示した画面構成を実現するには、次のような構造が考えられる。
ここでは、画面全体を3つの領域に分け、上にタイトルを、2段目に制御パネルを、残りの部分が予約表示部である。
HTML5による開発を行う際は、ソースコードとして実装すべきコードの種類は次の5種類である。
以下で、それぞれについて実装していく。
phpMyAdminにアクセスし、自分のデータベースを選択したら、以下のSQLを実行し3つのテーブルを作成する。
create table users ( id int not null primary key auto_increment, name varchar(30) not null, password varchar(30) not null, level int not null default 0 )
create table rooms ( id int not null primary key auto_increment, name varchar(20) not null, capacity int not null, location varchar(100) default null, start_time time default null, end_time time default null );
create table reservations ( id int not null primary key auto_increment, user_id int not null, room_id int not null, start_dt datetime not null, end_dt datetime not null, repeat_unit int default 0, repeat_count int default 0, last_modified timestamp default CURRENT_TIMESTAMP )
作成したテーブルから必要なデータを読み出すSQL文は以下のとおり
select u.name as user_name,r.name as room_name
from
reservations v inner join users u
on v.user_id=u.id
inner join rooms r on v.room_id=r.id
where
start_dt>="2015-10-10 00:00:00" and
end_dt<"2015-10-11 00:00:00";PHPによるデータベースアクセス処理は、処理目的ごとに作られる。 ここでは2種類作成する。
なお、データベース名とパスワードは伏字にしてあるので、適切に変えること。
<?php
/* 全会議室のリストを得る
* 引数はない。
*/
try {
$q = "select name from rooms;";
$pdo = new PDO("mysql:host=mysql514.db.sakura.ne.jp; dbname=forcreate_XXXXXXXX; charset=utf8", "forcreate", "XXXXXXXX");
$s = $pdo->prepare($q);
$s->execute();
print json_encode($s->fetchAll());
} catch (PDOException $e) {
print json_encode(array("msg" => $e->getMeesage()));
}
index.html
<!DOCTYPE html>
<html>
<head>
<title>会議室予約システムSP1</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="js/libs/jquery/jqeury.js"></script>
<script type="text/javascript" src="js/my.js"></script>
<link type="text/css" rel="stylesheet" href="css/my.css">
</head>
<body>
<div id="title"><h1>会議室予約システムSP1</h1></div>
<div id="control_panel"><p>
<input id="year" type="text" size="5"/>年
<input id="month" type="text" size="3"/>月
<select id="room">
<option>全会議室</option>
<option>会議室C</option>
</select>
<input id="bt_disp" type="button" value="表示"/>
</p></div>
<div id="calendar">
<table id="calendar_table" border="1">
<tr>
<td>予約ID</td>
<td>会議室</td>
<td>開始日時</td>
<td>終了日時</td>
<td>予約者</td>
<td>予約日時</td>
</tr>
</table>
</div>
</body>
</html>
css/my.css
html,body{
margin:0px;
height:100%;
}
div#title{
position:absolute;
top:0px;
left:0px;
right:0px;
bottom:auto;
height:60px;
background-color: #ffcccc;
}
div#title h1{
margin:8px;
}
div#control_panel{
position:absolute;
top:60px;
left:0px;
right:0px;
bottom:auto;
height:50px;
background-color: #ccffcc;
}
div#control_panel p{
margin:8px;
}
div#calendar{
position:absolute;
top:110px;
left:0px;
right:0px;
bottom:0px;
background-color: #ccccff;
padding:8px;
}
table#calendar_table{
margin:0px;
width:100%;
background-color: white;
border-collapse: collapse;
}
// システム管理者
var SystemManager=function(cam,dbm){
this.cam=cam;
this.dbm=dbm;
this.updateCalendar=function(y,m,d){};
this.reservationListGot=function(rl){};
};
// データベース管理者
var DBManager=function(){
this.cp=null;
this.sym=null;
this.getReservationList=function(y,m,r){};
this.reservationListGot=function(rl){};
this.setSymCp=function(sym,cp){};
this.getRoomList=function(){};
this.roomtListGot=function(rl){};
this.handleError=function(err){};
};
// カレンダー管理者
var CalendarManager=function(){
this.updateList=function(rl){};
};
// 制御パネル
var ControlPanel=function(sym,dbm){
this.sym=sym;
this.dbm=dbm;
this.dispButtonClicked=function(ev){};
this.roomListGot=function(rl){};
};