设计模式MVC

Posted 星点学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式MVC相关的知识,希望对你有一定的参考价值。

今天我们来学习一下设计模式吧

这次我吸取了上两次的经验,尽量使语言简练,表达清晰

设计模式有很多种,其中我们用得可能比较多的有适配器模式、原型模式、迭代器模式,当然还有MVC模式

设计模式

首先我们需要先了解一下设计模式

一般来说,一个设计模式有四个基本要素:

  1. 模式名称(pattern name)

  2. 问题(problem)

  3. 解决方案(solution)

  4. 效果(conswquences)

名称可以让我们更好交流,助于我们思考跟理解

一个设计模式往往需要解决一个什么样的问题

解决方案是设计的组成部分,因为设计模式并不是只应用单一场景,所以方案只是提供问题的抽象描述和用一个具有意义的元素来解决

效果主要是从空间和时间上来衡量

MVC

今天我们要讲的是MVC模式

让我们先来了解一下MVC模式吧

首先我们知道有很多框架都是使用MVC设计模式

比较出名的MVC框架有Java的Spring,可以说Spring出名到了每个Javaer都必须学的程度了,可见MVC的确不错,当然在众多php框架中,也有不过MVC设计模式的框架,例如ThinkPHP,zentaoPHP,都是一些学习成本比较低的框架,国外也有CodeIgniter和
Yii,这里只简单了解,有兴趣可以去官网学习一下,都是开源免费框架

MVC包括三类对象,分别是Model模型对象,View视图对象,以及Controller控制器对象

其中 Model 主要用来处理数据模型

View 是显示层,对数据进行显示表达

Controller 则是主要用来处理逻辑和响应用户输入

但是这样做,有什么好处呢?

这样的设计可以使用户界面的设计不必与其他对象混在一起,MVC将逻辑、模型、显示分离,来提高灵活性和复用性,MVC也还可以对付更一般的问题,将对象分离,使得对象之间可以互相影响,但是并不需要知道被影响对象的细节

初探MVC框架

这里我使用PHP语言描述,你也可以用其他语言试试,我们将一步一步慢慢制作出一个小型的MVC框架

首先我们创建一个文件夹,也就是我们的项目,根目录下定义一个index.php文件作为框架的单一入口

我们的框架目录如下:

|-----mvc
   |----- app          应用文件
      |------ Contorller       控制器文件
      |------ View             视图文件
      |------ Model             模型文件
   |----- includes        框架文件
      |------ Controller.php   控制器类
      |------ Core.php         框架核心类
      |------ Model.php        模型类
      |------ View.php         视图类
   |----- index.php    入口文件

首先,把index.php 入口文件完善

<?php 
header("Content-type:text/html; charset=utf-8");

define(APP_PATH, __DIR__ . '/');//定义常量

require(APP_PATH . 'includes/Core.php');//引入框架核心文件

(new Core())->start();  //启动框架

入口文件比较简单

声明了编码页面编码为UTF-8
定义一个APP_PATH常量指向当前文件目录
引入框架核心类
实例化核心类并调用start方法

当然,这里我们还没有实现Core核心类,没关系,我们先使用简单代替一下

<?php 
//框架核心类
class Core{
   public static function start(){
       echo '框架启动';
   }
}

Core核心类现在只要一个方法start,只是输出一句  框架启动

我们尝试访问一下入口文件

出现上面的画面,说明我们第一步已经成功了

接下来,我们要去完善Core核心类

首先我们要明确,核心类需要实现什么的功能,当然这并是一个简单的问题

我们不可能像天才一样一下子把所以事情想到,也没有从业十几年的工程师那样丰富的经验

所以我们只能是想到需要什么功能后再回到前面补充

这篇文章也是采用这样叙述顺序

因为我觉得一下子把我写好的全部类代码扔上来,你们可能会看不太懂或者不太明白其中的用意,完整得描述编码的整个过程,对于理解一个程序还是比较友好的

OK,那么有什么功能是我们现在可以想到的呢

1.我们不可能每次是一个类都去单独引入,所以我们需要一个自动加载类函数
2.既然我们有模型类,那我们就需要一定要连接数据库
3.我们要响应用户输入,所以我们还需要解析路由

我们将依次完善它们

自动加载类

实现自动加载类,我们需要用到一个函数 spl_autoload_register ,这个函数把我们实现的加载类方法注册到SPL__autoload函数队列

public static function start(){
       spl_autoload_register(array($this, 'autoload'));
   }

   public static function autoload($class){
       //框架文件
       $includes = __DIR__ . '/' . $class . '.php';
       //控制器文件
       $controller = APP_PATH . 'app/Controller/' . $class . '.php';
       //模型文件
       $model = APP_PATH . 'app/Model/' . $class . '.php';

       if (file_exists($includes)) {
           include $includes;

       } elseif (file_exists($controller)) {
           include $controller;

       } elseif (file_exists($model)) {
           include $model;

       } else {
           die('未找到 '. $class . '类');
       }

   }  

我们实现了一个autoload方法,并把它注册到SPL__autoload函数队列中,当我们实例化一个未定义的类时,系统将调用我们定义的autoload函数,并将类名做为参数传递

简单测试一下

public function testClass(){
   new TestClass();
}

在Core类中定义一个testClass方法,并在start中调用(在注册方法后面)

出现如下

设计模式MVC(一)

我们尝试在框架文件夹下创建这一个类文件TestClass.php , 代码如下

<?php 
class TestClass{
   public function __construct(){
       echo 'TestClass 类实例化';
   }
}

则出现

设计模式MVC(一)

说明我们的自动加载类功能完成了

数据库配置

OK,我们接着实现数据库方面的功能,因为数据库只在我们使用时才连接,所以这里我们只需要简单配置一下数据库连接参数

public function DBConfig($config){
   Model::$DBConfig = $config;
   Model::showConfig();   //用于测试
}

我们把数据库方面的参数传进$DBConfig方法中,并将配置属性赋给Model模型类的类变量$DBConfig

当然,Model模型类,我们也需要初步完善一下

<?php 
//模型类
class Model{
   public static $DBConfig;

   public  static function showConfig() {
       echo '<pre>'.print_r(Model::$DBConfig, true);
   }
}

Model类现在只有一个静态属性跟一个静态方法

OK,我们来测试一下

$config = array('host'=> 'localhost',
               'type' => 'mysql',
               'user' => 'root',
               'password' => '',
               'name' => 'test',
               );
$this->DBConfig($config);

同样是在start方法中

出现

设计模式MVC(一)

说明数据库参数我们也简单配置好了,当然,一般的做法是把数据库配置参数分离在一个文件中,便于修改,这里为了方便,我们就先这样

路由解析

接下来我们就要实现路由解析了

我们希望url是

域名:端口号/控制器/方法/参数

这样的形式,所以

public function route(){
       //默认控制器名
       $controller = 'index';
       //默认方法名
       $action = 'index';
       //url 参数
       $param = array();
       //获取url
       $url = $_SERVER['PATH_INFO'];
       //获取定位
       $position = strpos($url, '?');

       if($position) {
           $url = substr($url, 0, $position);
       }
       // 删除前后的“/”
       $url = trim($url, '/');

       if($url) {
           $urlArray = explode('/', $url);
           //控制器
           $controller = $urlArray[0];
           //方法
           $action = $urlArray[1];

       }

       $controller .= 'Controller';
       if(!class_exists($controller)) {
           die($controller.' 控制器不存在');
       }

       if(!method_exists($controller, $action)) {
           die($controller.' 控制器不存在 '.$action. ' 方法');
       }

       //实例化控制器
       $con = new $controller();
       //并调用方法
       $con->$action();
   }

这只是简单的路由解析,它实现从URL将控制器,方法提取出去,实例化控制器并调用方法

同样,简单测试一下

我们在应用文件夹app下的Controller新建一个文件IndexController.php

<?php 
class IndexController{
   public function __construct(){
       echo "Index控制器实例化<br>";
   }
   public function index(){
       echo "index 方法被调用<br>";
   }
}

接着调用

http://localhost:8080/mvc/index.php/Index/index

设计模式MVC(一)



可以看到IndexController被实例化并调用了index方法

写到这里,我们并还没有Controller控制器类,既然封装成类就是为了复用性,那么我们实现它并让所有的应用控制器继承他

我们在includes目录下新建一个Controller.php文件

<?php
class Controller{
   public function __construct(){
       echo 'Controller 基类被继承<br>';
   }
}

并让IndexController 继承他

<?php 
class IndexController extends Controller{
   public function index(){
       echo "index 方法被调用<br>";
   }
}

输入上面的URL

设计模式MVC(一)


说明我们的控制器基类已经被继承了

ok,现在我们要来完善Controller类

首先,我们可以想到的是控制器负责响应,那么他一定需要要渲染视图,分配变量等功能

那么我们也就需要一起实现视图的功能了

同样,在includes目录下新建一个View.php文件

//视图基类
class View{
   protected $_controller;
   protected $_action;
   protected $_var = array();

   //架构方法
   public function __construct($controller, $action){
       $this->_controller = $controller;
       $this->_action = $action;
   }

   //分配变量
   public function assign($key, $value) {
       $this->_var[$key] = $value;
   }

   //渲染
   public function render(){
       //将变量导入到当前的符号表
       extract($this->_var);

       $ViewFile = APP_PATH . 'app/View/' . $this->_controller .'/' . $this->_action . '.html';

       if(file_exists($ViewFile)){
           include $ViewFile;
       }

   }
}

extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量

我们的视图基类需要两个参数,分别是控制器跟方法,主要是用来寻找视图文件

使用一个数组来保存变量,并在渲染视图时,将变量导入当前的符号表

Controller类也需要完善一下了

// 控制器基类
class Controller{
   //视图
   protected $_view;

   //架构方法
   public function __construct($controller, $action){
       $this->_view = new View($controller, $action);
   }

   //显示视图
   public function show() {
       $this->_view->render();
   }
}

控制器需要渲染视图,所以需要一个变量保存视图,并在初始化时实例化视图对象

show方法通过调用视图对象的render方法来渲染视图

注意: 在route方法,需要添加两个参数

当然,测试一下

在app目录下的View文件夹中

新建一个Index文件夹,进入Index,新建一个index.html文件

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>index</title>
</head>
<body>
   <h1>这里是index文件</h1>
</body>
</html>

在inden方法中调用

public function index(){
       $this->show();
}

出现如下

说明,我们已经初步将控制,视图分离了

以后我们可以在控制器里面写我们逻辑,把视图跟逻辑分离也有助于代码更加清晰

写到这里,我们仅仅只是完成了一小部分工作,模型部分也还没有写,控制器跟视图也有很大的完善空间

但是由于篇幅过长,所以我决定暂时分成上下篇来写,在慢慢完善它

同样的

最后

本人水平有限,上面文章难免有疏漏,如果你愿意指出来,不胜感激




以上是关于设计模式MVC的主要内容,如果未能解决你的问题,请参考以下文章

在ASP.NET MVC的Action中直接接受客户端发送过来的HTML内容片段

Spring MVC 3.2 Thymeleaf Ajax 片段

mvc——URL路由_1_定义路由(映射url到动作方法)

有没有办法在使用 Asp.Net MVC ActionLink、RedirectToAction 等时包含片段标识符?

用于从 cloudkit 检索单列的代码模式/片段

是否有在单个活动中处理多个片段的 Android 设计模式?