MIDP UI 僅有 20 幾個類,看似簡單,但當界面超過 7-8 個時,若將導航邏輯分散在各 CommandListener 中,代碼會迅速變成「義大利麵條」:可讀性差、擴展性差、可維護性差。本文展示如何引入 MVC 模式,建立集中式導航控制器,徹底解耦界面與業務邏輯。
| 層級 | 職責 | 典型類別 |
|---|---|---|
| Model | 資料與業務邏輯(電話簿記錄、RMS 存取) | PhoneBookModel |
| View | 界面呈現與使用者互動 | IndexFunctionUI、NewPhoneUI、ListPhoneUI… |
| Controller | 導航協調、事件分發、界面切換 | UIController |
public class UIController {
private PhoneBookMIDlet phoneBookMIDlet;
private Display display;
private Model model;
// 各界面實例
private IndexFunctionUI indexFunctionUI;
private NewPhoneUI newPhoneUI;
// ...
public UIController(PhoneBookMIDlet pbm) {
this.phoneBookMIDlet = pbm;
}
}
public static class EventID {
private EventID() {}
// UI 導航事件
public static final byte EVENT_NEWPHONE_BACK_MAINUI = 3;
public static final byte EVENT_LISTPHONE_BACK_MAINUI = 4;
// 功能指令事件
public static final byte ADD_NEW_RECORD = 100;
public static final byte LIST_RECORD = 103;
// ...
}
public void init(Model model) {
this.model = model;
this.display = Display.getDisplay(phoneBookMIDlet);
indexFunctionUI = new IndexFunctionUI(this);
newPhoneUI = new NewPhoneUI(this);
listPhoneUI = new ListPhoneUI(this);
searchPhoneUI = new SearchPhoneUI(this);
displayWelcome(); // 啟動畫面
}
public void setCurrent(Displayable disp) {
display.setCurrent(disp);
}
public void handleEvent(int eventID) {
switch (eventID) {
case EventID.ADD_NEW_RECORD:
display.setCurrent(newPhoneUI);
break;
case EventID.LIST_RECORD:
display.setCurrent(listPhoneUI);
break;
case EventID.SEARCH_RECORD:
display.setCurrent(searchPhoneUI);
break;
case EventID.EVENT_NEWPHONE_BACK_MAINUI:
case EventID.EVENT_LISTPHONE_BACK_MAINUI:
case EventID.EVENT_SEARCHUI_BACK_MAINNUI:
display.setCurrent(indexFunctionUI);
break;
default:
break;
}
}
// 重載:攜帶參數的事件(如搜尋關鍵字、編輯資料)
public void handleEvent(int eventID, Object[] obj) {
// 擴展點
}
public class NewPhoneUI extends Form implements CommandListener {
private UIController uiController;
private Command saveCommand, backCommand;
public NewPhoneUI(UIController controller) {
super("新增聯絡人");
this.uiController = controller;
// 初始化輸入欄位、Command
this.addCommand(saveCommand);
this.addCommand(backCommand);
this.setCommandListener(this);
}
public void commandAction(Command c, Displayable d) {
if (c == backCommand) {
// 僅通知控制器事件代碼,不關心具體跳轉邏輯
uiController.handleEvent(UIController.EventID.EVENT_NEWPHONE_BACK_MAINUI);
} else if (c == saveCommand) {
// 收集資料,交由 Model 處理,再通知控制器
uiController.handleEvent(UIController.EventID.EVENT_SAVE_RECORD, new Object[]{name, phone});
}
}
}
GroupManageUI 類,建構子接受 UIControllerUIController.init() 中實例化並保存引用EventID 增加 MANAGE_GROUPS,在 handleEvent() 添加 case 分支零修改既有界面代碼,符合開放封閉原則。
UIController,一目了然「想起來,這種解決方法真是非常經典!!」—— 原作者感嘆。是的,經典之所以經典,是因為它解決了本質複雜度。