深入淺出 MVC

60
深入淺出 MVC Jace Ju 2011年8月4日星期四

description

 

Transcript of 深入淺出 MVC

Page 1: 深入淺出 MVC

深入淺出 MVCJace Ju

2011年8月4日星期四

Page 2: 深入淺出 MVC

MVC 演化史• http://huoding.com/2011/05/02/64

• 最早是由 Xerox PARC 提出,並以 Smalltalk 實作。

2011年8月4日星期四

Page 3: 深入淺出 MVC

MVC 的目的• 將資料運算邏輯中的 UI 邏輯分離出來。

• 在從分離出來的 UI 邏輯中,再次分離流程邏輯。

2011年8月4日星期四

Page 4: 深入淺出 MVC

MVC 是複雜的三角關係?

2011年8月4日星期四

Page 5: 深入淺出 MVC

ViewController

Model

圖解 MVC

2011年8月4日星期四

Page 6: 深入淺出 MVC

ViewController

Model

圖解 MVC

Controller 初始化 Model(箭頭方向表示知悉該物件)

2011年8月4日星期四

Page 7: 深入淺出 MVC

ViewController

Model

圖解 MVC

View 向 Model 註冊

Controller 初始化 View

2011年8月4日星期四

Page 8: 深入淺出 MVC

ViewController

Model

圖解 MVC

使用者看到 View 的初始輸出

2011年8月4日星期四

Page 9: 深入淺出 MVC

ViewController

Model

圖解 MVC

使用者向 Controller發出請求

2011年8月4日星期四

Page 10: 深入淺出 MVC

ViewController

Model

圖解 MVC

Controller 改變 Model 狀態 Model 再次通知 View

2011年8月4日星期四

Page 11: 深入淺出 MVC

ViewController

Model

圖解 MVC

使用者看到 View 改變了輸出

2011年8月4日星期四

Page 12: 深入淺出 MVC

MVC 重點• 使用者接觸的對象是 Controller 。

• View 與 Controller 其實沒有分得這麼開。

• 這是古早時代桌面應用軟體的觀點。

2011年8月4日星期四

Page 13: 深入淺出 MVC

所以後來出現了 MVP

叫我嗎?

2011年8月4日星期四

Page 14: 深入淺出 MVC

ViewPresenter

Model

圖解 MVP

2011年8月4日星期四

Page 15: 深入淺出 MVC

View

Model

圖解 MVP

View 初始化 Presenter

Presenter

使用者看到 View 的初始輸出

2011年8月4日星期四

Page 16: 深入淺出 MVC

View

Model

圖解 MVP

Presenter 初始化 Model

Presenter

2011年8月4日星期四

Page 17: 深入淺出 MVC

View

Model

圖解 MVP

使用者透過 View來發出請求

View 呼叫 Presenter來處理使用者的請求

Presenter

2011年8月4日星期四

Page 18: 深入淺出 MVC

View

Model

圖解 MVP

Persenter 改變並獲取 Model 狀態

Presenter

2011年8月4日星期四

Page 19: 深入淺出 MVC

View

Model

圖解 MVP

使用者看到 View 改變了輸出

PresenterPresenter

將 Model 的狀態更新到 View 上面

2011年8月4日星期四

Page 20: 深入淺出 MVC

MVP 重點• 主角是 View ,使用者接觸的對象也是

View 。

• Presenter 取代 Controller 的位置,派送的角色由 View 來扮演。

• 微軟的 WinForms 就實作了這個模式。

• 有兩種形式: Supervising Controller 和Passive View 。

2011年8月4日星期四

Page 21: 深入淺出 MVC

Supervising Controller

• 可以讓 View 直接觀察 Model 的狀態變化。

• 做的事跟 MVC 的 Controller 差不多,但不負責派送。

2011年8月4日星期四

Page 22: 深入淺出 MVC

Passive View

• 由 Presenter 統一收集 Model 狀態,再轉交給 View 。

• View 不需要知道 Model 的存在。

• Presenter 的工作量比較大。

• View 便於做測試。

2011年8月4日星期四

Page 23: 深入淺出 MVC

MVC / MVP是在物件狀態持續的狀況下所延伸出來的互動模式

2011年8月4日星期四

Page 24: 深入淺出 MVC

那麼...無狀態的 Web Application

能用 MVC 嗎?

2011年8月4日星期四

Page 25: 深入淺出 MVC

當然可以

2011年8月4日星期四

Page 26: 深入淺出 MVC

Web MVC

2011年8月4日星期四

Page 27: 深入淺出 MVC

• 最早的 Web MVC 是 Java 的 Model 2 。

• 接下來是 Java 的 Struts 。

• PHP 界也有 PHPMVC 。

• 發揚光大的是 Ruby on Rails。

• 最後就是一堆 Web Framework 瘋狂地採用 Web MVC 了。

2011年8月4日星期四

Page 28: 深入淺出 MVC

義大利麵條程式碼

<?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日星期四

Page 29: 深入淺出 MVC

義大利麵條程式碼

<?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日星期四

Page 30: 深入淺出 MVC

將 View 從程式中獨立出來

2011年8月4日星期四

Page 31: 深入淺出 MVC

<?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日星期四

Page 32: 深入淺出 MVC

<?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日星期四

Page 33: 深入淺出 MVC

將 Model 從程式中獨立出來

2011年8月4日星期四

Page 34: 深入淺出 MVC

<?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日星期四

Page 35: 深入淺出 MVC

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日星期四

Page 36: 深入淺出 MVC

<?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日星期四

Page 37: 深入淺出 MVC

完成 Controller

2011年8月4日星期四

Page 38: 深入淺出 MVC

<?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日星期四

Page 39: 深入淺出 MVC

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日星期四

Page 40: 深入淺出 MVC

<!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&amp;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日星期四

Page 41: 深入淺出 MVC

Web MVC 重點• 每一次的 http request 都是獨立的 MVC 流程。

• 可以讓 View 直接參考 Model ,而不需要透過 Observer Pattern 。

• View 不擁有 Controller 的參考,使用者直接透過瀏覽器發出的 http request 來呼叫 Controller 。

2011年8月4日星期四

Page 42: 深入淺出 MVC

ViewController

Model

Web MVC 基本概念圖

Dispatcher

Route

Multi-formats

Template

Business Logic

Data Access

2011年8月4日星期四

Page 43: 深入淺出 MVC

各家 Web Framework 實作 MVC 的方式不見得相同

2011年8月4日星期四

Page 44: 深入淺出 MVC

常見的 Controller 實作方式

• Action Controller

• Command Object

2011年8月4日星期四

Page 45: 深入淺出 MVC

既然有 Web MVC那麼有 Web MVP 嗎?

2011年8月4日星期四

Page 46: 深入淺出 MVC

當然有微軟的 WebForms 就是了

2011年8月4日星期四

Page 47: 深入淺出 MVC

JavaScript 可以用MVC 或 MVP 嗎?

2011年8月4日星期四

Page 48: 深入淺出 MVC

當然可以

2011年8月4日星期四

Page 49: 深入淺出 MVC

• JavaScriptMVC (http://javascriptmvc.com/)

• Backbone.js (http://documentcloud.github.com/backbone/)

• Spine (http://maccman.github.com/spine/)

常見的 JavaScript MVC Framework

2011年8月4日星期四

Page 50: 深入淺出 MVC

簡易實作 JavaScript MVP

2011年8月4日星期四

Page 51: 深入淺出 MVC

<!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日星期四

Page 52: 深入淺出 MVC

var myapp = {};

mvc.js (app)

2011年8月4日星期四

Page 53: 深入淺出 MVC

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日星期四

Page 54: 深入淺出 MVC

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日星期四

Page 55: 深入淺出 MVC

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日星期四

Page 56: 深入淺出 MVC

var myapp = {};

myapp.Model = function () { // ...};

myapp.Presenter = function () { // ...};

myapp.view = { // ...};

$(function () { myapp.view.init();})

mvc.js (執行)

2011年8月4日星期四

Page 57: 深入淺出 MVC

越複雜的 UI 越需要以 MVP 來整合

2011年8月4日星期四

Page 58: 深入淺出 MVC

以上的基本概念希望能幫助大家瞭解

MVC / MVP

2011年8月4日星期四

Page 59: 深入淺出 MVC

問題與討論

2011年8月4日星期四

Page 60: 深入淺出 MVC

謝謝大家

2011年8月4日星期四