解决此问题的正确方法是将数据库对象注入到另一个类中(依赖注入):
$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");
$pagination = new Paginator($db);
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`");
class Paginator
{
protected $db;
// Might be better to use some generic db interface as typehint when available
public function __construct(DB_MySQL $db)
{
$this->db = $db;
}
public function get_records($q) {
$x = $this->db->query($q);
return $this->db->fetch($x);
}
}
解决它的另一种方法是将数据库类的实例注入到使用它的方法中:
$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");
$pagination = new Paginator();
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`", $db);
class Paginator
{
public function get_records($q, DB_MySQL $db) {
$x = $db->query($q);
return $db->fetch($x);
}
}
无论选择哪种方法都取决于具体情况。如果只有一个方法需要数据库的实例,你可以把它注入到方法中,否则我会把它注入到类的构造函数中。
另请注意,我已将您的类从 重命名为 。Paginator是一个更好的名字,恕我直言,对于这个类来说,因为它对其他人(重新)查看你的代码是很清楚的。另请注意,我已将第一个字母大写。pagi
Paginator
我做的另一件事是更改查询以选择您正在使用的字段,而不是使用“通配符”。这与我更改类名的原因相同:查看代码的人(重新)将确切地知道将在不检查数据库和/或结果的情况下检索哪些字段。*
更新
因为答案引发了关于为什么我会走依赖注入路线而不是声明对象的讨论,我想澄清为什么我会在关键字上使用依赖注入:当你有一个这样的方法时:global
global
function get_records($q) {
global $db;
$x = $db->query($q);
return $db->fetch($x);
}
当您在某个地方使用上述方法时,不清楚所使用的类或方法是否依赖于 。因此,它是一个隐藏的依赖关系。上述内容不好的另一个原因是,您已经将实例(因此)类与该方法/类紧密耦合。如果在某些时候需要使用 2 个数据库,该怎么办?现在,您必须遍历所有代码才能更改为 。您永远不需要仅仅为了切换到另一个数据库而更改代码。因此,您不应该这样做:$db
$db
DB_MySQL
global $db
global $db2
function get_records($q) {
$db = new DB_MySQL("localhost", "root", "", "test");
$x = $db->query($q);
return $db->fetch($x);
}
同样,这是一个隐藏的依赖项,并将类与方法/类紧密耦合。因此,也不可能正确地对类进行单元测试。您不是只测试单元(类),而是同时测试该类。如果您有多个紧密耦合的依赖项,该怎么办?现在,您突然使用所谓的单元测试来测试多个类。因此,在使用依赖关系注入时,您可以轻松地切换到另一个数据库类,甚至是用于测试目的的模拟数据库类。除了只测试一个单元的好处(你不必担心因为依赖关系而得到错误的结果),它还将确保你的测试能够快速完成。DB_MySQL
Paginator
Paginator
DB_MySQL
有些人可能认为单例模式是访问数据库对象的正确方法,但是应该清楚的是,在阅读了上述所有内容之后,单例基本上只是另一种制作事物的方式。它可能看起来不同,但它具有与 完全相同的特征,因此具有相同的问题。global
global