PHP设计模式-框架底层各种方法的实现

框架用起来固然方便,帮你封装好了各种方法,只负责按照给出的示例调用即可,这样其实不利于对一个语言的了解。这几天在 B 站看到了一个合集的视频,讲述 PHP 的各种设计模式,以及在 MVC 模型中的实现,感觉似曾相识但又从没动手写过这些设计思想。看完才发现,由面向对象扩展出来的还有这么多种设计模式。这里做一个学习记录。

三种基本设计模式

  1. 工厂模式:工程方法或类生成对象,而不是在代码中直接 new
  2. 单例模式:使某个类的对象只允许被创建一次
  3. 注册模式:全局共享和交换对象

工厂模式

普通使用

$db = new \Work\Database();

工厂模式

// DBFactory.php
namespace Work;

interface DB{
    public funtion connect();
}

// 产品1
class _mysqli implements DB{
    public funtion connect(){
        echo 'mysqli';
    }
}
// 产品2
class _pdo implements DB{
    public funtion connect(){
        echo 'pdo';
    }
}

class DBFactory {
    static public function Factory($class_name){
        return new $class_name;
    }
    
    // 一般是下面这种写法
    static public function Factory($class_name){
        switch($class_name){
            case '_pdo':
                return new _pdo();
                break;
            case 'mysqli':
                return new _mysqli();
                break;
        }
    }
}
$db = \Work\DBFactory::Factory('_pdo');
$db->connect();

好处在于如果$db = new Database();在很多地方被new,如果Database()名称或请求参数被改变,那么很多地方都要做出改变。有了工厂模式,只需要在工厂里面进行改变。

根据参数返回不同的产品

比如:项目中多处实例化这个类,后面发现类名不合适或要添加构造函数参数,那就要到处改了

使用工厂模式,用参数获取不同的类,只需要改工厂里面的类,调用方不用改

单例模式

  • 避免到处被new而消耗资源,常用于数据库操作
  • 用单例全局控制某些配置信息

确保 一个类只有一个实例 ,并且对外部提供这个全局实例访问入口

  1. 一个类只有一个对象
  2. 私有化构造方法:防止被new
  3. 私有化克隆方法:方式被 clone
  4. 构造实例,new自己
// Database.php
namespace Work;

class Database {
    // 静态变量私有化
    private static $db;
    
    // 私有构造方法防止被 new
    private function __construct() {
    }
    
    // 私有克隆方法,防止被克隆
    private function __clone(){
    }
    
    // 静态方法,用来被实例化,对外的接口
    public static function getInstance(){
        if(self::$db){
            return self::$db;
        } else {
            self::$db = new self();
            return self::$db;
        }
    }
}

实现了单例模式

无论调用多少次,创建数据库操作只会new()一次。

// index.php
$db = Work\Database::getInstance();
$db = Work\Database::getInstance();
$db = Work\Database::getInstance();
$db = Work\Database::getInstance();

工厂内部也要改:

namespace Work;

class Factory {
    static function createDatabase(){
        $db = Database::getInstance();
        return $db;
    }
}

注册树模式

上述两种方法都有缺点,都需要调用工厂或类。

把对象注册到注册树上,那么在任何一个地方都能调用。

// Register.php
namespace Work;

class Register {
    protected static $objects;
    
    static function set($alias, $object){
        self::$objects[$alias] = $object;
    }
    
    function _unset($alias){
        unset(self::$objects[$alias]);
    }
}

在工厂里面把对象映射上去:(只在这构造一次)

// Factory.php
namespace Work;
class Factory {
    static function createDatabase(){
        $db = Database::getInstance();
        Register::set('db1', $db);
        return $db;
    }
}

使用的时候,不需要调用工厂或单例,直接在注册器上拿;

$db = \Work\Register::get('db1');

适配器模式

  1. 将截然不同的函数借口封装成统一的 API
  2. 比如:mysql、mysqli、Pdo 三种可以用适配器统一改成一致。还有 Cache 适配器,比如 Memcahe、Redis、File、apc 缓存函数统一成一致。

声明一个接口,约定统一 API 方式

// Wrok/Database.php
namespace Work;

interface IDatabase{
    function connect($host, $user, $password, $dbname);
    function query($sql);
    function close();
}

分别创建三种适配器的实例,分别实现接口内的方法:

<?php
// Work/Database/MySQL.php
namespace Work\Database;
use Work\IDatabase;

class MySQL implements IDatabase {
    protected $conn;
    
    function connect($host, $user, $password, $dbname){
        $conn = mysql_connect($host, $user, $password);
        mysql_select_db($dbname, $conn);
        $this->conn = $conn;
    }
    function query($sql){
        $res = mysql_query($sql, $this->conn);
        return $res;
    }
    function close(){
        mysql_close($this->conn);
    }
}

实现 MySQLi:

<?php
// Work/Database/MySQLi.php
namespace Work\Database;

use Work\IDatabase;
class MySQLi implements IDatabase{
    protected $conn;
    
    function connect($host, $user, $password, $dbname){
        $conn = mysqli_connect($host, $user, $password, $dbname);
        $this->conn = $conn;
    }
    function query($sql){
        return mysqli_query($this->conn, $sql);
    }
    function close(){
        mysqli_close($this->conn);
    }
}

实现 PDO:

<?php
// Work/Database/PDO.php
namespace Work\Database;
use Work\IDatabase;

class PDO implements IDatabase{
    protected $conn;
    
    function connect($host, $user, $password, $dbname){
        $conn = mysqli_connect($host, $user, $password, $dbname);
        $this->conn = $conn;
    }
    function query($sql){
        return mysqli_query($this->conn, $sql);
    }
    function close(){
        mysqli_close($this->conn);
    }
}

实现:三种模式直接切换

$db = new \Work\Database\MySQL();
// $db = new \Work\Database\PDO();
// $db = new \Work\Database\MySQLi();
$db->connect('127.0.0.1', 'root', 'xxx', 'test');
$db->query('show database');
$db->close();

策略模式

  1. 将一组特定行为和算法封装成类,以适应某些特定的上下文环境
  2. 例如:电商网站,正对男性女性用户要各自跳转到不同的商品类目,并且所有广告位展示不同的广告

实现:https://www.bilibili.com/video/av19567303/?p=17

观察者模式

一个对象发生改变时,依赖它的对象全部会收到通知并自动更新。实现了低耦合,非侵入式的通知与更新机制。

实现:https://www.bilibili.com/video/av19567303/?p=21

原型模式

和工厂模式类似,都用来创建对象。不同在于,原型模式先创建好一个原型对象,再通过 CLone 原型对象来创建新的对象。免去了创建对象时的初始化操作。适用于大对象的创建(大对象每 new 一次会消耗很大,只需要内存拷贝)。

实现:https://www.bilibili.com/video/av19567303/?p=22

装饰器模式

动态添加类的功能。传统方法是写一个子类继承它,并重实现类。装饰器的话只需要再运行的时候添加一个装饰器对象即可实现。

实现:https://www.bilibili.com/video/av19567303/?p=23

迭代器模式

不需要了解内部实现的前提下,遍历一个聚合对象的内部元素。相对于传统方法,迭代器可以隐藏遍历元素所需的操作。

实现:https://www.bilibili.com/video/av19567303/?p=24

代理模式

客户端 和实体之间建立一个代理对象proxy,客户端对实体进行操作会委派给代理对象,隐藏实体的具体实现细节。

还可以与业务代码分离,部署到另外的服务器中。

https://www.bilibili.com/video/av19567303/?p=25

面向对象设计基本准则
  1. 单一职责:一个类只需要做好一件事情
  2. 开放封闭:一个类应该是可以扩展的,而不可修改的
  3. 依赖倒置:一个类,不应该依赖另一个类。每个类对另外一个类是可替代的。
  4. 配置化:尽可能使用配置,而不是硬编码
  5. 面向接口编程:只需要关心接口,不需要面向实现

MVC

模型(Model):数据和存储的封装

视图(view):展现层的封装,比如 Web 系统中的模板文件

控制器(Controller):逻辑层的封装,业务代码

各种设计模式在 MVC 里面的使用

https://www.bilibili.com/video/av19567303/?p=30

本文链接:https://ariser.cn/index.php/archives/347/
本站文章采用 知识共享署名4.0 国际许可协议进行许可,请在转载时注明出处及本声明!