深入淺出 MVC
-
Upload
jace-ju -
Category
Technology
-
view
23.826 -
download
3
description
Transcript of 深入淺出 MVC
深入淺出 MVCJace Ju
2011年8月4日星期四
MVC 演化史• http://huoding.com/2011/05/02/64
• 最早是由 Xerox PARC 提出,並以 Smalltalk 實作。
2011年8月4日星期四
MVC 的目的• 將資料運算邏輯中的 UI 邏輯分離出來。
• 在從分離出來的 UI 邏輯中,再次分離流程邏輯。
2011年8月4日星期四
MVC 是複雜的三角關係?
2011年8月4日星期四
ViewController
Model
圖解 MVC
2011年8月4日星期四
ViewController
Model
圖解 MVC
Controller 初始化 Model(箭頭方向表示知悉該物件)
2011年8月4日星期四
ViewController
Model
圖解 MVC
View 向 Model 註冊
Controller 初始化 View
2011年8月4日星期四
ViewController
Model
圖解 MVC
使用者看到 View 的初始輸出
2011年8月4日星期四
ViewController
Model
圖解 MVC
使用者向 Controller發出請求
2011年8月4日星期四
ViewController
Model
圖解 MVC
Controller 改變 Model 狀態 Model 再次通知 View
2011年8月4日星期四
ViewController
Model
圖解 MVC
使用者看到 View 改變了輸出
2011年8月4日星期四
MVC 重點• 使用者接觸的對象是 Controller 。
• View 與 Controller 其實沒有分得這麼開。
• 這是古早時代桌面應用軟體的觀點。
2011年8月4日星期四
所以後來出現了 MVP
叫我嗎?
2011年8月4日星期四
ViewPresenter
Model
圖解 MVP
2011年8月4日星期四
View
Model
圖解 MVP
View 初始化 Presenter
Presenter
使用者看到 View 的初始輸出
2011年8月4日星期四
View
Model
圖解 MVP
Presenter 初始化 Model
Presenter
2011年8月4日星期四
View
Model
圖解 MVP
使用者透過 View來發出請求
View 呼叫 Presenter來處理使用者的請求
Presenter
2011年8月4日星期四
View
Model
圖解 MVP
Persenter 改變並獲取 Model 狀態
Presenter
2011年8月4日星期四
View
Model
圖解 MVP
使用者看到 View 改變了輸出
PresenterPresenter
將 Model 的狀態更新到 View 上面
2011年8月4日星期四
MVP 重點• 主角是 View ,使用者接觸的對象也是
View 。
• Presenter 取代 Controller 的位置,派送的角色由 View 來扮演。
• 微軟的 WinForms 就實作了這個模式。
• 有兩種形式: Supervising Controller 和Passive View 。
2011年8月4日星期四
Supervising Controller
• 可以讓 View 直接觀察 Model 的狀態變化。
• 做的事跟 MVC 的 Controller 差不多,但不負責派送。
2011年8月4日星期四
Passive View
• 由 Presenter 統一收集 Model 狀態,再轉交給 View 。
• View 不需要知道 Model 的存在。
• Presenter 的工作量比較大。
• View 便於做測試。
2011年8月4日星期四
MVC / MVP是在物件狀態持續的狀況下所延伸出來的互動模式
2011年8月4日星期四
那麼...無狀態的 Web Application
能用 MVC 嗎?
2011年8月4日星期四
當然可以
2011年8月4日星期四
Web MVC
2011年8月4日星期四
• 最早的 Web MVC 是 Java 的 Model 2 。
• 接下來是 Java 的 Struts 。
• PHP 界也有 PHPMVC 。
• 發揚光大的是 Ruby on Rails。
• 最後就是一堆 Web Framework 瘋狂地採用 Web MVC 了。
2011年8月4日星期四
義大利麵條程式碼
<?php// 列表頁 (index.php)$link = mysql_connect('127.0.0.1', 'username', 'password');mysql_query('SET NAMES utf8');mysql_select_db('mvc', $link);?><!DOCTYPE html><html><head><meta charset="UTF-8" /><title>News</title></head><body><?php$sql = "SELECT * FROM news " . "WHERE onlineDateTime <= NOW() " . "AND NOW() < offlineDateTime ORDER BY id";$result = mysql_query($sql, $link);?><ul class="posts"><?php while ($row = mysql_fetch_assoc($result)): ?><li><a href="detail.php?id=<?php echo intval($row['id']); ?>"><?php echo htmlspecialchars($row['title']); ?></a></li><?php endwhile; ?></ul></body></html><?phpmysql_close($link);?>
2011年8月4日星期四
義大利麵條程式碼
<?php// 明細頁 (detail.php)$link = mysql_connect('127.0.0.1', 'username', 'password');mysql_query('SET NAMES utf8');mysql_select_db('mvc', $link);?><!DOCTYPE html><html><head><meta charset="UTF-8" /><title>News</title></head><body><?php$id = (int) $_GET['id'];$sql = "SELECT * FROM news " . "WHERE onlineDateTime <= NOW() " . "AND NOW() < offlineDateTime AND id = $id";$result = mysql_query($sql, $link);?><div class="post"><?php if ($row = mysql_fetch_assoc($result)): ?><h1><?php echo htmlspecialchars($row['title']); ?></h1><div><?php echo nl2br(htmlspecialchars($row['content'])); ?></div><?php endif; ?></div></body></html><?phpmysql_close($link);?>
2011年8月4日星期四
將 View 從程式中獨立出來
2011年8月4日星期四
<?phpclass View { private $_vars = array();
public function __set($name, $val) { $this->_vars[$name] = $val; }
public function __get($name) { return isset($this->_vars[$name]) ? $this->_vars[$name] : null; }
public function display($tpl) { include $tpl; }}
View
2011年8月4日星期四
<?php// 列表頁 (index.php)$link = mysql_connect('127.0.0.1', 'username', 'password');mysql_query('SET NAMES utf8');mysql_select_db('mvc', $link);
$sql = "SELECT * FROM news " . "WHERE onlineDateTime <= NOW() " . "AND NOW() < offlineDateTime ORDER BY id";$result = mysql_query($sql, $link);
$newsList = array();while ($row = mysql_fetch_assoc($result)) { $newsList[] = $row;}
require_once 'View.php';$view = new View();$view->newsList = $newsList;$view->display('index.tpl');
mysql_close($link);?>
<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>News</title></head><body><ul class="posts"><?php foreach ($this->newsList as $row): ?><li><a href="detail.php?id=<?php echo intval($row['id']); ?>"><?php echo htmlspecialchars($row['title']); ?></a></li><?php endforeach; ?></ul></body>
2011年8月4日星期四
將 Model 從程式中獨立出來
2011年8月4日星期四
<?php// News.phpclass News {
private $_link = null; public function __construct() { $this->_link = mysql_connect('127.0.0.1', 'username', 'password'); mysql_query('SET NAMES utf8'); mysql_select_db('mvc', $this->_link); } public function __destruct() { mysql_close($this->_link); }
Model
2011年8月4日星期四
public function findAll() { $sql = "SELECT * FROM news " . "WHERE onlineDateTime <= NOW() " . "AND NOW() < offlineDateTime ORDER BY id"; $result = mysql_query($sql, $this->_link);
$newsList = array(); while ($row = mysql_fetch_assoc($result)) { $newsList[] = $row; } return $newsList; } public function find($id) { $sql = "SELECT * FROM news " . "WHERE onlineDateTime <= NOW() " . "AND NOW() < offlineDateTime AND id = $id"; $result = mysql_query($sql, $this->_link);
return mysql_fetch_assoc($result); }}
Model (續)
2011年8月4日星期四
<?php// 列表頁 (index.php)
require_once 'News.php';
$newsModel = new News();$newsList = $newsModel->findAll();
require_once 'View.php';$view = new View();$view->newsList = $newsList;$view->display('index.tpl');?>
<?php// 明細頁 (detail.php)
require_once 'News.php';
$id = (int) $_GET['id'];$newsModel = new News();$row = $newsModel->find($id);
require_once 'View.php';$view = new View();$view->row = $row;$view->display('detail.tpl');?>
Model 以外的程式
2011年8月4日星期四
完成 Controller
2011年8月4日星期四
<?php
class Controller {
private $_action = 'index';
private $_newsModel = null;
private $_view = null;
public function __construct() { if (isset($_GET['action'])) { $action = strtolower($_GET['action']); if (method_exists($this, $action)) { $this->_action = $action; } } $this->_init(); call_user_func(array($this, $this->_action)); }
protected function _init() { require_once 'News.php'; $this->_newsModel = new News();
require_once 'View.php'; $this->_view = new View(); }
Controller
2011年8月4日星期四
public function index() { $this->_view->newsList = $this->_newsModel->findAll(); $this->_view->display('index.tpl'); }
public function detail() { $id = (int) $_GET['id']; $this->_view->row = $this->_newsModel->find($id); $this->_view->display('detail.tpl'); }}
Controller (續)
<?php// 進入點 (index.php)require_once 'Controller.php';$controller = new Controller();
2011年8月4日星期四
<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>News</title></head><body><ul class="posts"><?php foreach ($this->newsList as $row): ?><li><a href="index.php?action=detail&id=<?php echo intval($row['id']); ?>"><?php echo htmlspecialchars($row['title']); ?></a></li><?php endforeach; ?></ul></body>
View
原來的連結是detail.php?id=<?php echo intval($row['id']); ?>
2011年8月4日星期四
Web MVC 重點• 每一次的 http request 都是獨立的 MVC 流程。
• 可以讓 View 直接參考 Model ,而不需要透過 Observer Pattern 。
• View 不擁有 Controller 的參考,使用者直接透過瀏覽器發出的 http request 來呼叫 Controller 。
2011年8月4日星期四
ViewController
Model
Web MVC 基本概念圖
Dispatcher
Route
Multi-formats
Template
Business Logic
Data Access
2011年8月4日星期四
各家 Web Framework 實作 MVC 的方式不見得相同
2011年8月4日星期四
常見的 Controller 實作方式
• Action Controller
• Command Object
2011年8月4日星期四
既然有 Web MVC那麼有 Web MVP 嗎?
2011年8月4日星期四
當然有微軟的 WebForms 就是了
2011年8月4日星期四
JavaScript 可以用MVC 或 MVP 嗎?
2011年8月4日星期四
當然可以
2011年8月4日星期四
• JavaScriptMVC (http://javascriptmvc.com/)
• Backbone.js (http://documentcloud.github.com/backbone/)
• Spine (http://maccman.github.com/spine/)
常見的 JavaScript MVC Framework
2011年8月4日星期四
簡易實作 JavaScript MVP
2011年8月4日星期四
<!DOCTYPE html><html><head><meta charset="UTF-8" /><title>JavaScript MVP</title></head><body> <form action=""> <input type="text" id="num" value="0" /> <button type="button" id="increase">+</button> <button type="button" id="decrease">-</button> </form> <script src="jquery.js"></script> <script src="mvc.js"></script></body></html>
HTML
2011年8月4日星期四
var myapp = {};
mvc.js (app)
2011年8月4日星期四
myapp.Model = function () { var val = 0; this.add = function (v) { val += v; }; this.sub = function (v) { val -= v; }; this.getVal = function () { return val; };};
mvc.js (Model)
2011年8月4日星期四
myapp.Presenter = function () { var model = null; this.init = function () { model = new myapp.Model(); }; this.increase = function () { model.add(1); }; this.decrease = function () { model.sub(1); }; this.getModel = function () { return model; }};
mvc.js (Presenter)
2011年8月4日星期四
myapp.view = { $increaseButton: null, $decreaseButton: null, $num: null, presenter: null, init: function () { this.presenter = new myapp.Presenter(); this.$increaseButton = $('#increase'); this.$decreaseButton = $('#decrease'); this.$num = $('#num'); this.presenter.init(); this.bindEvents(); this.$num.val(this.presenter.getModel().getVal()); }, bindEvents: function () { var view = this; var presenter = this.presenter; this.$increaseButton.click(function () { presenter.increase(); view.$num.val(presenter.getModel().getVal()); }); this.$decreaseButton.click(function () { presenter.decrease(); view.$num.val(presenter.getModel().getVal()); }); }};
mvc.js (View)
2011年8月4日星期四
var myapp = {};
myapp.Model = function () { // ...};
myapp.Presenter = function () { // ...};
myapp.view = { // ...};
$(function () { myapp.view.init();})
mvc.js (執行)
2011年8月4日星期四
越複雜的 UI 越需要以 MVP 來整合
2011年8月4日星期四
以上的基本概念希望能幫助大家瞭解
MVC / MVP
2011年8月4日星期四
問題與討論
2011年8月4日星期四
謝謝大家
2011年8月4日星期四