Php 在另一个类中使用一个类时的OOP效率

Php 在另一个类中使用一个类时的OOP效率,php,class,oop,pdo,Php,Class,Oop,Pdo,我有一个名为DB(class.pdo.php)的类,它使用pdo对mysql查询进行所有处理,还有一个名为user的类,我用来管理登录系统 我的问题涉及到总是必须在用户的每个公共函数中实例化$db,这样我才能使用db。这有效吗?我不应该在用户的_construct()中实例化DB吗 这是我的密码 require_once("../../class.pdo.php"); class user { private $db = null; public function __construct

我有一个名为DB(class.pdo.php)的类,它使用pdo对mysql查询进行所有处理,还有一个名为user的类,我用来管理登录系统

我的问题涉及到总是必须在用户的每个公共函数中实例化$db,这样我才能使用db。这有效吗?我不应该在用户的_construct()中实例化DB吗

这是我的密码

 require_once("../../class.pdo.php");

class user {

private $db = null;

public function __construct(){
    /* Empty? */
}

public function find_by_email($email){
    $db = new db();
    $db->query('SELECT * FROM users WHERE email = :email LIMIT 1');
    $db->bind(':email',$email);
    $result = $db->single();
    return $result;
}

public function create($email,$password,$first_name,$last_name){ 
    $db = new db();
    $db->query("INSERT INTO users(email,password,first_name,last_name,created_at) VALUES (:email,:password,:first_name,:last_name,NOW())");
    $db->bind(':email',$email);
    $db->bind(':password',$password);
    $db->bind(':first_name',$first_name);
    $db->bind(':last_name',$last_name);
    $result = $db->execute();
    return $db->lastInsertId();
}

 [more similar functions ommited]

从对象的角度来看,我会让数据库在方法中实例化,而不是整个类

每个方法应该只看到它需要的变量和数据,以便执行它的功能。 例如,
createUser()
方法需要查看变量或属性,如
$username
$usergroupId
,以及
$database

但是,您可能有一个名为
randomPassword()
的函数,它根据数字和字母生成随机密码

此randomPassword()函数不需要数据库对象,因此,对象范围内已初始化的数据库连接将是浪费

最好只在需要的方法中创建新的数据库对象

此外,在我的应用程序中,我不会在每次使用
新数据库时创建新数据库连接。相反,我选择了一个保持连接活动的单例PDO数据库对象


然后,我可以静态调用数据库对象来检索现有连接。因此,如果在运行应用程序的过程中需要20个数据库对象,那么应用程序只返回相同的对象和相同的连接

使用Singleton模式为连接创建一个对象,并在每次需要时使用它怎么样,不要总是创建新对象?

我会对延迟加载做类似的事情:不要在构造函数中启动,除非您确定每次创建对象时都需要连接,但绝对不要在每次方法调用时创建新对象。相反,将生成的对象保存到一个对象变量中,该变量在每次方法调用时都会被检查,如果丢失,则会启动连接

class user {
  protected $_db = null;
  private function _init_db() { $this->_db = new XXX; }
  public function create( $x, $y, $z ) {
    if ( ! $this->_db ) $this->_init_db();
    # use $this->_db ..
  }
  public function find_by_email( $x, $y, $z ) {
    if ( ! $this->_db ) $this->_init_db();
    # etc
  }
}

这样做的好处是避免全局静态(Singleton…),并且只在最后一刻创建连接/对象,因此您可以确定您确实需要它,而且它不仅仅是一个无用的连接。

尽管有一些评论建议使用Singleton模式,但我完全不同意将其用于此目的

您的应用程序并不总是只使用一个数据库连接

让我向您展示我是如何做到这一点的:

class DbConnector {
    private $dbh;
    private $dsn;

    public function __construct($dsn) {
        $this->dsn = $dsn;
    }

    private function connect() {
        if($this->dbh === null) {
            $this->dbh = new PDO($this->dsn);
        }
    }

    public function disconnect {
        if($this->dbh !== null) {
            $this->dbh = null;
        }
    }

    public function query($sql) {
        $this->connect();
        //... do the rest
    }

    public function fetchAll($sql) {
        $this->connect();
        //... do the rest
    }

    public function insert($table, $values) {
        $this->connect();
        //... do the rest
    }

    public function update($table, $values, $cond) {
        $this->connect();
        //... do the rest
    }

    public function delete($table, $cond) {
        $this->connect();
        //... do the rest
    }
}

class User {
    private $dbConn;
    public function __construct(DbConnector $dbConn) {
        $this->dbConn = $dbConn;
    }

    public function create($email,$password,$first_name,$last_name){ 
        $this->dbConn->query("INSERT INTO users(email,password,first_name,last_name,created_at VALUES (:email,:password,:first_name,:last_name,NOW())");
        $this->dbConn->bind(':email',$email);
        $this->dbConn->bind(':password',$email);
        $this->dbConn->bind(':first_name',$email);
        $this->dbConn->bind(':last_name',$email);
        $this->dbConn->execute();
        return $this->dbConn->lastInsertId();
    }

    // ...
}
结果:

  • 未使用单例=可测试
  • 需要时,只需打开与数据库的连接
  • 您的连接是持久的。如果在每个方法中打开和关闭连接,则会失去创建事务的能力

说到效率,代码的主要问题是它为调用的每个方法建立了新的连接。这一个确实效率低下,甚至会杀死您的数据库服务器。这是你遇到的另一个问题所无法比拟的

因此,一般来说,您可以采用任何方式—在每个函数中以某种方式获取db类的实例,或使用类变量—但任何一种方式都必须在整个应用程序中使用单个PDO实例

另外,从代码量的角度来看,我发现您的函数效率很低,并且会以这种方式对它们进行优化

public function create($email,$password,$first_name,$last_name){ 
    $sql = "INSERT INTO users(email,password,first_name,last_name,created_at) VALUES (?,?,?,?,NOW())";
    $this->db->query($sql);
    $result = $db->execute(func_get_args());
    return $db->lastInsertId();
}

你如何定义“有效”一词?如何衡量“效率”?实例化一次,然后使用依赖注入将其传递给所有需要它的类。您的类和PDO之间有什么区别?很可能效率低下。从软件工程的角度来看,这也不好:您的类(其方法)依赖于
db
的存在,但不要在任何地方宣传这一点;只有当您阅读方法实现时,它才可见。@Afonso Gomes:服务器提供和使用了几十种不同的资源。而且您无法优化所有这些功能的使用。每次对资源A的优化都会消耗一些资源B。我不同意为每个方法创建一个连接,因为这样一来,数据库中的信息就不会持久化。连接器应在创建之前创建并传递给对象。每个方法都可以通过调用DB类中的
connect
之类的方法来负责,因为创建DB对象时不需要立即连接。这就是所谓的惰性初始化。我同意@henriques Barcelos,我确实添加了一点,即我个人有一个单例PDO,我只是将它导入到每个需要它的方法中,而不是为每个方法创建一个新对象:)所以。。。你是说我在正确的轨道上?我在类dbSingleton的u构造中尝试了{$this->dbh=new PDO($dsn,$this->user,$this->pass,$options);}。如何使用单例对这些类进行单元测试?单例可能会导致单元测试出现问题,但是,在每次应用程序运行时打开10个连接,或者在适当的情况下打开1个可重用的连接,效率更高吗?为什么不合并父工厂模式以在正常请求时返回singleton对象,但在需要时返回新连接呢?singleton与其说是模式,不如说是反模式。数据库连接器不应使用singleton,因为并非总是只将一个连接连接到一个数据库。@HenriqueBarcelos是什么阻止您使用默认连接的singleton,但在提供不同凭据时创建另一个实例?如果不注入singleton,则无法更改它。如果只执行以下操作:
public function someMethod(){DbConnector::getInstance()->query(…);}
您将永远无法更改连接器。@HenriqueBarcelos