定義
分為三個部分,模型(Model)包含核心功能和數據。視圖(View)展示訊息。控制器(Controller)處理使用者輸入。
View 和 Controller 共同構成使用者介面(UI)。變更–傳播機制確保了使用者介面和模型之間的一致性。
主要設計思想
一般來說修改使用者介面的機會往往大於修改業務邏輯的機會,在修改業務邏輯時不必變動使用者介面的代碼(關注點分離)。
因此需要把使用者介面和業務邏輯分離。
1.相同的訊息在不同介面有不同的顯示。
2.應用程式的顯示和行為必須立即反應數據的改變。
3.使用者介面應該易於改變。
4.可以移植使用者介面而不影響核心功能。
解決方案
MVC 將應用程序分為 3 個部份,輸入–處理–輸出。
Model 封裝了數據以及核心功能。
View 向使用者顯示訊息,View 從 Model 獲得數據,一個 Model 可能有多個 View。
每個 View 都有一個相關的 Controller,Controller 接受輸入,該輸入通常為外部輸入e.g.,滑鼠,鍵盤事件,
而事件被轉換為 Model 或 View 的請求。使用者僅透過 Controller 與系統交互。
MVC 的分離讓同一個 Model 可以有多個 View。如果使用者透過一個 View 的 Controller 去改變了 Model,那麼所有相依於該 Model 的 View 都應該反應這種變化。
因此一旦 Model 的數據發生變化,Model 要通報所有 View。而 View 從 Model 取得新數據並顯示變化。這可以透過 Observer Pattern 來達成。
結構
1.Model 包含核心功能,封裝相應數據並提供可存取數據的函式讓外部存取,這些數據讓View 來使用。
變更–傳播機制維護一個註冊表。所有的 View 和 Controller 註冊他們有關變更的待通知的需求。Model 狀態的改變觸發變更–傳播機制。
2.View 向使用者顯示訊息,不同的 View 用不同的方式呈現 Model 的訊息。
每個 View 定義一個被變更–播機制觸發的更新過程。當更新過程被調用時,View 就會從Model 取得並顯示新的數據。
初始化階段,所有的 View 都與 Model 相關,並向變更–傳播機制註冊。每個 View 將建立一個對應的 Controller。View 和 Controller 的關係是一對一的。
View 通常會提供讓 Controller 操作其顯示的功能。這對不影響 Model 狀態的動作是有必要的。如捲動畫面。
3.Controller 接受使用者的輸入事件,事件如何被發送到 Controller 是取決於使用者平台。Controller 接受事件後會轉發為對 View 或是 Model 的請求。
如果 Controller 的行為依賴於 Model,那麼 Controller 也必須向 Model 註冊自己並實現更新過程。
動態特性
1.初始化步驟:
建立 Model 實體,初始化 Model 內部數據。
建立 View 實體,需要取得 Model 的實體作為本身初始化參數。
View 加入 Model 的變更–傳佈機制。
View 初始化 Controller , View 向 Controller 的初始化過程傳入 Model 和其本身當作參數。
Controller 加入 Model 的變更–傳播機制。
應用程式開始處理輸入事件。
2.如何處理輸入:
Controller 接受使用者輸入並轉換事件,啟動 Model 的服務過程。
Model 執行請求並改變了內部數據。
Model 調用更新過程,通知所有註冊的 View 和 Controller。
每個有註冊的 View 向 Model 取得更新的數據並顯示。
每個有註冊的 Controller 向 Model 更新的數據並執行相對應動作。
實作:
步驟1~6為基本實現,步驟7~9為增加靈活的實用性。
1.建立 Model 實現核心功能。
除了核心功能外,Model 會持有 observer 的集合,以及加入和刪除 observer 方法。
最後加上通知所有的 observer 狀態更新的方法。
public class RandomModel { private static final int RANDOM_NUMBER_MAX = 10000; private int mRandomNumber; private ArrayList<IObserver> mObservers = new ArrayList<IObserver>(); public void rollOnce() { mRandomNumber = new Random().nextInt(RANDOM_NUMBER_MAX); notifyObservers(); } public int getRandomNumber() { return mRandomNumber; } public void addObserver(IObserver observer) { mObservers.add(observer); } public void removeObserver(IObserver observer) { if(mObservers.contains(observer)){ mObservers.remove(observer); } } private void notifyObservers() { int sizeOfObservers = mObservers.size(); for (int i = 0; i < sizeOfObservers ; ++i) { mObservers.get(i).update(this); } } }
2.實現變更–傳播機制
建立 observer 介面,observer 會有一個 update 方法,該方法會讓所有的 observer 子類別複寫。
public interface IObserver { public void update(RandomModel model); }
3.建立 View
View 除了提供所有的可見元素之外,還必須實作 observer 介面並複寫 update 方法,在update 方法中藉由 Model 取得新數據以更新相關的可見元素。
另外在 View 的建構式中必須註冊變更–傳播機制,以及建立 Controller。
public class RandomView implements ActionListener, IObserver { private static final int VIEW_HEIGHT = 65; private static final int VIEW_WIDTH = 400; private static final String VIEW_TITLE = "RandomView"; private static final String NAME_OF_ROLL_BUTTON = "Roll it"; private RandomModel mRandomModel; private RandomController mRandomController; private JFrame mViewFrame; private JPanel mViewPanel; private JButton mButtonRollOnce; private JTextField mTextField; public RandomView(RandomModel randomModel) { mRandomModel = randomModel; mRandomModel.addObserver(this); mRandomController = new RandomController(mRandomModel,this); initLayoutComponents(); } public void initLayoutComponents() { mViewPanel = new JPanel(new GridLayout()); mViewFrame = new JFrame(VIEW_TITLE); mButtonRollOnce = new JButton(NAME_OF_ROLL_BUTTON); mButtonRollOnce.addActionListener(this); mTextField = new JTextField(); mViewFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mViewFrame.setSize(new Dimension(VIEW_WIDTH, VIEW_HEIGHT)); mViewPanel.add(mButtonRollOnce); mViewPanel.add(mTextField); mViewFrame.add(mViewPanel); mViewFrame.setVisible(true); } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == mButtonRollOnce) { mRandomController.startRollOnce(); } } @Override public void update(RandomModel model) { int randomNumber = model.getRandomNumber(); mTextField.setText(String.valueOf(randomNumber)); System.out.println("in RandomView update"); } }
4.實作 Controller
Controller 必須實作 observer 介面並複寫 update 方法。
Controller 會將使用者輸入轉換為對 Model 或是 View 的事件請求。
public class RandomController implements IObserver{ private RandomModel mRandomModel; private RandomView mRandomView; public RandomController(RandomModel model, RandomView view){ mRandomView = view; mRandomModel = model; mRandomModel.addObserver(this); } public void startRollOnce(){ mRandomModel.rollOnce(); } @Override public void update(RandomModel model) { System.out.println("in RandomController update"); } }
5.設計並實作 View 和 Controller 關係
通常 View 和 Controller 之間為一對一。
6.實現 MVC 的準備。
public class Main { public static void main(String[] args){ RandomModel randomModel = new RandomModel(); RandomView randomView = new RandomView(randomModel); //otherView with tha same Model //OtherView otherView = new OtherView(RandomModel); } }
7.建立動態 View
如果應用程序具有動態打開和關閉 View 的功能,那麼可以建立一個用來管理 View 的元件。
8.可更換 Controller
View 可支援更換不同的 Controller,這種情況通常是對同一個 View 的不同操作。
e.g.,預設的 Controller 予許使用者進行操作,但在一些情況下禁止使用者進行任何操作(read only),為此可以在 View 中建立替換 Controller 的方法。
public void setController(OtherController controller){ mRandomController = controller; }
9.層次化 View 和 Controller
MVC 實現了可重用的 View 和 Controller,對於經常層次化使用的使用者接口元素(button, menu, editor)也是如此,
如果應用程序的使用者接口主要靠結合事先定義好的 View 對象來建立,那麼可以用組合模式來建立層次式靜態 View。
如果同時啟動多個 View,那麼可能就有好幾個 Controller 同時關心事件,如果事件以某種順序分配到所有的 Controller 來處理,可以使用責任鏈模式管理事件的委託。
變體
Document View Pattern
View 結合了 Controller 的任務,兩者合併 。
已知應用
MFC
ET++
效果
優點:
1.同一 Model 的多個 View
2.同步化 View
3.可更換的 View 和 Controller
缺點:
增加複雜性
View 和 Controller 的緊密連結
View 的效率可能低落
Article Comments