设计模式MVC
Posted 星点学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式MVC相关的知识,希望对你有一定的参考价值。
今天我们来学习一下设计模式吧
这次我吸取了上两次的经验,尽量使语言简练,表达清晰
设计模式有很多种,其中我们用得可能比较多的有适配器模式、原型模式、迭代器模式,当然还有MVC模式
设计模式
首先我们需要先了解一下设计模式
一般来说,一个设计模式有四个基本要素:
模式名称(pattern name)
问题(problem)
解决方案(solution)
效果(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中调用(在注册方法后面)
出现如下
我们尝试在框架文件夹下创建这一个类文件TestClass.php , 代码如下
<?php
class TestClass{
public function __construct(){
echo 'TestClass 类实例化';
}
}
则出现
说明我们的自动加载类功能完成了
数据库配置
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方法中
出现
说明数据库参数我们也简单配置好了,当然,一般的做法是把数据库配置参数分离在一个文件中,便于修改,这里为了方便,我们就先这样
路由解析
接下来我们就要实现路由解析了
我们希望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
可以看到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
说明我们的控制器基类已经被继承了
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 片段