将借用规则封装在对象中

JavaBeginner
立即练习

介绍

社区工具借用台将实体工具借给登记成员。两把电钻可以同名,却属于不同的借用关系。维修中的工具必须保持不可借,已借工具只能由记录中的借用人归还。

你将把这些规则写入 Java 对象,再使用准备好的小型命令循环。第一个实验只在内存中保留变更;下一个实验教授保存。你应已了解 Java 类、方法、私有字段、列表和基本异常。阅读与练习时间为作者估计的 45 分钟。

借出一件工具并保留其身份

在本步骤中,你将运行一次正常借用,阅读准备好的对象代码,再添加一条小规则,防止它被再次借出。

打开 Terminal 标签页。cd 选择项目目录;本实验后续所有 shell 命令均从该目录开始。若输入有误但尚未执行,按 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

现在你位于借用台程序内,而不是 shell。逐行输入以下内容,每行后按 Enter:

borrow A11 M01
list
quit

预期看到 Accepted: A11 BORROWED M01。A12 是另一把 Drill,仍为 AVAILABLE:ID 标识实体工具,而名称可以重复。quit 返回 shell。这次正常借用在修复前就能成功,但代码尚未防止同一工具再次被借出。由于本次运行使用了 --memory,原文件没有改变,下次运行会重新开始。

用 cat 阅读准备好的源代码;它显示文件而不编辑文件:

cat Tool.java

工具保存稳定的 id、显示用的 name、state、borrower 和 note。enum 具有一组固定的命名值:AVAILABLE 表示无人持有工具,BORROWED 表示一位成员持有工具,MAINTENANCE 表示禁止借用。短横线 - 表示没有借用人。私有字段只能通过方法改变。阅读 borrow:它目前不检查原状态,就赋予 BORROWED 状态并指定新成员。预测第二次借用会覆盖什么。构造方法、getter 方法和枚举已经完整,请保留它们。

命令框架也已准备好:

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 修正字符。在赋值上方插入以下守卫,保留原有的两次赋值和类的其余部分:

        if (state != State.AVAILABLE) {
            throw new IllegalArgumentException("Tool is not available");
        }

这里,!= 表示“不等于”。这个条件同时拒绝已有借用和维修状态,不改变身份、名称或备注。把分支写入现有方法,不要替换整个类。检查花括号和分号。

按 Ctrl+O 保存,按 Enter 确认现有文件名,再按 Ctrl+X 退出。如果想放弃尚未保存的错误修改,按 Ctrl+X,在保存提示处回答 N。重新打开文件即可再试。在 shell 中,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 返回 shell;desk.tsv 保持不变。归还方法仍不完整,将在下一步完成。

只允许记录中的借用人归还

在本步骤中,你将完成反向状态变化,同时保留其他借用。

从同一个 Terminal 标签页的 shell 提示符开始。只有当前状态为 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 移到该行开头。在它前面插入以下两项检查;原有的状态赋值及 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 和备注。

你已经表达了两条允许的状态变化:AVAILABLE → BORROWED 和 BORROWED → AVAILABLE。MAINTENANCE 没有归还箭头,这个应用也不提供维修管理命令。拒绝属于正常业务结果,不会结束命令循环。你可以再次运行程序练习。本实验中,变更在 quit 后会按设计消失;保留源代码,继续独立的持久化实验,学习其原因。

总结

你使用枚举和私有对象状态保护工具借用。同名工具保留独立 ID,非法操作保留对象原状,合法归还让其他成员能够再次借用。下一项学习目标是跨运行保存结果。