本系列第四篇將介紹書店管理系統的重要升級:引入 UUID 自動生成機制,並完整實現圖書管理模塊。相比前幾期,本次更新不僅優化了主界面導航方式,更通過設計模式的應用,展示了如何構建清晰分層、易於擴展的桌面應用架構。
主界面現通過頂部菜單「模塊」進入各功能區:
用戶模塊主界面:
圖書模塊主界面:
添加用戶界面(無需輸入 UUID):
添加圖書界面:
清晰分層的包結構是維護性的關鍵:
public class BookStore extends javax.swing.JFrame {
public BookStore() {
super("書店管理系統---CHX---聯系QQ——619699629");
initComponents();
this.setResizable(false);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// 居中顯示邏輯
Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(scr.width / 5, scr.height / 5);
}
// 菜單項事件:切換到圖書模塊
private void jMenuItemBookActionPerformed(ActionEvent evt) {
this.setContentPane(new BookListPanel(this));
this.validate();
}
// 菜單項事件:切換到用戶模塊
private void jMenuItemUserActionPerformed(ActionEvent evt) {
this.setContentPane(new ListPanel(this));
this.validate();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(() -> new BookStore().setVisible(true));
}
}
設計亮點:通過 setContentPane() 動態替換面板實現模塊切換,避免多窗口管理複雜性;菜單項綁定快捷鍵(Ctrl+U/B)提升操作效率。
public class BookModel implements Serializable {
private String uuid; // 主鍵,自動生成
private String name; // 書名
private double inPrice; // 進價
private double salePrice; // 售價
// 重寫 equals/hashCode 基於 uuid,保證集合操作正確性
@Override public boolean equals(Object obj) { ... }
@Override public int hashCode() { ... }
}
public class BookQueryModel extends BookModel {
private double inPrice2; // 進價上限
private double salePrice2; // 售價上限
// 支持區間查詢:inPrice ~ inPrice2, salePrice ~ salePrice2
}
public interface BookDao {
boolean create(BookModel book);
boolean delete(String uuid);
boolean update(BookModel book);
List getAll();
List getByCondition(BookQueryModel bqm);
BookModel getSingle(String uuid);
}
public class BookDaoSerImpl implements BookDao {
private final String FILE_NAME = "Book.txt";
@Override
public boolean create(BookModel book) {
List books = FileIoUtil.readFormFile(FILE_NAME);
// 業務級唯一性校驗
if (books.stream().anyMatch(m -> m.getUuid().equals(book.getUuid())))
return false;
books.add(book);
FileIoUtil.write2file(books, FILE_NAME);
return true;
}
@Override
public List getByCondition(BookQueryModel bqm) {
return getAll().stream().filter(book -> {
if (bqm.getUuid() != null && !book.getUuid().equals(bqm.getUuid())) return false;
if (bqm.getName() != null && !book.getName().contains(bqm.getName())) return false;
if (bqm.getInPrice() > 0 && book.getInPrice() < bqm.getInPrice()) return false;
if (bqm.getInPrice2() > 0 && book.getInPrice() > bqm.getInPrice2()) return false;
if (bqm.getSalePrice() > 0 && book.getSalePrice() < bqm.getSalePrice()) return false;
if (bqm.getSalePrice2() > 0 && book.getSalePrice() > bqm.getSalePrice2()) return false;
return true;
}).collect(Collectors.toList());
}
}
注意:原代碼中 getByCondition 邏輯有殘缺,此處補全為流式寫法更清晰。實際項目中建議遷移至 SQLite/嵌入式數據庫。
public interface BookEbi {
boolean create(BookModel book);
boolean delete(String uuid);
boolean update(BookModel book);
List getAll();
List getByCondition(BookQueryModel bqm);
BookModel getSingle(String uuid);
}
public class BookEbo implements BookEbi {
private BookDao dao = BookDaoFactory.getBookDao();
@Override
public boolean create(BookModel book) {
// 核心改動:調用 UUID 模塊自動生成主鍵
String uuid = UuidDaoFactory.getUuidDao().getNextUuid(UuidModelConstance.BOOK);
book.setUuid(uuid);
return dao.create(book);
}
// 其他方法直接委託 DAO...
}
這裡體現了「依賴倒置」:業務層不直接 new 實現類,而是通過工廠獲取接口,同時演示了跨模塊調用 UUID 服務。
public class BookListPanel extends JPanel {
private JFrame mainFrame;
private JList bookLists;
public BookListPanel(JFrame mainFrame) {
this.mainFrame = mainFrame;
initComponents();
loadData();
}
private void loadData() {
List list = BookEbiFactory.getBookEbi().getAll();
bookLists.setListData(list.toArray(new BookModel[0]));
}
// 刪除示例:獲取選中項 -> 確認 -> 調用業務層 -> 刷新
private void btnToDeleteActionPerformed(ActionEvent evt) {
BookModel book = bookLists.getSelectedValue();
if (book == null) { showMsg("請選擇要刪除的圖書!"); return; }
mainFrame.setContentPane(new BookDeletePanel(mainFrame, book.getUuid()));
mainFrame.validate();
}
}
private void btnAddActionPerformed(ActionEvent evt) {
String name = tfdName.getText().trim();
double inPrice, salePrice;
try {
inPrice = Double.parseDouble(tfdInPrice.getText());
salePrice = Double.parseDouble(tfdSalePrice.getText());
} catch (NumberFormatException e) { showMsg("價格格式錯誤"); return; }
if (name.isEmpty()) { showMsg("書名不能為空"); return; }
BookModel book = new BookModel();
book.setName(name);
book.setInPrice(inPrice);
book.setSalePrice(salePrice);
// UUID 由業務層自動注入
if (BookEbiFactory.getBookEbi().create(book)) back();
else showMsg("該圖書已存在!");
}
public class FileIoUtil {
@SuppressWarnings("unchecked")
public static List readFormFile(String fileName) {
List list = new ArrayList<>();
File file = new File(fileName);
if (!file.exists()) return list;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
list = (List) in.readObject();
} catch (Exception e) { e.printStackTrace(); }
return list;
}
public static void write2file(List list, String fileName) {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) {
out.writeObject(list);
} catch (Exception e) { throw new RuntimeException("寫入失敗", e); }
}
}
泛型方法實現了對任意實體列表的讀寫,配合 Serializable 接口實現零配置持久化。
下一期(系列四·2)將發布 UUID 模塊完整代碼、用戶模塊細節及公共基礎類,敬請期待。