# :-: 一、单例模式
* 一个类, 仅允许实例化一次,即仅允许创建一个实例
* 应用场景: 数据库的连接对象, HTTP请求, 游戏中的主角等
```php
class People{
}
$obj1 = new People();
$obj2 = new People();
var_dump($obj1);
echo '<br>';
var_dump($obj2);
// Temp 类被实例化了二次,创建了二个完全不同的对象
var_dump($obj1 === $obj2); // false, 说明这二个对象完全不相同
```
* 如何时才能确保一个类仅被实例化一次呢? 使用单例模式可以实现
```php
class Demo{
// 将构造方法私有化, 使用户在类的外部,无法通过"new"关键字来实例化该类
private function __construct(){
}
// 此时类外部无法通过new实例化, 所以类的实例化工作只能在类中完成了
// 声明一个静态属性,用来保存当前类的实例
private static $instance = null; // 如果不赋值,默认值就是null, 此处用null初始化仅仅是使代码更清晰
// 声明一个实例当前类的静态方法, 思考一下为什么必须设置静态的?(因为外部已无法实例化该类创建对象了)
public static function getInstance(){
// 如果当前类实例为null,说明当前类尚未被实例化过,那我们就就需要先将类实例化后,再返回调用者
if (is_null(self::$instance)) {
self::$instance = new self();
}
// 返回当前类实例
return self::$instance;
}
// 当获取到当前类实例之后, 外部是可以通过clone方法来快速复制该对象的, 所以类内部也应该将__clone()禁用
// 只需要将__clone()方法的访问限方式定为私有private即可
private function __clone(){
}
}
//$obj1 = new Demo(); // 报错, 因构造方法私有化, 外部已无法用new 来完成类的实例化
$obj1 = Demo::getInstance();
$obj2 = Demo::getInstance();
var_dump($obj1);
echo '<br>';
var_dump($obj2);
echo '<br>';
// 验证Demo类是否仅实例化了一次
var_dump($obj1 === $obj2); // true , 完全符合要求
echo '<hr>';
```
>[info] 数据库连接为例, 演示单例模式的应用
```php
class Db{
private static $pdo = null;
public static function getInstance(...$connectParams){
if (is_null(self::$pdo)) {
// 因为构造函数没有返回值, 所以实例当前类并赋值给静态属性的过程,只能在构造方法中完成
// self::$pdo = new self(...$connectParams);
new self(...$connectParams);
}
return self::$pdo;
}
// 当前构造方法是私有的, 仅表示在类外部不允许调用, 但是在类的内部仍然有效的
private function __construct(...$connectParams){
// 当前$connectParams 是一个索引数组,每一个元素对应着不同的连接参数
$dsn = $connectParams[0];
$username = $connectParams[1];
$password = $connectParams[2];
// 在私有的构造方法中完成类实例的创建过程
// 创建一个PDO类实例, 并赋值给当前类实例self::$pdo
self::$pdo = new \PDO($dsn, $username, $password);
}
// 私有化克隆方法
private function __clone(){
}
}
// 为简化代码, 使用剩余参数来传参
$connectParams = ['mysql:host=localhost;dbname=ouyangke', 'root', 'root'];
$pdo = Db::getInstance(...$connectParams);
// 做一个数据表查询来演示一下, 数据的格式化大家自己完成
print_r($pdo->query('select * from user')->fetchAll());
```
*****
# :-: 二、工厂模式
* 主要用于批量创建对象,使创建对象的过程剥离出来,标准化
* 适合于一个类有多个实例, 而这些实例会在不同的地方被创建和引用
* 使用工厂模式来创建对象, 可以实现, 一处修改, 全局生效
```php
namespace admin;
class Demo{
}
$obj = new Demo();
$obj = new Demo();
$obj = new Demo();
# 由于业务需要, Demo2类的类名需要改变,那么所有引用到这个类名的代码全部需要修改
```
>[info] Demo的实例化过程剥离出来,由一个专门的类去完成, 将会避免这种情况发生
```php
namespace admin;
class Test1{
public function __construct($arg1){
echo '对象创建成功, 参数是: ' . $arg1;
}
}
class Test2{
public function __construct($arg1, $arg2){
echo '对象创建成功, 参数是: ' . implode(', ', [$arg1, $arg2]);
}
}
class Test3{
public function __construct($arg1, $arg2, $arg3){
echo '对象创建成功, 参数是: ' . implode(', ', [$arg1, $arg2, $arg3]);
}
}
class Test4{
public function __construct(){
echo '对象创建成功, 无参数';
}
}
// 工厂类: 专用于创建类实例
class Factory{
/**
* @param [String] 需要实例化的类名称
* @param [Array] 实例化时需要传入的参数,使用剩余参数,可以自适应数量变化
* @return [Object] 类实例
*/
public static function create($className, ...$arguments){
// 剩余参数的展开规则: $arguuments 是数组,...可以将它展开
return new $className(...$arguments);
}
}
Factory::create(Test1::class, 100);
echo '<hr>';
Factory::create(Test2::class, 100, 200);
echo '<hr>';
Factory::create(Test3::class, 100, 200, 300);
echo '<hr>';
Factory::create(Test4::class);
```
*****
# :-: 三、MVC模式的原理与实现
:-: ![mvc](http://kanyun.8car.net/php/mvc.gif)
>[info] Model.php 文件
```php
//模型类: 用于数据库操作
class Model{
public function getData(){
return [
['uid'=>1, 'name'=>'欧阳克','phone'=>'18011112222','age'=>18],
['uid'=>2, 'name'=>'黄蓉','phone'=>'13011113333','age'=>16],
['uid'=>3, 'name'=>'郭靖','phone'=>'18722224444','age'=>22],
];
}
}
```
>[info] View.php 文件
```php
//视图类: 渲染数据
//为简化代码,这里忽略数据库操作,直接以二维数组模拟数据库查询的结果集
class View{
public function fetch($data){
$table = '<table border="1" cellspacing="0" width="400">';
$table .= '<caption>用户信息表</caption>';
$table .= '<tr bgcolor="lightblue"><th>ID</th><th>姓名</th><th>手机</th><th>年龄</th></tr>';
foreach ($data as $user) {
$table .= '<tr>';
$table .= '<td>' . $user['uid'] . '</td>';
$table .= '<td>' . $user['name'] . '</td>';
$table .= '<td>' . $user['phone'] . '</td>';
$table .= '<td>' . $user['age'] . '</td>';
$table .= '</tr>';
// Heredoc语法实现多行字符串,类似双引号功能
// $table .= <<< PRODUCT
// <tr>
// <td>{$user['uid']}</td>
// <td>{$user['name']}</td>
// <td>{$user['phone']}</td>
// <td>{$user['age']}</td>
// </tr>
// PRODUCT;
}
$table .= '</table>';
return $table;
}
}
```
>[info] Controller.php 文件
```php
/**
* mvc 思想
* 任务:将商品信息表展示出来
*/
// 加载'模型类'
require 'Model.php';
// 加载'视图类'
require 'View.php';
// 控制器
class Controller{
public function index(){
//获取数据
$model = new Model();
$data = $model->getData();
//渲染模板
$view = new View();
return $view->fetch($data);
}
}
//客户端调用
$controller = new Controller();
echo $controller->index();
```
* 存在的问题
1. Model类和View类的实例化,都在Controller类中完成,导致Controller类严重依赖Model类和View类;
2. Model类和View类的构造方式的变化,将会直接改变实例化的过程,导致Controller类不能独立于这二个类;
3. 即,Controller类,严重依赖Model类和View类,这种现象,就是我们常说的的: 代码之间的耦合度太高
4. 比较好的解决方案是,将Model和View类的实例化过程放在Controller类之外完成,而将他们的对象以参数方式传入到
* 解决方案
1. 依赖注入:对象可以像其它普通类型参数一样,进行传递
2. 依赖注入本意:将当前类依赖的其它类实例,以方法参数的形式,注入到当前类中,简称:"依赖注入"
>[info] 依赖注入:普通方法
>> Controller.php 文件
1. 将外部对象以参数形式注入到控制器的方法中
2. 调用控制器时,先将Model和View实例化,再调用控制器的方法
3. 将Model和View实例做为控制器方法中的参数传入
```php
// 加载'模型类'
require 'Model.php';
// 加载'视图类'
require 'View.php';
// 控制器
class Controller{
// 1. 将外部对象以参数形式注入到控制器的方法中;
public function index(Model $model, View $view){
//获取数据
$data = $model->getData();
//渲染模板
return $view->fetch($data);
}
}
// 2. 调用控制器时,先将Model和View实例化,再调用控制器的方法
$model = new Model();
$view = new View();
//客户端调用
$controller = new Controller();
//3. 将Model和View实例做为控制器方法中的参数传入
echo $controller->index($model, $view);
```
* 依赖注入可以很好的解决类之间的代码耦合
* 其实依赖注入时,对象即可以注入到控制器方法中,也可以注入到控制器构造方法中
* 直接注入到构造方法中,可以极大的简化代码,特别是在多个方法中都要用到这些外部对象时
>[info] 依赖注入:构造方法
>> Controller.php 文件
1. Controller中设置一个对象容器属性用来保存外部注入的对象
2. Controller类中创建构造方法,并将外部对象,做为构造方法的参数注入到类中
3. 修改调用方法index(),删除注入参数,修改调用语句
4. 调用控制器时,先将Model和View实例化,再调用控制器的方法
5. 客户端调用时,Controller类直接使用外部对象为参数进行实例化
```php
// 加载'模型类'
require 'Model.php';
// 加载'视图类'
require 'View.php';
// 控制器
class Controller {
// 1. Controller中设置一个对象容器属性用来保存外部注入的对象
protected $model = null;
protected $view = null;
//2. Controller类中创建构造方法,并将外部对象,做为构造方法的参数注入到类中
public function __construct(Model $model, View $view){
$this->model = $model;
$this->view = $view;
}
//3. 修改调用方法index(),删除注入参数,修改调用语句
public function index(){
//获取数据
$data = $this->model->getData();
//渲染模板
return $this->view->fetch($data);
}
}
// 4. 调用控制器时,先将Model和View实例化,再调用控制器的方法
$model = new Model();
$view = new View();
//5. 客户端调用时,Controller类直接使用外部对象为参数进行实例化
$controller = new Controller($model, $view);
echo $controller->index();
```
- 序言
- PHP基础
- 认识PHP
- 环境安装
- PHP语法
- 流程控制
- PHP数组
- PHP函数
- PHP类与对象
- PHP命名空间
- PHP7新特性
- PHP方法库
- PHP交互
- 前后端交互
- 项目常规开发流程
- MySQL数据库
- 会话控制
- Ajax分页技术
- 细说函数
- 类与对象
- 对象进阶
- 类与对象进阶
- OOP面向对象
- 设计模式
- 路由与模板引擎
- 异常类
- PHP爬虫
- PHP抓取函数
- PHP匹配函数
- 正则表达式
- PHP字符串函数
- 抓取实战
- PHP接口
- 了解接口
- PHP插件
- PHPSpreadsheet
- ThinkPHP6
- 安装
- 架构
- 数据库
- 数据库操作
- 视图
- 模版
- 模型
- 杂项
- 命令行
- 交互
- 微信小程序
- 介绍
- 配置
- 组件
- 交互
- API
- 其他知识
- 百度小程序
- 介绍
- 配置
- 组件
- 交互
- API
- 其他知识
- Linux
- 服务器上线流程
- 安装svn
- MySQL
- 认识MySQL
- MySQL函数
- 杂项
- composer依赖管理工具