diff --git a/courseA/IM_DTRAM.html b/courseA/IM_DTRAM.html
index 733715e..9d7c790 100644
--- a/courseA/IM_DTRAM.html
+++ b/courseA/IM_DTRAM.html
@@ -36,7 +36,7 @@
DTRAMでは,InventoryManagementのモデルを以下のようなチャンネル単位で記述していきます.
ここでは,商品登録用のイベントチャンネルを ItemRegistration ,itemId で登録されている商品に対して入荷か出荷を行うためのイベントチャンネルを ReceivingOrShipping(itemId: として宣言しています.
ItemRegistration チャンネルでは inventory リソースが,メッセージ registerItem(itemId, itemName, quantity) を受け取ると, リソースの状態 itemDB が insert(itemDB, itemId, {"count": quantity, "name": itemName}) に変わることを示しています.
- ここで,insert() 関数は,第1引数に渡された写像に対し,第2引数と第3引数の対応を追加した結果得られる写像を返す関数です.
+ ここで,insert() 関数は,第1引数に渡された写像に対し,第2引数と第3引数の対応を追加した結果得られる写像を返す関数です.
ReceivingOrShipping(itemId: チャンネルでは inventory.{itemId}.count リソースがメッセージ receiveOrShip(quantity) を受け取ると,
遷移前の状態 prev_quantity が prev_quantity + quantity に変わることを示しています.
- 本課題では,SimpleTwitterプログラムにアカウント名を変更可能にする機能追加を行っていただきます.
+ ここでは,SimpleTwitterのDTRAMによる仕様記述に関する課題に取り組んでいただきます.
+ 以下にDTRAMで記述されたSimpleTwitterのモデルを示します.こちらを適宜参照しながらモデルの理解を行ってください.
- このSimpleTwitterプログラムでは,GPSの位置情報が更新されるたびに,それに合わせて地図の表示位置も自動で更新されます.しかしながら,地図アプリのユーザはいつでも自分の周辺の地図を閲覧したいわけではありません.
- 例えば,電車に乗りながら目的地周辺の地図情報を確認したい場合,電車の動きに合わせて地図の表示位置が勝手に動いてしまうと困ります.
+ DTRAMでは,SimpleTwitter の仕様を以下のような階層化されたリソースで表します.
+
accounts: アカウント全体を管理accounts.{accountId}: accountId で登録されているアカウントaccounts.{accountId}.name: accountId で示されたアカウント名accounts.{accountId}.tweets: accountId で示されたアカウントのツイートリストaccounts.{accountId}.tweets.{tid}: accountId で示されたアカウントが持つツイートリストに tid で登録されているツイートSignup ,accountId で登録されているアカウントに対してツイートを行うためのイベントチャンネルを Tweet(accountId:Str ) として宣言しています.Signup チャンネルでは accounts リソースが,メッセージ signup(accountId, name) を受け取ると, リソースの状態 accountDB が insert(accountDB, accountId, {"name": itemName, "tweets": nil}) に変わることを示しています.nil は空のマップやリストを表しています.insert() 関数は,第1引数に渡された写像に対し,第2引数と第3引数の対応を追加した結果得られる写像を返す関数です.Tweet(accountId:Str ) チャンネルでは accounts.{accountId}.tweets リソースがメッセージ tweet(contents) を受け取ると,
+遷移前の状態 tweetList が append(tweetList, contents) に変わることを示しています.append() 関数は,第1引数に渡された写像に対し,第2引数の対応を追加した結果得られる写像を返す関数です.++channel Signup { +out accounts(accountDB:Map , signUp(accountId:Str , name:Str )) = insert(accountDB, accountId, {"name": name, "tweets": nil}) +} + +channel Tweet(accountId:Str ) { +out accounts.{accountId}.tweets(tweetList:List , tweet(contents:Str )) = append(tweetList, contents) +} +
- 現在の設計では,以下のクラス図のようにGPSの緯度情報を保持している Latitude オブジェクトが地図の中心位置の緯度成分を表す MapLatitude オブジェクトを,
- GPSの経度情報を保持している Longitude オブジェクトが地図の中心位置の経度成分を表す MapLongitude オブジェクトを,それぞれPUSH型のデータ転送で更新するようになっています.
+ 先ほどのSimpleTwitterのモデルのリソースとチャンネルを,DTRAMのモデリングツールを使用して可視化したものを以下の図に示します.


- 具体的には以下のコードのように,GPSの位置情報の変化をセンサーが検知するたびに Map クラスの updateGPS() メソッドが呼び出され,
- その中から Longitude クラスの updateGPS() メソッドと Latitude クラスの updateGPS() メソッドが呼ばれて,まずGPSの緯度と経度の情報が更新されます.
+ この図がモデルの実行に依らない,リソースを表していることに注意して下さい.
+ 図の中央にある accounts リソースは Signup チャンネルからアカウントを登録するメッセージを受け取ります.
+ accounts リソースの子リソースである,accounts.{accountId} リソースはアカウントを表しています.
+ リソース名に含まれる accountId はアカウントIDを表すパスパラメータで,一意なアカウントの特定を可能にしています.
+ accounts.{accountId} リソースの子リソースである accounts.{accountId}.tweets リソースはaccounts.{accountId} のツイートリストを表しており,
+ Tweet チャンネルからツイートを行うメッセージを受け取ります.
-public class Map {
- :
- private Longitude longitude;
- private Latitude latitude;
- :
- public void updateGPS(double cur_lat, double cur_long) {
- this.longitude.updateGPS(cur_lat, cur_long);
- this.latitude.updateGPS(cur_lat, cur_long);
- }
- :
-}
-
-
- そして,それらのメソッドの中からさらに,MapLongitude クラスの updateLongitude() メソッドと
- MapLatitude クラスの updateLatitude() メソッドが以下のコードのようにそれぞれ呼ばれて,引き続き地図の中心位置の情報も更新されます.
- これらの更新は常に連続して行われるため,このままの設計では,GPSの位置情報だけを更新して地図の中心位置は更新しないという,自動更新がOFFの状態の振る舞いを上手く実装することができません.
-
-public class Longitude {
- private MapLongitude mapLongitude;
- private double value;
- :
- public void updateGPS(double cur_lat, double cur_long) {
- this.value = cur_long;
- this.mapLongitude.updateLongitude(this.value);
- }
-}
-
-public class Latitude {
- private MapLatitude mapLatitude;
- private double value;
- :
- public void updateGPS(double cur_lat, double cur_long) {
- this.value = cur_lat;
- this.mapLatitude.updateLatitude(this.value);
- }
-}
-
-
- そこで,以下のクラス図のように新たに Presenter オブジェクトを導入し,Longitude オブジェクトも Latitude オブジェクトも,
- 必ずこのオブジェクトを経由して MapLatitude オブジェクトや MapLongitude オブジェクトを更新するように設計変更を行います.
- そうすることによって,Longitude オブジェクトや Latitude オブジェクトの状態の更新後に
- MapLatitude オブジェクトや MapLongitude オブジェクトの状態の更新をするか否かを,Presenter オブジェクト上で制御することができるようになります.
+ ここでは,SimpleTwitterモデルの仮想実行について説明します.この仮想実行で用いるテストケースは次の通りです.
+
+ シミュレーションツールを使用すると与えられたモデルに対して,任意の仮想実行を行うことができます.
+ ここでは上で示したテストケースに従って行った仮想実行を可視化したものを示していきます.まず,システムの初期状態を可視化したものが以下の図です.
+


- 具体的に変更後の設計の中では,以下のように Map クラスの updateGPS() メソッドは,
- Longitude クラスと Latitude クラスの updateGPS() メソッドの代わりに Presenter クラスの updateGPS() メソッドを呼び出すようにします.
+ 初期状態では,accounts リソースには何のアカウントも登録されていない状態になっています.
+ 次に,accounts にアカウント名が Satou のアカウントを登録するため,accounts リソースをダブルクリックします.
+

+ signUp というアカウントを登録するためのメッセージを選択します.
+

+ signUp メッセージのシグニチャが表示されます.
+ accountId はアカウントID,name はアカウント名を表します.
+

+ ここでは,signUp("@123", "Satou") を入力します.
+

+ 次の状態に遷移し,遷移後の状態が表示されます.@123というidを持つアカウント accounts.@123 が accounts の子リソースとして生成されていることがわかります.
+ accounts.@123 のツイートリスト accounts.@123.tweets が 空のリスト(nill),アカウント名 accounts.@123.name が Satouとなっていることを確認できます.
+ 次に,登録したアカウント accounts.@123 でツイートを行いたいので,accounts.@123.tweets リソースをダブルクリックします.
+

+ メッセージを選択する画面が同様に表示されますので,ツイートを行う tweet というメッセージを選択します.
+

+ ツイート内容の入力が可能な画面が表示されますので,ここでは tweet("Hello") と入力して実行します.
+

+ 次の状態に遷移したため,Hello というツイート内容を持つツイート accounts.@123.tweets.0 が accounts.@123.tweets の子リソースとしてツイートリストの0番目に生成されていることが確認できました.
+
+ 本課題ではまず,上記にて説明を行った SimpleTwitter の仕様に,ある機能を追加した仕様を考えます.
+ その仕様のモデルと,仮想実行の流れを可視化した以下の図を見ていただいた上で,2つの設問にお答えいただきます.
+ 設問にお答えいただく際は,必ず設問1,設問2の順番でご解答下さい.以下の図と同じものが設問のページにも記載されているのでこのまま設問のページに飛んでいただいてもかまいません.
-public class Map {
- :
- private Presenter presenter;
- :
- public void updateGPS(double cur_lat, double cur_long) {
- this.presenter.updateGPS(cur_lat, cur_long);
- }
+
+channel Signup {
+ out accounts(accountDB:Map , signUp(accountId:Str , name:Str )) = insert(accountDB, accountId, {"name": name, "tweets": nil})
}
+
+channel Tweet(accountId:Str ) {
+ out accounts.{accountId}.tweets(tweetList:List , tweet(contents:Str )) = append(tweetList, contents)
+}
+
+channel ChangeName(accountId:Str ) {
+ out accounts.{accountId}.name(prevNamr:Str , changeName(name:Str )) = name
+}
+
+
- そして,Presenter クラスの updateGPS() メソッドは,
- Longitude オブジェクトや Latitude オブジェクトの状態の更新と,
- MapLatitude オブジェクトや MapLongitude オブジェクトの状態の更新を,以下のように切り離して実行するようにします.
- そのため,この updateGPS() メソッドを書き換えることによって,自動更新のON/OFFを制御することができるようになります.
-
-public class Presenter {
- private Longitude longitude;
- private Latitude latitude;
- private MapLongitude mapLongitude;
- private MapLatitude mapLatitude;
- :
- public void updateGPS(double cur_lat, double cur_long) {
- double longitude = this.longitude.updateGPS(cur_lat, cur_long);
- double latitude = this.latitude.updateGPS(cur_lat, cur_long);
- this.mapLongitude.updateLongitude(longitude);
- this.mapLatitude.updateLatitude(latitude);
- }
-}
+
+

- 本課題では,リファクタリングに要した時間を測っていただきますので,お手元に時計をご用意ください.
- 時間計測にあたって,急いで作業していただく必要はまったくありません.
- 最初の課題から最後の課題まで一定のペースを保てるよう, 正しくリファクタリングを行うことを意識してください.
- 課題に着手する前に開発環境を立ち上げて現状のソースコードの確認をしていただいて構いませんが,実際のソースコードの変更は時間計測の準備ができるまで行わないで下さい.
-
- 作業に着手する前に,main ブランチから,メッセージでお伝えした自分専用のブランチ(user??Refactor)を新規作成して,一度 push を行ってください.
- push を行った後,開発環境の準備ができれば,時間計測を開始してください. 時間計測はできる限り,30秒以内の単位での計測をお願いします.
- ソースコードの変更作業は上記「Map のリファクタリングの概要」にしたがって進めてください.
- テストプログラムTestGPSUpdate.javaが正しく動作するまでは作業完了とは見なされないので注意してください.
-
- リファクタリングの作業が完了したら作業時間を記録し,その後,作業結果を自分専用のブランチに commit & push してください.
- ただし,main ブランチには決して merge をしないように,また自分専用のブランチを決して削除しないように注意してください.
- push 後に,以下のアンケートにお答えください.
-
- アンケートフォーム (別タブが開きます)
+
+ 課題アンケート (別タブが開きます)
+
+
+
+
+
+ 課題終了後の評価アンケート (別タブが開きます)