本系列記錄一個基於 Java Swing 的桌面版書店管理系統畢業設計,採用**分層架構**、**檔案儲存** (XML/序列化) 而非資料庫,適合教學示範 MVC 分層、DAO 模式、工廠模式等核心設計模式應用。
| 模組 | 核心功能 | 業務規則 |
|---|---|---|
| 用戶管理 | 註冊、修改、刪除、查詢 | 權限分級:Admin(1)、圖書(2)、進貨(3)、銷售(4)、庫存(5) |
| 圖書管理 | 增刪改查圖書基本資料 | ISBN 唯一、庫存同步 |
| 進貨管理 | 進貨單/明細、庫存增加 | 只能進既有圖書、自動加庫存 |
| 銷售管理 | 銷售單/明細、庫存扣減 | 庫存不足不可銷售、自動減庫存 |
| 庫存查詢 | 條件查詢庫存明細 | 即時反映進銷變動 |
┌─────────────────────────────────────┐ │ Presentation Layer (UI) │ Swing 視圖: JFrame, JPanel, JTable │ listPanel, AddPanel, QueryPanel... │ ├─────────────────────────────────────┤ │ Business Logic Layer (EBI/EBO) │ 介面/實作: UserEbi, UserEbo │ 事務控制、權限檢查、業務規則 │ ├─────────────────────────────────────┤ │ Data Access Layer (DAO) │ 介面/實作: UserDAO, UserDAOFileImpl │ CRUD、檔案 IO、序列化/反序列化 │ ├─────────────────────────────────────┤ │ Common / Utility Layer │ VO, Enum, UUID生成, 檔案工具, 日期工具 │ 跨層共用類別 │ └─────────────────────────────────────┘
cn.hncu.bookStore ├── bookStoreApp.java // 主視窗啟動類 ├── common/ // 共用常數、工具 │ ├── uuidModelConstance.java // 實體類型枚舉 │ ├── uuid/ // UUID 生成機制 (DAO+EBO+Factory) │ └── utils/ // DateUtils, FileIOUtil (序列化工具) ├── user/ // 用戶模組 (完整 MVC) │ ├── vo/ // UserModel, UserQueryModel │ ├── common/ // UserTypeEnum (權限枚舉) │ ├── dao/ // UserDAO, UserDAOFileImpl, Factory │ ├── business/ // UserEbi, UserEbo, Factory │ └── ui/ // listPanel, AddPanel, UpdatePanel... ├── book/ // 圖書模組 (結構同 user) ├── in/ // 進貨模組 ├── out/ // 銷售模組 └── store/ // 庫存模組
public class bookStoreApp extends JFrame {
private JMenuItem menuItemUser, menuItemBook, menuItemInMain,
menuItemOutMain, menuItemStock, menuItemLogin;
public bookStoreApp() {
initComponents(); // 初始化選單欄 (NetBeans GUI Builder 產生)
// 預設顯示登入面板
this.setContentPane(new Login(this));
this.validate();
}
// 選單項事件:切換 ContentPane 實現面板切換
void menuItemUserActionPerformed(ActionEvent evt) {
this.setContentPane(new cn.hncu.bookStore.user.ui.listPanel(this));
this.validate();
enableMenus(true); // 登入後啟用功能選單
}
// 登出/切回登入面板
private void menuItemLoginActionPerformed(ActionEvent evt) {
disableAllMenus();
this.setContentPane(new Login(this));
this.validate();
}
// ... 其他選單事件類似
}
public class FileIOUtil {
private FileIOUtil() {} // 工具類私有構造
/** 從檔案讀取物件列表 (泛型) */
public static List readFromFile(String fileName) {
File file = new File(fileName);
if (!file.exists()) return new ArrayList();
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
@SuppressWarnings("unchecked")
List list = (List) in.readObject();
return list;
} catch (Exception e) {
throw new RuntimeException("讀取檔案失敗: " + fileName, e);
}
}
/** 寫入物件列表到檔案 */
public static boolean writeToFile(List list, String fileName) {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) {
out.writeObject(list);
return true;
} catch (Exception e) {
throw new RuntimeException("寫入檔案失敗: " + fileName, e);
}
}
}
// 介面
public interface UUIDDAO {
String getNextNum(UUIDModelConstance umc);
}
// 實作: 以檔案儲存各類型計數器
public class UUIDEbo implements UUIDDAO {
private static final String FILE_NAME = "uuid.txt";
@Override
public String getNextNum(UUIDModelConstance umc) {
String modelName = umc.getName(); // 如 "UserModel"
List list = FileIOUtil.readFromFile(FILE_NAME);
for (UUIDModel um : list) {
if (um.getModelName().equals(modelName)) {
int next = um.getCurrentNum();
um.setCurrentNum(next + 1);
FileIOUtil.writeToFile(list, FILE_NAME);
return String.valueOf(next);
}
}
// 首次使用,建立計數器從 1 開始
UUIDModel um = new UUIDModel();
um.setModelName(modelName);
um.setCurrentNum(2);
list.add(um);
FileIOUtil.writeToFile(list, FILE_NAME);
return "1";
}
}
public class DateUtils {
private static final SimpleDateFormat FMT = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
public static String longToString(long time) {
return FMT.format(new Date(time));
}
public static long stringToLong(String str) throws ParseException {
return FMT.parse(str).getTime();
}
}
public enum UserTypeEnum {
ADMIN(1, "超級管理員"),
BOOK(2, "圖書管理員"),
IN(3, "進貨管理員"),
OUT(4, "銷售管理員"),
STOCK(5, "庫存管理員");
private final int type;
private final String name;
UserTypeEnum(int type, String name) { this.type = type; this.name = name; }
public static int getTypeByName(String name) { ... }
public static String getNameByType(int type) { ... }
}
| 模式 | 應用位置 | 效益 |
|---|---|---|
| DAO 模式 | 各模組 dao 包 | 資料存取邏輯與業務邏輯解耦,易切換儲存方式 |
| 工廠模式 | 各模組 dao.factory, business.factory | 隱藏具體實現類,依賴介面程式設計 |
| 單例/靜態工具 | FileIOUtil, IDGenerate, DateUtils | 無狀態工具類集中管理,避免重複建立 |
| 枚舉 | UserTypeEnum, UUIDModelConstance | 類型安全、防止非法值、自帶轉換方法 |
| 模板方法/策略 | EBI/EBO 分離 | 業務介面固定,實現可替換 (如未來換資料庫) |
第二部分將詳細展示**用戶模組**完整實作:VO、DAO、EBI/EBO、UI 面板 (列表、新增、修改、刪除、查詢) 及事件處理邏輯。