mvc是什么
軟體架構模式中,MVC是開發者耳熟能詳的,然而由於Web應用程式的流行,今日多數開發者談及MVC聯想到的,其實是Model 2。
基本上,MVC與Model 2都是將系統畫分為Model、View和Controller三個部份,最大不同在於Model與View間的互動方式,隨著軟、硬體技術的進化與應用程式規模的擴展,MVC/Model 2的應用方式也在調整、改變與結合。
维基百科(传统桌面mvc模式)
MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
控制器: 负责请求转发,对请求进行处理
视图: 界面人员进行图形界面设计
模型: 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
组件
- 模型(Model)
用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“ Model ”有对数据直接访问的权力,例如对数据库的访问。“Model”不依赖“View”和“Controller”,也就是说, Model 不关心它会被如何显示或是如何被操作。但是 Model 中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此 Model 的 View 必须事先在此 Model 上注册,从而,View 可以了解在数据 Model 上发生的改变。(比较:观察者模式(软件设计模式))
- 视图(View)
能够实现数据有目的的显示(理论上,这不是必需的)。在 View 中一般没有程序上的逻辑。为了实现 View 上的刷新功能,View 需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。
- 控制器(Controller)
起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据 Model 上的改变。
练习
心脏起搏器
代码
package heart_driver;
public class HeartDriver {
public static void main(String[] args) {
new HeartDriver();
}
public HeartDriver() {
Controller controller = new Controller();
Model model = new Model();
View view = new View();
view.setController(controller);
controller.setModel(model);
controller.setView(view);
model.addHeartBeatListener(view);
view.slider.setValue(75); model.frequency = 75; }
}
Model
package heart_driver;
import java.util.ArrayList;
interface HeartBeatListener {
public void onHeartBeat();
}
class Model {
int frequency;
boolean on;
ArrayList<HeartBeatListener> listeners = new ArrayList<>();
public void addHeartBeatListener(HeartBeatListener listener) {
listeners.add(listener);
}
public void removeHeartBeatListener(HeartBeatListener listener) {
listeners.remove(listener);
}
public void setFrequency(int freq) {
this.frequency = freq;
}
public void on() {
on = true;
new Thread(new Runnable() {
@Override
public void run() {
while(on) {
for(HeartBeatListener listener : listeners)
listener.onHeartBeat();
try {
Thread.sleep(60000/frequency);
} catch (InterruptedException e) {}
}
}
}).start();
}
public void off() {
on = false;
}
}
View
package heart_driver;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class View extends JFrame implements HeartBeatListener,ActionListener,ChangeListener {
Controller controller;
JButton onButton,offButton;
JSlider slider;
JProgressBar bar;
public View() {
JPanel buttonPanel = new JPanel();
onButton = new JButton("on");
offButton = new JButton("off");
onButton.addActionListener(this);
offButton.addActionListener(this);
buttonPanel.add(onButton);
buttonPanel.add(offButton);
add(buttonPanel,BorderLayout.NORTH);
bar = new JProgressBar();
bar.setForeground(Color.RED);
add(bar,BorderLayout.CENTER);
slider = new JSlider();
slider.setMaximum(200);
slider.setMinimum(1);
slider.setValue(70);
slider.addChangeListener(this);
JPanel sliderPanel = new JPanel();
sliderPanel.add(new JLabel("频率:"));
sliderPanel.add(slider);
add(sliderPanel,BorderLayout.SOUTH);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("心脏起搏器");
off();
setVisible(true);
}
public void setController(Controller controller) {
this.controller = controller;
}
public void on() {
offButton.setEnabled(true);
onButton.setEnabled(false);
}
public void off() {
offButton.setEnabled(false);
onButton.setEnabled(true);
}
@Override
public void onHeartBeat() {
try {
for(int i=0;i<bar.getMaximum();i+=bar.getMaximum()/5) {
bar.setValue(i);
Thread.sleep(20);
}
for(int i=bar.getMaximum();i>=0;i-=bar.getMaximum()/5) {
bar.setValue(i);
Thread.sleep(20);
}
} catch (InterruptedException e) {}
}
@Override
public void stateChanged(ChangeEvent e) {
int freq = ((JSlider)e.getSource()).getValue();
controller.setFrequency(freq);
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==onButton)
controller.on();
else if(e.getSource()==offButton)
controller.off();
}
}
Controller
package heart_driver;
class Controller {
View view;
Model model;
public void setView(View view) {
this.view = view;
}
public void setModel(Model model) {
this.model = model;
}
public void setFrequency(int freq) {
model.setFrequency(freq);
}
public void on() {
view.on();
model.on();
}
public void off() {
view.off();
model.off();
}
}
优缺点
优点
由controller协调,view不会直接对model做出任何变更,view只负责呈现逻辑,即根据model资料呈现结果,相关操作对应的业务逻辑不在view中。因此可以用不同的呈现方式,gui , console
controller负责商务逻辑中请求参数的收集与检验,更复杂的业务逻辑交给model处理,使得controller维持轻巧,model也有较高的重用性,controller也负责调配对应的view,所以model不会与view耦合。
Model不會知道或決定View實際呈現方式,因此View可以獨立於Model,自由變化。
Model狀態有變化時,曾向Model註冊過的多個View,都可獲得通知而得到同步更新效果。
第三項也許是最重要的優點,相較於Controller與Model來說,View相對來說最常變化,畢竟使用者往往對操作畫面要求最多也最善變,將Model與View切割後,Model就可以相對穩定地進行發展;而且View往往會由專門的使用者介面設計者負責,可能是專門的美術或設計師,未必擅長實際採用的技術,但可透過工具進行介面設計,讓View可以獨立於技術成份重的Model,自由變化,有助於團隊成員間的分工合作。
其他
Model2模式
軟體架構模式中,MVC是開發者耳熟能詳的,然而由於Web應用程式的流行,今日多數開發者談及MVC聯想到的,其實是Model 2。
基本上,MVC與Model 2都是將系統畫分為Model、View和Controller三個部份,最大不同在於Model與View間的互動方式,隨著軟、硬體技術的進化與應用程式規模的擴展,MVC/Model 2的應用方式也在調整、改變與結合。
實際上MVC模式本身並沒有明確定義,因而採納MVC模式中最大特徵「將系統畫分為Model、View和Controller三個部份」,並將三個部份的互動流程因實際應用場合進行調整,都可說是一種MVC模式,其中最為人熟悉的,就是Web應用程式經常採用的Model 2,又稱Web MVC,或由於過於流行而直接稱MVC了。
Web應用程式先天上,會被畫分為Model、View和Controller三個部份,View通常是客戶端的瀏覽器依取得的HTML繪製畫面,Controller位於Web伺服器,與資料庫互動的則是Model;傳統MVC模式實作的桌面應用程式,MVC三個部份通常位於同臺電腦,或者是透過持續網路連線互動。
然而,對於基於HTTP的Web應用程式來說,由於HTTP基於請求/回應(Request/Response)模型,沒有請求就不會有回應,因此無法實現傳統MVC中Model主動通知View的流程,如果View想根據Model最新狀態來呈現畫面,基本作法之一,是透過持續性地主動詢問(Poll)伺服端Model來達成。
基於HTTP限制而調整傳統MVC互動流程,得到的模式稱為Model 2,除了Model無法主動View外,基本上它的優點與傳統MVC前三項優點是相同的。
Model 2概念最早出現在1998年JSP規格書0.92版中,規格書中提及兩個模型,第一個模型稱為Model 1(因為文件提及的順序),第二個模型稱為Model 2。1999年Govind Seshadri在Javaworld發表了《Understanding JavaServer Pages Model 2 architecture》文章,其影響之一是將Model 2界定為架構模式,第二個影響是確立Model 2為Web應用程式提供了一種MVC架構。
Model 1中通常會將請求發給某個網頁動態元件(例如JSP或Servlet),由它處理請求參數收集、驗證、商務處理以及畫面回應,有些商務邏輯亦可能會封裝成Model(例如JavaBean),然而前端元件亦要處理請求參數收集、驗證與畫面回應,程式碼與畫面設計勢必形成義大利麵式的混亂,也因而Model 1通常應用在規模較小、任務簡單的Web應用程式。
mvc 与 model2的异同
儘管由於HTTP模型限制,使得Model 2無法達成傳統MVC中Model主動通知View的流程,然而MVC與Model 2有許多相似點,除了系統同被畫分為Model、View和Controller三個部份之外,Model 2中通常根據URL模式(Pattern)決定哪個Controller要接收View發送的請求參數,實際上在傳統MVC中,Controller名稱與方法簽署(Method signature)就相當於Model 2的URL模式,而方法的參數名稱與實際接收的引數,就相當HTTP請求中的參數名稱與值。
舉例來說,在實現Model 2時可定義從/messages/view?bid=34&id=123,決定使用MessagesController的view方法來處理bid與id請求參數;相對地,若有個桌面應用程式,畫面操作時若呼叫messagesController.view(bid, id),則messagesController變數結合view方法簽署,就相當於Model 2中URL模式,而bid與id變數就相當於請求參數。Web應用程式的網頁會設定發送表單的動作(action)對象,桌面應用程式會設定按鈕操作時呼叫的Controller與方法,兩者的意義實際是相同的。
隨著Ajax技術廣泛應用,以及伺服端非同步處理技術的進步,由於HTTP模型而造成的限制亦逐漸被克服。Web應用程式中為了取得Model最新狀態,即使使用傳統的持續詢問方式,已不再需要重清(Refresh)整個頁面,可利用Ajax在背景取得代表Model狀態的中性資料,而後用JavaScript更新畫面上相應的部分即可。
如果要作到更接近主動通知的概念,也就是在Model狀態有變化時,View可立即得到更新,伺服端可保留一定數量非同步請求,暫不回應,一旦Model狀態變化再進行回應,效果上就可達到傳統MVC模型中Model主動通知View的流程。
聊聊多样化的mvc/model2
隨著JavaScript、DOM、Ajax應用日漸廣泛,甚至HTML5新API的推波助瀾,同一Web應用程式中實現傳統MVC與Model 2的場合,越來越常見。
傳統Web應用程式中View原本由伺服端產生相關畫面,現在轉移給瀏覽器更動態的呈現,瀏覽器亦可有自成一體的應用程式,由HTML、DOM、CSS作為View的實現,JavaScript用以實現Controller與Model,就能在瀏覽器中實現傳統MVC模式應有流程,瀏覽器中JavaScript撰寫的Model,可將伺服端比擬為資料庫,向伺服端取得資料,就像對資料庫查詢,Ajax是與伺服端溝通的機制,伺服端傳回的資料,可能是中性格式、HTML或CSS組成的畫面片段,甚至是一段待執行的JavaScript程式碼。
無論如何,傳統Model 2中View的職責減輕了,這有助於開發者職責的畫分。過去由伺服端產生所有回應畫面的情境下,網頁設計師雖可獨立於Model設計畫面,但使用的元件仍會與伺服端技術有關(像是JSTL自訂標籤等),如果採用的伺服端技術不同,設計師就得學習使用不同的元件。
當原本伺服端的View將組織畫面的職責卸下,交給瀏覽器處理之後,設計師可專心使用瀏覽器上的JavaScript、DOM、CSS等相關技術,只要協調好與伺服端的資料交換格式,設計師甚至可以先行製作假資料,完全獨立於伺服端工作進度之外,完成自身任務。