はじめに
地域の工具貸出窓口では、登録メンバーに実物の工具を貸し出します。同じ名前のドリルが 2 台あっても、それぞれ別の貸出に属します。整備中の工具は貸出不可のままにし、貸出中の工具は記録されている借用者だけが返却できるようにする必要があります。
これらのルールを Java オブジェクトに組み込み、用意された小さなコマンドループを使います。この最初のラボでは変更をメモリ内に保持し、次のラボで保存を学びます。Java のクラス、メソッド、private フィールド、リスト、基本的な例外を理解していることが前提です。読解と練習の時間は、作成者による見積もりで 45 分です。
工具の識別を保ったまま 1 件貸し出す
このステップでは、正常な貸出を実行し、用意されたオブジェクトを読んでから、2 回目の貸出から保護する小さなルールを追加します。
Terminal タブを開きます。cd はプロジェクトフォルダーを選択します。このラボの以後のシェルコマンドはすべてそこから実行します。入力を間違えてまだ実行していない場合は、Ctrl+U で行を消して入力し直します。「file not found」と表示された場合は、環境をリセットせず、この cd をもう一度実行してください。
cd /home/labex/project/tool-library
用意されたアプリケーションは、すでにコンパイルできます。javac は Java コンパイラ、-encoding UTF-8 はソースのエンコーディングの指定、-d out はコンパイル済みクラスを out に置く指定、*.java はこのフォルダーの Java ソースファイルの選択です。
javac -encoding UTF-8 -d out *.java
コンパイルが成功すると何も表示されません。編集後に起動するときは、毎回先にコンパイルを成功させてください。そうしないと Java が古いクラスを実行する可能性があります。java はプログラムを起動し、-Dfile.encoding=UTF-8 はテキストのエンコーディング、-cp out はコンパイル済みクラスを指定します。Desk はエントリークラスです。desk.tsv が記録を提供し、--memory はこのラボでの保存を無効にします。
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv --memory
現在はシェルではなく、貸出窓口プログラムの中にいます。次の行を 1 行ずつ入力し、その都度 Enter を押してください。
borrow A11 M01
list
quit
Accepted: A11 BORROWED M01 が表示されるはずです。A12 は別の Drill で、AVAILABLE のままです。名前は重複しても、ID が実物の工具を識別します。quit でシェルに戻ります。この正常な貸出は修復前でも動きますが、コードはまだ再貸出から保護していません。この実行は --memory を使ったため、元のファイルは変わらず、次の実行は初期状態から始まります。
ファイルを編集せず表示する cat で、用意されたソースを読みます。
cat Tool.java
工具には安定した id、表示用の name、state、borrower、note があります。enum は名前の付いた固定の値の集合を持つ型です。AVAILABLE は誰も工具を持っていないこと、BORROWED はメンバーが持っていること、MAINTENANCE は貸出を禁止することを表します。ハイフン - は借用者がいないことを表します。private フィールドはメソッドを通してのみ変更できます。borrow を読んでください。現在は古い状態を確認せず、BORROWED と新しいメンバーを代入しています。2 回目の貸出が何を上書きするか予想しましょう。コンストラクタ、ゲッター、列挙型はすでに完成しているので、そのまま残します。
コマンドの枠組みも用意されています。
cat Desk.java
found.borrow(c[2]) と、その周囲の try/catch を探します。ループはメンバー ID の存在を確認しますが、Tool は自身の状態を保護しなければなりません。IllegalArgumentException は Rejected: ... となり、ループは開いたままです。状態の確認は、どちらの代入よりも前に行う必要があります。変更後に例外を送出すると、拒否された貸出が一部だけ適用された状態になってしまいます。
編集前に、ソースだけのバックアップを作ります。mkdir -p は必要に応じてフォルダーを作り、cp はファイルをコピーします。これは自分で作るバックアップであり、完成済みの解答ではありません。貸出窓口やメンバーのデータはコピーもリセットもしないでください。
mkdir -p work
cp Tool.java work/Tool-before-borrow.java.txt
ターミナルのテキストエディター nano で、既存のファイルを開きます。
nano Tool.java
nano のショートカット欄の ^ は Ctrl を意味します。Ctrl を押しながらもう一方のキーを押し、キャレット自体は入力しません。Ctrl+W を押し、state = State.BORROWED; と入力して Enter を押すと、borrow 内の代入が見つかります。Ctrl+A でその行の先頭に移動します。矢印キーでカーソルを動かし、Backspace で文字を修正できます。既存の 2 つの代入とクラスの残りを保ったまま、代入の上に次のガードを挿入します。
if (state != State.AVAILABLE) {
throw new IllegalArgumentException("Tool is not available");
}
!= は「等しくない」を意味します。この条件は既存の貸出と整備中の両方を拒否し、識別情報、名前、メモは変更しません。クラス全体を置き換えず、既存のメソッドにこの分岐を書き加えてください。波括弧とセミコロンを確認します。
Ctrl+O で保存し、Enter で既存のファイル名を確定してから、Ctrl+X で終了します。未保存の間違いを破棄する場合は、Ctrl+X を押し、保存の確認に N と答えます。ファイルを開き直してやり直してください。シェルで nano Tool.java を実行すると再び開け、Ctrl+W で同じ目印を探せます。コンパイルエラーが出たら、ファイル名と行を読み、nano でその箇所を修正して保存し、再コンパイルします。コンパイル失敗後に起動しないでください。
このステップでのソース編集をすべて取り消したい場合、次の復旧コマンドで先ほどの Tool.java のバックアップだけを戻せます。編集内容を失うため、このソース編集を最初からやり直す場合だけ使ってください。データは決してリセットされません。これは復旧の選択肢であり、通常の次の操作ではありません。
cp work/Tool-before-borrow.java.txt Tool.java
ガードを保存したら(または復旧後に書き直したら)、コンパイルして起動します。
javac -encoding UTF-8 -d out *.java
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv --memory
貸出窓口内で、次のコマンドを順番に入力します。
borrow A11 M01
list
borrow A11 M02
borrow A13 M01
borrow TYPO M01
borrow A12 M02
list
quit
最初の貸出は受理されます。重複した貸出と整備中の工具の貸出は、意図どおり Rejected: Tool is not available で拒否されます。TYPO は不明な ID として拒否されるので、リセットせず次の行で ID を修正します。A12 は引き続き M02 が独立して借りられます。最後の一覧は A11 → M01 と A13 の整備状態・メモを保持していなければなりません。quit でシェルに戻り、desk.tsv は変わりません。返却メソッドはまだ未完成で、次のステップで取り組みます。
記録された借用者だけが返却できるようにする
このステップでは、ほかの貸出を保持しながら、逆方向の状態遷移を完成させます。
同じ Terminal タブのシェルプロンプトから始めます。返却が有効なのは、状態が BORROWED で、指定されたメンバーが borrower と一致する場合だけです。Java の文字列では .equals() が内容を比較し、! がその比較結果を反転します。両方の確認が通ったら、状態を AVAILABLE にし、借用者を - にクリアします。先に例外を送出することで、失敗した返却がオブジェクトの一部だけを変更することを防げます。
貸出のガードは残します。次の編集前に、ソースだけの新しいバックアップを作り、復旧時も完成した貸出メソッドを保持できるようにします。
cp Tool.java work/Tool-before-return.java.txt
nano Tool.java
Ctrl+W を押して state = State.AVAILABLE; を検索し、Enter を押します。これは giveBack 内の最初の代入です。Ctrl+A でその行の先頭に移動します。その前に次の 2 つのチェックを挿入し、既存の状態の代入と borrower = "-"; は両方のチェックの後に残します。クラス全体を置き換えたり、貸出のガードを削除したりしないでください。
if (state != State.BORROWED) {
throw new IllegalArgumentException("Tool is not borrowed");
}
if (!borrower.equals(member)) {
throw new IllegalArgumentException("Borrower does not match");
}
最初の分岐は、貸出可能な工具と整備中の工具を拒否します。貸出中の工具だけが借用者の確認に進みます。表示名ではなく、記録された借用者とメンバーを比較してください。波括弧を確認し、Ctrl+O、Enter、Ctrl+X で保存して終了します。未保存の間違いは Ctrl+X、N で破棄できます。コンパイルエラーはファイルを開き直して該当メソッドを検索し、修正してください。起動前にコンパイルを成功させます。貸出のガードを保ったまま、このステップの編集を意図的に破棄するには、cp work/Tool-before-return.java.txt Tool.java を実行してから、返却の編集だけをやり直します。古い before-borrow バックアップを戻してはいけません。
現在のソースをプログラムで使うために再コンパイルし、新しいメモリ内の貸出窓口を開きます。このラボでは、毎回の実行が元のファイルから始まります。
javac -encoding UTF-8 -d out *.java
java -Dfile.encoding=UTF-8 -cp out Desk desk.tsv --memory
貸出窓口内で、次のコマンドを順番に入力します。
list
return A14 M01
return A13 M03
return A14 M03
borrow A14 M02
list
quit
最初の一覧には、既存の貸出 A14 → M03 が表示されます。間違った借用者には Rejected: Borrower does not match、整備中の工具の返却には Rejected: Tool is not borrowed が返ります。どちらの拒否でもオブジェクトは変わりません。正しい返却では Accepted: A14 AVAILABLE - が表示され、その後 M02 が借りられます。A13 は整備中のままで、A11 と A12 は別々の ID とメモを保持します。
許可された 2 つの矢印 AVAILABLE → BORROWED と BORROWED → AVAILABLE を表現できました。MAINTENANCE から返却する矢印はなく、このアプリケーションには整備管理コマンドもありません。拒否は通常の業務結果であり、コマンドループを終了させません。再実行して練習できます。このラボでは quit 後に変更が意図的に消えます。ソースを保持し、独立した永続化ラボに進んで、その理由を学びましょう。
まとめ
列挙型とオブジェクトの private な状態を使い、工具の貸出を保護しました。同名の工具でも別々の ID を保持し、無効な操作ではオブジェクトを変えず、有効な返却後は別のメンバーが借りられるようになりました。次の学習目標は、実行をまたいで結果を保存することです。



