博客列表 >PHP 24 MVC与设计模式(依赖注入-服务容器-Facade)(0804tue)

PHP 24 MVC与设计模式(依赖注入-服务容器-Facade)(0804tue)

老黑
老黑原创
2020年08月08日 15:24:35559浏览

主要内容:

  1. MVC的主要思想
    (model模型、view视图、controller控制 - 程序员主要是在折腾控制)

  2. 三种主要设计模式:
    a-“依赖注入”,通过class去构建Model、view,然后通过controller去控制
    b- 服务容器:将对外部的依赖放到容器中,让依赖更加简单、抽象。
    c- Facade门面技术:在服务容器和控制器之间生成一个静态过渡类,让后续的控制器访问进一步抽象。

    《盗梦空间》—— 不得不服老外在编程技术上的积累和精进。国内还有太多路要走。
    后面的层层嵌套、深入,类似《盗梦空间》,开始觉得还精巧,到后面把握不住火候的话,就感觉玩大了,兜不住了 —— 这个时候就需要不断地提升认知。
    每一层class的延伸,包括facade门面技术,都类似从一层梦境进入另外一层梦境。越来越抽象,越来越牵一发而动全身。


. 1. MVC的主要思想

  • M: Model, 模型, 数据库的操作
  • V: View, 视图, 页面, html
  • C: Controller, 控制器
  1. <?php
  2. // -----模型部分: 当前页面要显示的数据
  3. $pdo = new PDO('mysql:host=localhost;dbname=phpedu', 'root', 'root');
  4. $users = $pdo->query('select * from users limit 10')->fetchAll(PDO::FETCH_ASSOC);
  5. ?>
  6. <!-- 视图部分 -->
  7. <!DOCTYPE html>
  8. <html lang="en">
  9. <head>
  10. <meta charset="UTF-8">
  11. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  12. <title>用户列表</title>
  13. </head>
  14. <body>
  15. <table border="1" width="60%">
  16. <caption>用户表</caption>
  17. <tr>
  18. <th>id</th>
  19. <th>name</th>
  20. <th>email</th>
  21. </tr>
  22. <?php foreach ($users as $user): ?>
  23. <tr>
  24. <td><?=$user['id']?></td>
  25. <td><?=$user['name']?></td>
  26. <td><?=$user['email']?></td>
  27. </tr>
  28. <?php endforeach ?>
  29. </table>
  30. </body>
  31. </html>
  • 不同的项目切入点不同。

    • 仿站: V - M - C(模仿别人的网站,自然是从view开始)
    • 自主: M - V - C(但自主研发,则是从数据开始)
  • 几种设计模式
    ① 依赖注入
    ② 服务容器
    ③ Facade


2. “依赖注入”,通过class去构建Model、view,然后通过controller去控制

  • 第一步,先建立model和view文件,里面包含了model和view的class
    • Model.php
  1. <?php
  2. namespace mvc_demo;
  3. use PDO;
  4. // 模型类: 数据库操作
  5. class Model
  6. {
  7. // 获取数据
  8. public function getData()
  9. {
  10. return (new PDO('mysql:host=localhost;dbname=phpedu', 'root', 'root'))
  11. ->query('select * from users limit 10')->fetchAll(PDO::FETCH_ASSOC);
  12. // 通过链式方式得到数据
  13. }
  14. }
  15. // 测试模型
  16. // print_r((new Model)->getData());
  • View.php文件
  1. <?php
  2. namespace mvc_demo;
  3. // 视图: 数据展示
  4. class View
  5. {
  6. // 数据展示
  7. public function fetch($data)
  8. {
  9. // 表格方式展示,使用字符串拼接实现table的html代码
  10. // $table .= 相当于 $table = $table. + ...用来拼接语句)
  11. $table = '<table>';
  12. $table .= '<caption>用户信息表</caption>';
  13. $table .= '<tr><th>ID</th><th>用户名</th><th>邮箱</th></tr>';
  14. // 遍历用户表
  15. foreach ($data as $user){
  16. $table .= '<tr>';
  17. $table .= '<td>'.$user['id'].'</td>';
  18. $table .= '<td>'.$user['name'].'</td>';
  19. $table .= '<td>'.$user['email'].'</td>';
  20. $table .= '</tr>';
  21. }
  22. $table .= '</table>';
  23. //这块的table跟之前有一些区别,去掉了php等标识,可以了解下。
  24. return $table;
  25. }
  26. }
  27. echo '<style>
  28. table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
  29. caption {font-size: 1.2rem; margin-bottom: 10px;}
  30. tr:first-of-type { background-color:yellow;}
  31. td,th {border: 1px solid; padding:5px}
  32. </style>';
  33. // 视图测试
  34. // require 'Model.php';
  35. // echo (new View)->fetch((new Model)->getData());

3. 第二步,通过控制器来加载MV

3-1. 第一种,直接类内进行model、view的实例化

    1. Model, View 类的实例都是在Controller内完成的。会带来“代码耦合度过高”的问题
    1. 控制器知道的太多了, 造成了当前的控制器严重依赖外部的某些对象
    1. 这种现象就是大家常常听说过的: 代码的耦合度过高
  • 需要使用外部对象的 “依赖注入”解决”代码的耦合度过高的问题”
  1. <?php
  2. // 控制器1
  3. namespace mvc_demo;
  4. // 加载模型类
  5. require 'Model.php';
  6. // 加载视图类
  7. require 'View.php';
  8. class Controller1
  9. {
  10. // 获取数据,并展示出来
  11. public function index()
  12. {
  13. // 1. 获取数据
  14. $model = new Model();
  15. $data = $model->getData();
  16. // 2. 渲染模板
  17. $view = new View();
  18. return $view->fetch($data);
  19. }
  20. }
  21. // 客户端调用(测试)
  22. // 创建控制器实例/对象
  23. $controller = new Controller1();
  24. echo $controller->index();
  25. //

3-2. 第二种:外部实例化Model、View,然后以参数形式注入到Controller中

  • 将类/对象依赖的外部的对象,以控制器方法的参数形式注入到当前的控制器中
  • 此时, 控制器依赖的外部对象的实例化过程,就可以在控制器类的外部完成,一般在客户端完成
  • 但controller这个类中不同的方法要去调用m、v的时候需要反复以方法传参的方式来进行。这边不够好。
  1. <?php
  2. // 控制器2: 使用依赖注入解决类/对象之间的耦合度过高的问题
  3. namespace mvc_demo;
  4. // 加载模型类
  5. require 'Model.php';
  6. // 加载视图类
  7. require 'View.php';
  8. class Controller2
  9. {
  10. // 获取数据,并展示出来
  11. public function index($model, $view)
  12. {
  13. // 1. 获取数据
  14. $data = $model->getData();
  15. // 2. 渲染模板(也就是数据展示)
  16. return $view->fetch($data);
  17. }
  18. /* 问题又来了: 控制器依赖的外部对象,尽管已经通过依赖注入进行了当前类中,
  19. 但是没有办法实现外部对象的复用。例如下面调用的时候又需要重新new?*/
  20. public function edit($model)
  21. {
  22. }
  23. }
  24. // 客户端调用(测试)
  25. // 创建控制器实例/对象
  26. $model = new Model();
  27. $view = new View();
  28. $controller = new Controller2();
  29. echo $controller->index($model, $view);
  30. $controller->edit($model);

3-3. 通过构造方法将外部对象初始化,实现了外部对象在当前类的共享/复用

  • 这种直接相当于将m、v通过类构造传参的方式让类内所有的方法都可以使用了,相对就方便了很多。
  • 可以看出,从controller1到controller3的过程是逐渐抽象化、提升使用方便度的过程。
  1. <?php
  2. // 控制器3: 实现外部依赖注入的对象在类内部的共享/复用
  3. namespace mvc_demo;
  4. // 加载模型类
  5. require 'Model.php';
  6. // 加载视图类
  7. require 'View.php';
  8. class Controller3
  9. {
  10. // 外部依赖的对象
  11. private $model = null;//后面的null也可以不写
  12. private $view = null;
  13. // 通过构造方法将外部对象初始化,实现了外部对象在当前类的共享/复用
  14. public function __construct($model, $view)
  15. {
  16. $this->model = $model;
  17. $this->view = $view;
  18. }
  19. // 获取数据,并展示出来
  20. public function index()
  21. {
  22. // 1. 获取数据
  23. $data = $this->model->getData();
  24. // 2. 渲染模板
  25. return $this->view->fetch($data);
  26. }
  27. // 外部对象的复用
  28. public function edit()
  29. {
  30. // $this->model;
  31. }
  32. }
  33. // 客户端调用(测试)
  34. // 创建控制器实例/对象
  35. $model = new Model();
  36. $view = new View();
  37. // 将外部对象的注入点从普通方法,改到了构造方法中
  38. $controller = new Controller3($model, $view);
  39. echo $controller->index();//之前的参数是方法级别的,现在则提升到了类级别。
  40. $controller->edit();
  41. // o:这种情况下class内的每个方法就要相对简单,因为无法在括号中传参。
  42. // 当然也有可能方法本身就应该相对简单,一个方法一个非常独立的返回。当然这默认所有的类中的参数方法都是可以使用的了。

3-4. 通过服务容器来接管对外部对象的依赖

  • 上面的3是依赖外部对象,但如果外部对象很多,例如m、v等,这种情况下可以用容器,对象容器或服务容器,来进行接管。
  1. <?php
  2. // 控制器4: 将当前依赖的外部对象,放到一个"服务容器"中进行统一管理
  3. namespace mvc_demo;
  4. use Closure; // 后面使用闭包类型的时候前面就不用加/了。
  5. // 加载模型类
  6. require 'Model.php';
  7. // 加载视图类
  8. require 'View.php';
  9. // -----------------------------------------
  10. // 服务容器
  11. class Container1
  12. {
  13. // 1. 对象容器
  14. protected $instances = [];
  15. // 2. 向对象容器中添加对象
  16. // 参数1: 是外部对象在当前对象容器数组中的键名/别名
  17. // 参数2: 是当前需要绑定到容器的对象的实例化过程(函数)
  18. public function bind($alias, Closure $process)
  19. //第二个参数是一个函数,使用闭包类型
  20. {
  21. $this->instances[$alias] = $process;
  22. }
  23. // 3. 从对象容器中取出对象, 调用它
  24. public function make($alias, $params=[] ) {
  25. return call_user_func_array($this->instances[$alias], []);
  26. }
  27. }
  28. // 将外部对象: Model, View的实例绑定到服务容器中
  29. $container = new Container1;
  30. // 绑定模型类实例绑定到服务容器中
  31. $container->bind('model', function(){
  32. return new Model();
  33. });
  34. // 绑定视图类实例绑定到服务容器中
  35. $container->bind('view', function(){
  36. return new View();
  37. });
  38. // ------------------------------------------
  39. class Controller4
  40. {
  41. // 获取数据,并展示出来
  42. public function index(Container1 $container)
  43. {
  44. // 1. 获取数据
  45. $data = $container->make('model')->getData();
  46. // 2. 渲染模板
  47. return $container->make('view')->fetch($data);
  48. }
  49. }
  50. // 客户端调用(测试)
  51. $controller = new Controller4();
  52. echo $controller->index($container);
  53. // 为什么要用服务容器?
  54. // 将当前类对许多外部对象的依赖, 转为对一个服务容器的依赖
  55. // 将外部对象的依赖,使用服务容器进行了接管
  56. // 这样下来后面的地方就简化了很多。

3-5. Facade门面技术: 将对服务容器中的对象的访问进行静态接管

  1. <?php
  2. // 控制器5: Facade门面技术, 静态接管服务容器中的成员的访问
  3. namespace mvc_demo;
  4. use Closure;
  5. require 'Model.php';
  6. require 'View.php';
  7. // -----------------------------------------
  8. // 服务容器
  9. class Container2
  10. {
  11. // 1. 对象容器
  12. protected $instances = [];
  13. public function bind($alias, Closure $process)
  14. {
  15. $this->instances[$alias] = $process;
  16. }
  17. // 3. 从对象容器中取出对象, 调用它
  18. public function make($alias, $params=[] ) {
  19. return call_user_func_array($this->instances[$alias], []);
  20. }
  21. }
  22. // 将外部对象: Model, View的实例绑定到服务容器中
  23. $container = new Container2;
  24. // 绑定模型类实例绑定到服务容器中
  25. $container->bind('model', function(){
  26. return new Model();
  27. });
  28. // 绑定视图类实例绑定到服务容器中
  29. $container->bind('view', function(){
  30. return new View();
  31. });
  32. // ------------------------------------------
  33. // 在服务容器与工作的控制器之间再添加一个中间层: Facade
  34. class Facade
  35. {
  36. // 服务容器
  37. protected static $container = null;
  38. // 初始化方法: 就是给当前的Facade类中的$container属性赋值
  39. // 理解为Facade的构造方法(但不是)
  40. // 将外部的服务容器注入到当前的facade中
  41. public static function initialize(Container2 $container)
  42. {
  43. static::$container = $container;
  44. // 给本类中的静态属性赋值,其实就是将前面的容器初始化。
  45. }
  46. }
  47. // 模型类成员访问静态化(给成员套一个静态访问的马甲)
  48. class UserModel extends Facade
  49. {
  50. public static function getData()
  51. {
  52. return static::$container->make('model')->getData();
  53. }
  54. }
  55. // 视图类成员访问静态化(给成员套一个静态访问的马甲)
  56. class UserView extends Facade
  57. {
  58. public static function fetch($data)
  59. {
  60. return static::$container->make('view')->fetch($data);
  61. }
  62. }
  63. // ------------------------------------------
  64. class Controller5
  65. {
  66. // 构造方法,初始化facade
  67. public function __construct(Container2 $container)
  68. {
  69. Facade::initialize($container);
  70. }
  71. // 用Facade方式类成员
  72. public function index()
  73. {
  74. // 1. 获取数据
  75. $data = UserModel::getData();
  76. // 2. 渲染模板
  77. return UserView::fetch($data);
  78. }
  79. }
  80. // 客户端调用(测试)
  81. $controller = new Controller5($container);
  82. echo $controller->index();

4. 作业实战

  • 按照老师的方法自己走了一边,并且用Facade的方法实现了。
  • 但对中间各种class(model、view、container、Facade、controller(o - 能否归纳为 MV-CF-C模式,中间的C-F分别为container和Facade))之间的嵌套关系还是比较懵。估计要后面不断看代码才行。
  • 自己的z1model.php,其中gettdata多加t是为了试验是否可以乱改名字等。
  1. <?php
  2. namespace mvc;
  3. use PDO;
  4. class dataModel{
  5. public function gettData(){
  6. return (new PDO('mysql:host=localhost;dbname=liangtest','liang','123456'))
  7. ->query('SELECT * FROM `shao` LIMIT 8')->fetchAll(PDO::FETCH_ASSOC);
  8. }
  9. }
  10. print_r((new dataModel)->gettData());
  11. /*
  12. 8月4日作业
  13. 1. 将课堂视频至少看二遍以上,细细品服务容器与facade带来的好处,并写出自己的总结
  14. 2. 试着用mvc实现一个简单的业务逻辑(可仿课堂案例,但不要全照抄)
  15. 3. 可选: 如何优化Facade的调用过程
  16. */
  • z2view.php部分
  1. <?php
  2. namespace mvc;
  3. class shaoView{
  4. public function fetch($items){
  5. $table = '<table>';
  6. $table .= '<caption>内容列表</caption>';
  7. $table .= '<tr>
  8. <th>id</th>
  9. <th>date</th>
  10. <th>title</th>
  11. <th>tags</th>
  12. </tr>';
  13. foreach ($items as $item){
  14. $table .= '<tr>';
  15. $table .= '<td>'.$item['id'].'</td>';
  16. $table .= '<td>'.$item['date'].'</td>';
  17. $table .= '<td>'.$item['title'].'</td>';
  18. $table .= '<td>'.$item['label'].'</td>';
  19. $table .= '</tr>';
  20. }
  21. $table .= '/<table>';
  22. return $table;
  23. }
  24. }
  25. echo '<style>
  26. table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
  27. caption {font-size: 1.2rem; margin-bottom: 10px;}
  28. tr:first-of-type { background-color:yellow;}
  29. td,th {border: 1px solid; padding:5px}
  30. </style>';
  31. require 'z1model.php';
  32. echo (new shaoView)->fetch((new dataModel)->gettData());

  • z3controller部分
  1. <?php
  2. namespace mvc;
  3. use Closure;
  4. require 'z2view.php';
  5. class objContainer{
  6. protected $insts = [];
  7. public function bind($name, Closure $process){
  8. $this->insts[$name] = $process;
  9. }
  10. public function make($name,$params=[]){
  11. return call_user_func_array($this->insts[$name],[]);
  12. }
  13. }
  14. $container = new objContainer;
  15. $container->bind('dataModel',function(){
  16. return new dataModel();
  17. });
  18. $container->bind('shaoView',function(){
  19. return new shaoView();
  20. });
  21. class Facade{
  22. protected static $container =null;
  23. public static function initialize(objContainer $container){
  24. static::$container = $container;
  25. }
  26. }
  27. class dataamodel extends Facade{
  28. public static function gettData(){
  29. return static::$container->make('dataModel')->gettData();
  30. }
  31. }
  32. class shaooView extends Facade{
  33. public static function fetch($data){
  34. return static::$container->make('shaoView')->fetch($data);
  35. }
  36. }
  37. class Controller{
  38. public function __construct(objContainer $container){
  39. Facade::initialize($container);
  40. }
  41. public function index(){
  42. $data = dataamodel::gettData();
  43. return shaooView::fetch($data);
  44. }
  45. }
  46. $controller = new Controller($container);
  47. echo $controller->index();

最终比较幸运,数据都能读取出来。

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议