Swing 應用中,界面間跳轉常見反模式:
prevView)if (prev instanceof ViewA) ... else if (prev instanceof ViewB) ... (文中 600 行案例)// 基礎容器:管理面板切換、提供控件存取
public abstract class BaseView extends JWindow {
// 獲取按鈕、表格、下拉框等控件的統一方法
public abstract JPanel getPanel(); // 當前界面核心面板
}
// 具體界面:僅負責構建自己的面板
public class MyView extends BaseView {
private JPanel myPanel;
public void layoutPanel() {
JButton btn = new JButton("OK");
myPanel.add(btn);
// 佈局邏輯...
}
@Override public JPanel getPanel() { return myPanel; }
}
class ViewPath {
private final JPanel[] path = new JPanel[10]; // 最大深度 10
private int index = 0; // 當前位置
private final BaseView container; // 所屬容器 (同一個 BaseView 實例)
ViewPath(JPanel startPanel, BaseView container) {
this.container = container;
path[0] = startPanel;
}
// 查找面板在路徑中的位置
int find(JPanel panel) {
for (int i = 0; i <= index; i++) {
if (path[i] == panel) {
index = i; // 更新當前位置
return i;
}
}
return -1;
}
// 獲取/設置導航相關屬性...
}
public class ViewJump {
private static ViewPath[] paths = new ViewPath[0]; // 路徑池
// 1. 註冊/獲取路徑 ID
public static int registerPath(JPanel currentPanel, BaseView container) {
int idx = findPath(currentPanel);
if (idx == -1) {
idx = createNewPath(currentPanel, container);
}
return idx;
}
// 2. 設置下一跳目標
public static void setNext(int pathId, JPanel nextPanel) {
ViewPath vp = paths[pathId];
vp.path[vp.index + 1] = nextPanel;
}
// 3. 跳轉到下一界面
public static void jump(int pathId) {
ViewPath vp = paths[pathId];
if (vp.path[vp.index + 1] == null) return;
vp.container.remove(vp.path[vp.index]);
vp.index++;
vp.container.add(vp.path[vp.index], BorderLayout.CENTER);
vp.container.validate();
vp.container.repaint();
}
// 4. 返回上一界面
public static void back(int pathId) {
ViewPath vp = paths[pathId];
if (vp.index > 0) {
vp.container.remove(vp.path[vp.index]);
vp.index--;
vp.container.add(vp.path[vp.index], BorderLayout.CENTER);
vp.container.validate();
vp.container.repaint();
}
}
// 5. 返回首頁
public static void backHome(int pathId) { ... }
// 內部輔助方法:findPath, createNewPath (含路徑池擴容/清理)
}
public void jumpToMyView() {
MyView myView = new MyView();
int pathId = ViewJump.registerPath(this.currentPanel, this);
ViewJump.setNext(pathId, myView.getPanel());
ViewJump.jump(pathId);
}
protected void goBack() {
int pathId = ViewJump.registerPath(this.myPanel, this);
ViewJump.back(pathId);
}
| 維度 | 傳統硬編碼 | ViewJump 方案 |
|---|---|---|
| 耦合度 | 雙向強耦合 | 零耦合:界面互不知曉 |
| 新增界面成本 | 修改所有相關跳轉點 | 零修改現有代碼 |
| 代碼行數 | 600+ 行判斷邏輯 | 每跳轉點 3 行 |
| 運行時靈活性 | 靜態固定 | 動態路徑、可回溯、可分叉 |
| Session 狀態 | 分散各界面 | 集中在容器/路徑上下文 |
public interface ViewContainer {
void showPanel(Panel panel);
Panel getCurrentPanel();
}
public interface NavigableView {
Panel getPanel();
ViewContainer getContainer();
}
ViewPath[] 實現應用重啟後恢復導航棧WindowListener 自動保存
public static void jumpAsync(int pathId) {
SwingWorker loader = new SwingWorker<>() {
@Override protected JPanel doInBackground() {
return loadHeavyPanel(); // 耗時界面構建
}
@Override protected void done() {
try { ViewJump.setNext(pathId, get()); ViewJump.jump(pathId); }
catch (Exception e) { handleError(e); }
}
};
loader.execute();
}
public static void setNextWithAuth(int pathId, JPanel nextPanel, String requiredRole) {
if (SecurityContext.hasRole(requiredRole)) {
setNext(pathId, nextPanel);
} else {
setNext(pathId, accessDeniedPanel);
}
}
引入 第三方導航管理器 消除界面間直接依賴,以 路徑陣列 記錄跳轉歷史,實現 O(1) 導航操作、零耦合擴展、統一狀態管理。此模式適用於任何「基於容器面板切換」的富客戶端框架 (Swing, JavaFX, SWT, Android Fragment 等),是解決界面導航複雜度的經典架構模式。