定義
分為三個部分,模型(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 的效率可能低落
在〈MVC Pattern(Model View Controller)〉中有 1 則留言
[…] MVC 在 POSA 的說明可以參考這篇,本篇是套用 MVC 於 Android […]