# :-: 一、單例模式
* 一個類, 僅允許實例化一次,即僅允許創建一個實例
* 應用場景: 數據庫的連接對象, 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模式的原理與實現
:-: 
>[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依賴管理工具