定義

分為三個部分,模型(Model)包含核心功能和數據。視圖(View)展示訊息。控制器(Controller)處理使用者輸入。
View 和 Controller 共同構成使用者介面(UI)。變更傳播機制確保了使用者介面和模型之間的一致性。

主要設計思想

一般來說修改使用者介面的機會往往大於修改業務邏輯的機會,在修改業務邏輯時不必變動使用者介面的代碼(關注點分離)。

因此需要把使用者介面和業務邏輯分離。
1.相同的訊息在不同介面有不同的顯示。
2.應用程式的顯示和行為必須立即反應數據的改變。
3.使用者介面應該易於改變。
4.可以移植使用者介面而不影響核心功能。

解決方案

MVC 將應用程序分為 個部份,輸入處理輸出。
Model 封裝了數據以及核心功能。
View 向使用者顯示訊息,View Model 獲得數據,一個 Model 可能有多個 View
每個 View 都有一個相關的 ControllerController 接受輸入,該輸入通常為外部輸入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 將建立一個對應的 ControllerView 和 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 更新的數據並執行相對應動作。

實作:

步驟16為基本實現,步驟79為增加靈活的實用性。
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 的效率可能低落